From efb19606a786051e5646e0e492ba05cc11ca0086 Mon Sep 17 00:00:00 2001 From: tamakyi Date: Sun, 18 Oct 2026 21:36:03 +0800 Subject: [PATCH] restrict v3 --- .workbuddy/memory/2026-10-18.md | 16 ++++++++++++++++ route/question/question.go | 22 +++++++++++++--------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/.workbuddy/memory/2026-10-18.md b/.workbuddy/memory/2026-10-18.md index 2c486ad..49ebc02 100644 --- a/.workbuddy/memory/2026-10-18.md +++ b/.workbuddy/memory/2026-10-18.md @@ -171,3 +171,19 @@ L4 整页停用、举报按钮与箱主面板筛选、邮件告警。分享卡 3. 新增 1b「停用页是否部署」+ 文案断言,dry-run 也能验 i18n/模板。 教训(已与既有坑同类):**302 / 200 都不能单独证明处置生效**,必须先确立「基线 200」这个前提。 + +### 二次实跑:7/7 全绿(真实环境) + +`DOMAIN=admin6`:基线 200 → L4 302 → L1 200 + 分享卡 302(qid=73)→ 撤销后 200。 +证明 L1 分享卡拦截、L4 整页停用、撤销、以及 L4→L1 的**替换语义**在真实库上都按预期工作。 + +**随后发现并修掉一个顺序缺陷(L2)**:原实现把「强制私密」放在 `moderation.Check` 之前, +一旦审核失败(内容违规或审核源不可用,fail-closed),题目已被改成私密——箱主一次失败的编辑 +会让原有公开内容凭空消失。现已移到审核通过之后、`AnswerByID` 之前,仍满足 +「任何时刻不出现公开+已作答」(此刻它是未作答的私密题)。编译 EXIT=0、gofmt 干净。 +⚠️ 该修复需重新部署才可在测试站手测 L2/L3。 + +L2/L3 的手测断言(比看 flash 可靠): +- L2 后 `select is_private, answer from questions where id=` → `t` 且 answer 非空,公开页不显示; +- L3 后 `answer` 仍为空串(未被写入),`is_read` 不变。 +- 干扰项:回答会走七牛审核,审核不可用会 fail-closed 报「审核服务不可用」,那不是处置问题。 diff --git a/route/question/question.go b/route/question/question.go index 88382e8..fbeaf89 100644 --- a/route/question/question.go +++ b/route/question/question.go @@ -178,15 +178,6 @@ func PublishAnswer(ctx context.Context, pageUser *db.User, question *db.Question ctx.Success("question/item") return } - // L2:先把该题置为私密再写入回答,任何时刻都不会出现「公开 + 已作答」的中间态。 - if db.RestrictionAtLeast(restrictionLevel, db.RestrictionL2) { - if err := db.Questions.SetPrivate(ctx.Request().Context(), question.ID); err != nil { - logrus.WithContext(ctx.Request().Context()).WithError(err).Error("Failed to force question private") - ctx.SetInternalError() - ctx.Success("question/item") - return - } - } // 🚨 Content security check. // 统一守卫:按箱主「审核严格度」裁决是否放行(审核源不可用仍为 fail-closed, @@ -198,6 +189,19 @@ func PublishAnswer(ctx context.Context, pageUser *db.User, question *db.Question return } + // L2 收窄公开:审核通过后再把该题置为私密,然后写入回答。 + // 顺序不能提前到审核之前 —— 审核失败(违规或审核源不可用,fail-closed)会直接返回, + // 那时题目已经被改成私密,等于箱主一次失败的编辑让原有公开内容凭空消失。 + // 放在这里仍然满足「任何时刻都不出现公开 + 已作答」:此刻它还是一条未作答的私密题。 + if db.RestrictionAtLeast(restrictionLevel, db.RestrictionL2) { + if err := db.Questions.SetPrivate(ctx.Request().Context(), question.ID); err != nil { + logrus.WithContext(ctx.Request().Context()).WithError(err).Error("Failed to force question private") + ctx.SetInternalError() + ctx.Success("question/item") + return + } + } + if len(f.Images) > 0 { image := f.Images[0]