离线校验审计发现记录:confirmed 须有证据且不能自己验证自己

参考 Cloudflare security-audit-skill README 描述的规则,用 mock 数据演示一个校验器:confirmed 必须有完整源码追踪和有限的观察结果,needs_validation 不得带严重度,验证者不能是发现者。

已验证 Python 3.12.15 耗时 不到 0.1s 运行于 2026-10-27 22:02 UTC
结论

8 条 mock 记录全部判定正确,其中 5 条违规被指出具体原因

检查 8/8 通过
  • F1 (confirmed): 违规=无
  • F2 (confirmed): 违规=['self-verified']
  • F3 (confirmed): 违规=['missing-trace']
  • F4 (confirmed): 违规=['missing-observation']
  • F5 (needs_validation): 违规=无
  • F6 (needs_validation): 违规=['severity-on-unvalidated']
  • F7 (needs_validation): 违规=['missing-unresolved-fact']
  • F8 (rejected): 违规=无
实验指标(条)
校验结论与标注一致8条
被标出违规的记录5条

数值取自 GitHub Actions 实际运行输出中的 METRIC 行

以上内容从实际运行输出中提取,完整输出见下方。

python main.py
[PASS] F1 (confirmed): 违规=无
[PASS] F2 (confirmed): 违规=['self-verified']
[PASS] F3 (confirmed): 违规=['missing-trace']
[PASS] F4 (confirmed): 违规=['missing-observation']
[PASS] F5 (needs_validation): 违规=无
[PASS] F6 (needs_validation): 违规=['severity-on-unvalidated']
[PASS] F7 (needs_validation): 违规=['missing-unresolved-fact']
[PASS] F8 (rejected): 违规=无
METRIC: 校验结论与标注一致 = 8 条
METRIC: 被标出违规的记录 = 5 条
RESULT: 8 条 mock 记录全部判定正确,其中 5 条违规被指出具体原因

目的

Cloudflare 开源的 security-audit-skill 在 README 中写明:发现由一个 Agent 提出,必须由另一个全新的 Agent 去尝试推翻;confirmed 需要完整的源码追踪和有限的观察结果,needs_validation 要写清未解决的具体事实且不带严重度,并用零依赖的 Node.js 校验器检查 findings.json。

本实验把这几条规则写成一个只用标准库的小校验器,在 8 条 mock 记录上演示:缺证据的 confirmed、自己验证自己、给未验证结论打严重度,都能被机械地挡住。

注意:字段名和记录内容是本实验自己构造的 mock,不是该技能 findings.json 的真实 schema,也不代表其校验器的实现。

步骤

python main.py

程序对每条记录打印 [PASS]/[FAIL]、METRIC: 指标和最后一行 RESULT:;有记录的结论与人工标注不一致时以非零状态退出。

预期结果

  • F1、F5、F8 通过校验
  • F2 被判为「自己验证自己」,F3 缺源码追踪,F4 缺观察结果
  • F6 在未验证状态下带了严重度,F7 没写未解决的事实

启示与可以继续尝试

  • 想让团队的 Agent 产出可信的审查结论,可以先给「结论」定一个带必填证据的格式,再用脚本检查,而不是靠人逐条读
  • 把真实审查输出的字段映射进 check(),看看有多少条结论其实缺证据

内网复现

  • 只用 Python 标准库,不需要安装任何依赖,requirements.txt 仅作说明
  • 程序完全离线运行,不访问网络,也不需要 API key
  • 技能本身需要外网或内网 Git 镜像获取,本实验不依赖它

代码

main.py79 行
"""离线校验「审计发现」记录的小示例。

规则来自 cloudflare/security-audit-skill README 的描述:
- confirmed 需要完整的源码追踪和有限的观察结果
- needs_validation 需要写明未解决的具体事实,且不带严重度
- 验证者不能是发现者本人

字段名、记录内容都是本实验自己构造的 mock,不是该技能 findings.json 的真实 schema。
"""
import sys

SEVERITIES = {"low", "medium", "high", "critical"}

# (记录, 期望的违规原因集合;空集合表示应当通过)
RECORDS = [
    ({"id": "F1", "status": "confirmed", "severity": "high", "finder": "hunter-1",
      "verifier": "verifier-2", "trace": ["routes.py:12", "db.py:40"],
      "observed": "payload 触发了 SQL 报错,仅限本地沙箱"}, set()),
    ({"id": "F2", "status": "confirmed", "severity": "high", "finder": "hunter-1",
      "verifier": "hunter-1", "trace": ["a.py:1"], "observed": "复现成功"},
     {"self-verified"}),
    ({"id": "F3", "status": "confirmed", "severity": "medium", "finder": "hunter-2",
      "verifier": "verifier-1", "trace": [], "observed": "复现成功"},
     {"missing-trace"}),
    ({"id": "F4", "status": "confirmed", "severity": "medium", "finder": "hunter-2",
      "verifier": "verifier-1", "trace": ["b.py:9"], "observed": ""},
     {"missing-observation"}),
    ({"id": "F5", "status": "needs_validation", "finder": "hunter-3",
      "unresolved": "未确认 admin 路由是否在网关层已鉴权"}, set()),
    ({"id": "F6", "status": "needs_validation", "severity": "high", "finder": "hunter-3",
      "unresolved": "未确认配置是否生效"}, {"severity-on-unvalidated"}),
    ({"id": "F7", "status": "needs_validation", "finder": "hunter-3", "unresolved": ""},
     {"missing-unresolved-fact"}),
    ({"id": "F8", "status": "rejected", "finder": "hunter-4", "verifier": "verifier-2",
      "reason": "输入在上游已被白名单过滤"}, set()),
]


def check(rec):
    problems = set()
    status = rec["status"]
    if status == "confirmed":
        if rec.get("severity") not in SEVERITIES:
            problems.add("bad-severity")
        if not rec.get("trace"):
            problems.add("missing-trace")
        if not rec.get("observed"):
            problems.add("missing-observation")
        if rec.get("verifier") == rec.get("finder"):
            problems.add("self-verified")
    elif status == "needs_validation":
        if "severity" in rec:
            problems.add("severity-on-unvalidated")
        if not rec.get("unresolved"):
            problems.add("missing-unresolved-fact")
    return problems


def main():
    passed = 0
    flagged = 0
    for rec, expected in RECORDS:
        got = check(rec)
        if got:
            flagged += 1
        ok = got == expected
        passed += ok
        tag = "[PASS]" if ok else "[FAIL]"
        print(f"{tag} {rec['id']} ({rec['status']}): 违规={sorted(got) or '无'}")
    print(f"METRIC: 校验结论与标注一致 = {passed} 条")
    print(f"METRIC: 被标出违规的记录 = {flagged} 条")
    if passed != len(RECORDS):
        print("RESULT: 有记录的校验结论与标注不一致")
        sys.exit(1)
    print(f"RESULT: {len(RECORDS)} 条 mock 记录全部判定正确,其中 {flagged} 条违规被指出具体原因")


if __name__ == "__main__":
    main()
requirements.txt1 行
# 仅使用 Python 标准库,无需安装依赖
Esc
↑ ↓ 选择Enter 打开

键盘快捷键

j / k
下一条 / 上一条
o
展开或收起当前条目
Enter
打开当前条目详情
c
复制当前条目链接
s
收藏 / 取消收藏当前条目
/ 或 ⌘K
搜索
1 2 3
切换密度:标题 / 摘要 / 完整
t
切换主题
?
显示本帮助