- remediation master record for 5 resolved + 1 deferred + 1 open issues - ISSUES.md rows with evidence dates and lesson links - LESSON-20260926-failclosed-bypass-contract (validated) - remediation/lessons INDEX hooks + CURRENT task row (docs apply receipt)
9.1 KiB
9.1 KiB
title, status, created, updated, owner, task_id, severity, source_audit, base_dev_sha
| title | status | created | updated | owner | task_id | severity | source_audit | base_dev_sha |
|---|---|---|---|---|---|---|---|---|
| 系统 Code Review P0/P1 修复(IDOR、门禁盲区、fail-open、命令白名单、validated_only 硬拒) | done | 2026-09-26 | 2026-09-26 | pi | system-review-p0-p1-fixes-2026-09-26 | critical | ../audit/2026-09-26-system-code-review.md | f4c1b73f28 |
系统 Code Review P0/P1 修复
来源:2026-09-26 系统全面 code review(被审 tip f4c1b73f)。
用户授权(2026-09-26):修复 2 Critical(FIND-SEC-01、FIND-ARCH-01)+ 3 个 High 行(FIND-SEC-02/03、FIND-FLOW-01、FIND-QUAL-01),修复后与报告一起集成,清理任务 worktree。本文件是下列 issue 的唯一主记录;发现细节与证据链以来源审计为准,此处记修复方案、经过与验收。
修复范围与 issue 映射
| FIND | issue_id | 修复内容 | 状态 |
|---|---|---|---|
| FIND-SEC-01 | ISSUE-20260926-ctx-idor | GetUserContext 会话归属校验(handler 403 + SQL/内存双防线) | resolved |
| FIND-ARCH-01 | ISSUE-20260926-archcheck-untracked | arch-check 未登记表 fail-closed + 补登记 50 表 | resolved |
| FIND-SEC-02 | ISSUE-20260926-authz-failopen | Authorizer 无池严格拒绝;显式 NewInMemoryAuthorizer 接缝 | resolved |
| FIND-SEC-03 | ISSUE-20260926-swboot-shell-prefix | argv 化执行(去 sh -c)+ 白名单精确匹配 | resolved |
| FIND-FLOW-01 | ISSUE-20260926-intentflow-validated-confirm | Confirm 拒 validated_only + 执行器硬读 mode | resolved |
| FIND-QUAL-01 | ISSUE-20260926-dual-flags-drift | 组合根 flag 一致性 fail-fast | resolved(含残差) |
| FIND-QUAL-01(入口收敛残差) | ISSUE-20260926-governance-multi-entry | docgov 三入口 + Coordinator/OwnerMux 合并 | deferred |
| FIND-ARCH-02 | ISSUE-20260926-server-pool-wiring | server 持 Pool 直装 swboot(本轮未授权修复) | open |
各项修复经过
1. FIND-SEC-01 会话上下文 IDOR(ISSUE-20260926-ctx-idor)
- 根因:
handleGetUserContext只校验user_id归属,session_id直通GetUserContext;chat_messages查询WHERE session_id = $1无归属条件,绕过 chatflow 的会话授权。 - 修复(双层):
internal/server/handlers_context.go:sessionID 非空时经GetSessionOwnerID校验归属(不存在与归属他人同返 403,不泄露存在性)。internal/ctxmgr/manager.go:SQL 增加EXISTS(chat_sessions 归属目标用户)子句;内存分支按sessions[sessionID].UserID == userID过滤——未来调用方遗漏 handler 校验也不泄露。
- 回归:
TestGetUserContext_OtherUserSessionDenied(他户 session→403、不存在→403、本人→200 且含正文,内存 HTTP);TestGetUserContext_PG_SessionOwnerFilter(隔离 PG 库:他人视角 manager 层不返回正文、归属者正常)。
2. FIND-ARCH-01 arch-check 未登记表静默跳过(ISSUE-20260926-archcheck-untracked)
- 根因:
internal/archtest/scan.goif !tracked { continue }——表唯一写者门禁对未登记表(含swboot_controlled_executions、projects、outbox_events等)无强制力。 - 修复:
- scan.go:未登记表产生
sql_writefinding(fail-closed);仅豁免扫描正则已知假阳性词汇(set/of/skip/below/row/state/step/doc/docgov/message/project/aggregation/ref_count,来自ON CONFLICT DO UPDATE SET、FOR UPDATE OF/SKIP LOCKED与注释散文;静态清单,非策略豁免)。 module-boundaries.yaml:补登记 50 张既有写者真实表。多 owner 条目(projects、users、project_members、context_memory_chunks)是现状快照,新包写入仍被 block;单一写者收敛留在各模块批次(见 ISSUES 残差说明)。
- scan.go:未登记表产生
- 回归:
TestUntrackedTableWriteIsBlocked(新表写入必须 block 且 detail 提示登记);TestUpsertSetKeywordIsNotFlaggedAsUntracked(SET 噪音不误报)。全仓arch-check --mode blockPASS。
3. FIND-SEC-02 Authorizer 无池 fail-open(ISSUE-20260926-authz-failopen)
- 根因:
IsProjectMember/IsProjectOwner在!enabled()(pool==nil)时return userID > 0——任意登录用户获得任意项目访问;组合根始终NewAuthorizer(pool),nil pool 也构造成功,绕过「nil→failClosed」语义。 - 修复:
NewAuthorizer(nil)一律严格拒绝(false);新增显式NewInMemoryAuthorizer()仅供 ALLOW_IN_MEMORY 演示/测试接缝(视任意登录用户为成员);组合根app.Assemble在pool==nil && cfg.AllowInMemory时选用。误配(无库且未显式声明)→ 拒绝。 - 波及与处置:8 个依赖 fail-open 作测试接缝/隐式放行的既有用例——1 个改用显式内存授权 override,5 个业务行为用例改走
newServerWithMemoryIAM/AllowInMemory=true(显式演示模式契约),1 组 u5 用例补ModeLive(属 FLOW-01 波及),1 个生命周期用例由组合根修复自然恢复。无削弱鉴权断言的用例。 - 回归:
TestMembershipChecks_FailClosedWithoutPool;隔离 PG 库 authz 套件(ConcurrentLastWriter/DocumentDuty/AtomicDutyRevoke/UpsertProjectMember)PASS。
4. FIND-SEC-03 swboot sh -c + 前缀白名单(ISSUE-20260926-swboot-shell-prefix)
- 根因:受控命令经
sh -c cmdLine执行;允许判定为 basename 前缀匹配(go放行go-evil)。ApproveMerge 自身恒拒未变。 - 修复:
runCommand改 argv 化exec.CommandContext(fields[0], fields[1:]...)(元字符只成为字面参数);commandMatchesPrefixes→commandAllowed,仅 basename 精确相等(含绝对路径条目取其 basename)。admin 白名单粒度仍是真实边界(报告 FIND-SEC-03 说明),运维登记白名单时应只放具体命令名。 - 回归:
TestExecutor_CommandAllowlistIsExactNotPrefix(前缀伪装拒绝、精确/绝对路径放行、空命令拒绝);TestExecutor_NoShellEvaluation(echo x && touch <marker>不求值、marker 不产生、输出为字面参数)。
5. FIND-FLOW-01 validated_only 可 Confirm 后 dispatch(ISSUE-20260926-intentflow-validated-confirm)
- 根因:
DecisionConfirm允许PlanProposed;确认后 HTTP 即DispatchEligible;ExecuteClaimed不读ExecutionMode——shadow「安全」依赖「没人点 Confirm」。 - 修复(双层):
DecidePlan:Confirm 对plan.ExecutionMode == validated_only一律ErrPlanModeNotConfirmable(403)。ExecuteClaimed:硬读步骤级(缺省计划级)ExecutionMode,validated_only一律ErrPlanModeNotExecutable——覆盖所有派发路径(confirm 后、worker、误推进)。- 新错误码
PLAN_MODE_NOT_CONFIRMABLE/PLAN_MODE_NOT_EXECUTABLE入ErrorCode与 HTTP 映射。
- 回归:
TestConfirmRejectedForValidatedOnlyPlan;TestExecuteClaimedRefusesValidatedOnlyStep(经 DecisionContinue 误推进后 DispatchEligible 硬拒,步骤不成 succeeded、owner 零执行)。既有 4 个用例的draft()helper 补显式ModeLive(缺省 validated_only 是安全默认,live 机制用例需显式声明)。
6. FIND-QUAL-01 双套 flags 静默漂移(ISSUE-20260926-dual-flags-drift,含残差)
- 修复:
intentflow.ValidateFlagConsistency(组合根Assemble调用)——PlanTier升档但MULTI_INTENT_ENABLED=false时启动期 fail-fast(Assemble最早阶段调用),消除「intentflow 已升档、intent 仍关」静默漂移配置上线。 - 回归:
TestValidateIntentFlagConsistency(升档+开=过;legacy 恒过;四档×关=拒)。 - 残差(deferred,ISSUE-20260926-governance-multi-entry):docgov 三入口(chat PendingAction / docgov_owner / handlers_docgov)与 Coordinator ~40 intent case + OwnerMux 平行面合并是多日级重构(自带回归矩阵),超出本批授权合理范围;已在 ISSUES 登记 owner/触发条件。
验收汇总
被测 SHA:本任务 worktree HEAD(见下方提交记录);基线 f4c1b73f。
| 检查 | 结果 |
|---|---|
./scripts/arch-check.sh --mode block |
PASS(fail-closed 改动 + 50 表登记后) |
go vet ./... |
PASS |
go test -short ./... |
全绿 |
隔离 PG:TestGetUserContext_PG_SessionOwnerFilter |
PASS(临时库建删于 55432 verify 容器) |
| 隔离 PG:authz + server duty 套件 | PASS |
| 隔离 PG:duty-sync 脚本 | TestHealProjectDocuments_SkipsDutyLocked_PG FAIL——基线 f4c1b73f 同样失败(FK fixture 既有问题,与本批无关) |
TestPiAgent_FullLifecycle_MultiRole_RealWorld_E2E |
非 short 模式因 KB 离线前置检查失败——基线同样失败(需真实 Runner/KB,非本批回归) |
部署
代码已按授权集成 local_dev(见任务记录)。dev 共享服务未在本任务内重建/重启(未获 env 写授权);deployment=pending,重启与 /health、/ready、业务行为核验待部署任务执行。
经验
见 LESSON-20260926-failclosed-bypass-contract:旁路入口必须继承主路径硬边界;门禁/执行器对「未知」默认必须拒绝。