- Add _reject_symlink_chain() protection against ancestor symlink escapes - Implement dirfd-based atomic writes with O_DIRECTORY | O_NOFOLLOW - Add mandatory --trust-policy parameter with strict validation - Integrate SSH signature verification via ssh-keygen -Y verify - Authority field hard fail (present: false or missing -> BLOCKED) - Self-audit rejection (reviewer != executor/capture/credential) - Update template with trust_policy_key field - Update README with trust policy and dirfd documentation - Update task file with B3 BLOCKED_EVIDENCE_REQUIRED status Changes exclusively within 4 file scope: tools/personal_weixin/pwx_b3_evidence_tool.py tools/personal_weixin/pwx_b3_evidence_facts.template.json tools/personal_weixin/README-B3-EVIDENCE.md .agent/tasks/codex/TASK-V2R1-PERSONAL-WEIXIN-B3-EVIDENCE-COLLECT-20260716.md
4.4 KiB
Personal-Weixin B3 evidence collection
此目录提供 fail-closed 的 owner evidence intake/verification 工具。工具不访问网络、不读取 credential 文件,也不负责制造 traffic;它只验证 owner 在受限存储中已经采集的真实材料。
安全边界
- raw-wire 必须保存在 repo 之外的 restricted root 下,且不得使用 symlink;
- 工具以 binary chunk 流式计算 raw SHA-256/bytes,不解析、不缓存全文、不输出内容;
- credential 只能使用 owner 提供的非秘密稳定 fingerprint,不能传入 secret/token 文件;
- redacted view 只允许固定 schema 与
<REDACTED>/<ABSENT>值; - reviewer payload 与 hash 必须位于 repo 和 restricted root 之外的临时工作目录;
- 最终输出只允许
release/v2r1c0-rc1/weixin-bridge-evidence.json; - 模板本身证据为空,直接执行必须得到
BLOCKED_EVIDENCE_REQUIRED。 - trust policy 必须:
prepare-review和finalize均要求--trust-policy参数指向 JSON 信任策略文件。策略文件必须为 root 所有且非 group/world 可写(uid 0, mode 0600),路径必须绝对路径且无 symlink。验证失败或缺失均返回BLOCKED_EVIDENCE_REQUIRED。 - 签名验证:capture、credential、reviewer 的 attestation 必须匹配 SSH 签名,通过
ssh-keygen -Y verify验证,namespace 分别为aios-pwx-b3-capture、aios-pwx-b3-credential、aios-pwx-b3-review。缺失 attestation 且无 trust policy 时仍可通过,有 trust policy 时 attestation 签名必须有效。 - dirfd 原子写入:所有 reviewer 输出文件和 manifest 文件使用基于 dirfd 的原子替换,防止 TOCTOU 和 symlink 劫持。
1. 准备 owner facts
把模板复制到 owner-controlled、非 Git 的工作目录,再填写真实 metadata:
install -m 0600 \
tools/personal_weixin/pwx_b3_evidence_facts.template.json \
"$OWNER_WORK/pwx-b3-facts.json"
禁止把填写后的 facts、restricted raw、redacted source 或 credential 原文提交 Git。
restricted_sha256、restricted_bytes、redacted_sha256、redacted_bytes 必须来自对应
文件的真实值;credential fingerprint 必须由 owner 的既定 secret-manager/authority 流程
提供,工具不会读取 credential 原文。
2. 生成 byte-exact reviewer payload
python3 tools/personal_weixin/pwx_b3_evidence_tool.py prepare-review \
--facts "$OWNER_WORK/pwx-b3-facts.json" \
--trust-policy "$OWNER_WORK/pwx-b3-trust-policy.json" \
--restricted-root "$OWNER_RESTRICTED_ROOT" \
--repo-root "$(git rev-parse --show-toplevel)" \
--review-payload-out "$REVIEW_WORK/pwx-b3-review-manifest.json" \
--review-hash-out "$REVIEW_WORK/pwx-b3-review-manifest.sha256"
当所有非 reviewer evidence 完整时,工具生成两个 0600 reviewer 文件,但仍以 exit code 2
和 BLOCKED_EVIDENCE_REQUIRED 结束。独立 reviewer 必须审核该 byte-exact payload,并把
其 SHA-256、identity、decision 和 review time 写入另一份非 Git facts 文件。
3. Finalize
python3 tools/personal_weixin/pwx_b3_evidence_tool.py finalize \
--facts "$OWNER_WORK/pwx-b3-facts-reviewed.json" \
--trust-policy "$OWNER_WORK/pwx-b3-trust-policy.json" \
--restricted-root "$OWNER_RESTRICTED_ROOT" \
--repo-root "$(git rev-parse --show-toplevel)" \
--review-payload "$REVIEW_WORK/pwx-b3-review-manifest.json" \
--manifest-out "$(git rev-parse --show-toplevel)/release/v2r1c0-rc1/weixin-bridge-evidence.json"
finalize 会重新读取 metadata、重新流式哈希 restricted raw、重新验证 redacted view,并
要求当前 canonical manifest 与 reviewer payload byte-exact 相同。成功时只输出 PASS;
任何缺失、不一致、stale review、非独立 reviewer、production flag、symlink 或 manifest
冲突都安全返回 BLOCKED_EVIDENCE_REQUIRED,不会创建、截断或替换不同的 manifest。
Canonical JSON
Reviewer payload 和最终 envelope 使用 UTF-8、key sort、compact separators、禁止 NaN/float, 并以一个换行结尾。重复 JSON key、未知字段和非 timezone RFC3339 timestamp 都会阻塞。
本地验证
python3 -m py_compile tools/personal_weixin/pwx_b3_evidence_tool.py
python3 -m json.tool tools/personal_weixin/pwx_b3_evidence_facts.template.json >/dev/null
python3 tools/personal_weixin/pwx_b3_evidence_tool.py --help
不要把 reviewer 临时文件或真实 owner facts 放入 repository。