mcpField
V
verifying-skill-accuracy

verifying-skill-accuracy

github.com/trilwu测试与质量17749 次安装12 周前更新
代码仓库

关于 verifying-skill-accuracy

用于对照一手资料审查 LLM 辅助编写的技术技能。它提供来源层级和确定性检查方法,核验命令、标识符及具体说法,并特别关注可能悄然扭曲安全分析的错误。

主要功能

  1. 01依据一手资料核验说法
  2. 02优先排查系统性错误
  3. 03探测文档与标识符
  4. 04避免根据截断结果下结论
  5. 05重点检查静默错误

适用场景

  1. 01审查现有 SKILL.md
  2. 02核验新写或修订的技术内容
  3. 03复查报告中的命令或标识符问题
  4. 04添加验证标记前检查内容

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/trilwu/secskills --skill verifying-skill-accuracy

安装后在 Claude Code 中输入/verifying-skill-accuracy确认已加载。

详细介绍

验证技能的准确性

本仓库中的技能是在 LLM 辅助下编写的。其方法通常合理;在核实之前,具体细节都不可信。第一轮核验的统计结果是:12 个技能中有 11 个存在错误,共计 31 处事实错误,平均每个技能约 2.6 处;只有一个技能在两轮核验中都没有问题。对于任何未盖章的内容,都应假定错误率与此相同。

本技能介绍了如何降低错误率,以及何时可以声称某项技能已通过验证。

适用场景

  • 根据一手来源审查现有 SKILL.md
  • 添加或续期 verified: 前置元数据标记之前
  • 起草新技能内容后,核实刚写下的具体细节
  • 读者报告命令、ID 或字段名称无法使用时
  • 审查作者为模型的任何技术内容

不适用场景

  • 从头编写新技能——请使用 authoring-security-skills,然后以本技能作为最终核验关卡
  • 结构或风格问题(前置元数据、章节、行数)——应运行 python3 scripts/validate.py --strict,它只检查格式
  • 判断某种技术是否值得采用——这属于判断,而非事实核验

核心区别:格式与真伪

本仓库的 CI 是一个闭环。validate.py、sync_attack.py 和 run_evals.py 会检查前置元数据格式、交叉引用完整性、ATT&CK 索引一致性,以及依据自编评估用例进行的路由。即使技能中包含不存在的命令,这些检查也都可能通过。

CI 通过,说明技能格式正确;这并不能说明内容属实。切勿引用通过的测试运行结果作为准确性的证据。

按类别检查整个集合

一旦发现某类错误,在继续之前,先检查整个集合中的所有相关内容,而不只是发现错误的那个技能。同类错误往往会聚集出现;逐个技能检查会导致集合内部不一致。

本仓库中的两个案例说明了这一点。writing-sigma-rules 中的旧版 Sigma 日期格式修正后,又在 engineering-detections 中再次出现——而且出现在一个已经盖章的技能里,因为逐个技能盘点时没有包含其他地方已修正的错误类别。另一个案例是,attacking-entra-id 仍在调用已弃用的 AzureAD PowerShell cmdlet,而 investigating-m365-entra 已使用现代的 Graph 对应命令:整个集合自相矛盾,只有跨技能 grep 才能发现问题。

因此:修正问题实例后,立即 grep 所有技能,查找相同模式,并在提交说明中写明检查过哪些类别。安全类集合中值得全面检查的类别包括:工具更名和归档项目、CVE ID、云元数据端点、已移除的 API 版本、默认端口、规范版本声明,以及任何受平台版本限制的行为。

排查优先级:先查类别错误,再查单个错误

不要一开始就逐条核对事实。首先要问:是否存在一项系统性声明,一旦错误就会使后续内容全部失效?

在 analyzing-memory-images 中,技能使用 volatility3 调用 Volatility。实际上,正确的入口点是 vol。这一个错误就导致文件中的全部 40 条命令都无法复制使用——其影响远大于后来发现的两个不存在的插件,而且只需替换一次即可修复。

按以下顺序检查:

  1. 工具身份——二进制文件/入口点名称,以及语法所属的主版本。Volatility 2 和 3 几乎没有相同的命令接口。
  2. 命名方案——工具如何命名其插件、模块或子命令。确认命名方案是否正确,可以一次性验证或否定数十行内容。
  3. 路径和文件约定——相关版本中,实际的产物存放位置。
  4. 单项声明——现在才逐条核对。

类别错误修复成本低,但漏掉的代价很高。单项错误则恰好相反。

来源层级

使用可用的最高层级,并记录使用了哪个层级。

层级来源用途
1特定版本的官方文档、上游源代码、RFC、供应商自己的参考资料命令语法、插件名称、API 结构、协议细节
2供应商知识库、发行说明、变更日志、宣布变更的官方博客默认值、保留期限、许可证限制、弃用信息
3维护者撰写的文章、工具作者的会议资料意图、注意事项、行为原因
4社区帖子、教程、Stack Overflow供第 1 层级核实的线索——绝不能作为引文
—模型记忆不可使用。任何说法都不行。

在表格规则之上还有两条:

  • 匹配版本。 2.x 的文档不能证明 3.x 的行为。优先使用 latest 或 stable 文档分支,并注明技能是否针对较旧的固定版本。
  • 询问上次变更时间,不要只问是否属实。供应商的默认设置会变:Microsoft Audit (Standard) 的保留期限在 2023 年 10 月从 90 天延长至 180 天;在此之前编写的材料如今已不再正确,但乍看之下并不明显。

绝不可凭记忆断言以下内容

以下每一项都曾导致本仓库出现实际错误:

  • CLI 入口点和子命令名称
  • 插件、模块和软件包名称
  • 版本号,以及任何“默认值是 X”的说法
  • 事件 ID 及其确切含义(4778 是会话重新连接,不是连接)
  • 日志字段和列名称
  • 保留期限和许可证限制
  • CVE ID、CVSS 向量和受影响版本范围
  • API 路径、参数名称、必需作用域
  • 法律先例和案件结果——本仓库中的一份草稿引用了 Mango Markets 和 Platypus 的定罪结果,以证明利用行为会被作为盗窃罪起诉。但事实恰恰相反:Platypus 的两名被告在巴黎被判无罪,而 Eisenberg 因 Mango 案被定罪的判决于 2025 年 5 月被撤销。模型记忆给出了一条信心十足、却完全颠倒的说法。

阅读来源:获取 Markdown,而不是 HTML

通过 defuddle.md 获取文档;它会提取页面的主要内容,并以 Markdown 和 YAML frontmatter 的形式返回。为 URL 添加此前缀:

curl -sL "https://defuddle.md/learn.microsoft.com/en-us/purview/audit-log-retention-policies"

路径中可以省略协议——defuddle.md/example.com/x 和 defuddle.md/https://example.com/x 都可用。

原因有两点,第二点比第一点更重要:

  1. 减少 token 用量。 根据本仓库各轮处理中使用的页面测量:对于一篇说明文档(Microsoft Learn),内容缩减了 78%;对于一个含大量链接的 API 索引,缩减了 33%。说明性文字的压缩效果很明显;链接表格则不那么明显。
  2. 确定性地返回全文。 你会得到完整页面的 Markdown,可以用 grep 搜索,而不是模型对页面的摘要。这正是安全得出否定结论的关键——见下文。

将其用于文档、规范、供应商知识库和文章。不要将其用于:

  • JSON 或 API 响应——可读性提取会破坏结构化数据。请直接获取原始数据。
  • HTTP 状态探测——你需要的是实际主机的状态,而不是可能会为自己的错误页面返回 200 的代理。
  • 任何内部、归客户端所有或归目标所有的内容。 URL 会离开你的计算机并发送给第三方。绝不要将项目 URL、内部主机名或客户端的系统环境发送到外部提取服务。仅限公开的供应商文档。
  • 需要身份验证或由 JS 渲染的页面——它会以匿名客户端的身份获取页面。

获取受阻不等于否定结果。 一些主机会拒绝提取器,并返回错误内容而非页面——freedesktop.org 会返回 {"error":"Failed to fetch: 418 I'm a teapot"}。应将其视为“我没有读取该页面”,绝不能视为“页面没有提到这件事”。这是换了种伪装的截断式否定陷阱,解决办法也相同:在得出任何结论前,直接重新获取页面。

程序化存在性探测

如果项目为每个模块发布一个文档页面,那么存在与否取决于状态码,而非主观判断。这是置信度最高、耗时最低的检查方式,而且可以批量执行。

# One plugin: 200 = exists, 404 = does not
curl -s -o /dev/null -w "%{http_code}\n" \
  "https://volatility3.readthedocs.io/en/latest/volatility3.plugins.windows.idt.html"

# Every plugin the skill references, in one pass
plugins=$(grep -oE '\b(windows|linux|mac)\.[a-z_]+' SKILL.md | sort -u)
for p in $plugins; do
  code=$(curl -s -o /dev/null -w "%{http_code}" \
    "https://volatility3.readthedocs.io/en/latest/volatility3.plugins.$p.html")
  [ "$code" = "200" ] || echo "MISSING $code  $p"
done

这轮检查在几秒内确认了 32 个插件引用,并找出两个不存在的引用。针对不同项目调整 URL 模式即可——大多数文档生成器(Sphinx、mkdocs、pkg.go.dev、docs.rs、npm、PyPI)都会提供可用相同方式探测的、针对每个符号或软件包的 URL。

如果项目没有针对每个符号的文档,就退而求其次,对上游源码进行精确字符串搜索——直接获取原始文件,再用 grep 搜索字面标识符。这两种方法的结果都是确定的。优先使用它们,而不是询问任何模型,包括你自己。

截断导致的误判陷阱

摘要器说“未找到”,并不能证明内容不存在。

获取一个大型索引页面并询问其中包含什么,得到的回答是:Volatility Linux 插件索引中“未列出 pslist、pstree、psaux、sockstat”。这四个插件都存在。页面在模型看到这些条目之前就被截断了。若依据该输出采取行动,就会在原本正确的章节中引入四处错误——验证反而让文件变得更糟。

需要注意的不对称性:

  • 正向结果成本低。 如果工具显示了标识符,它就存在。
  • 负向结果成本高。 摘要内容中没有出现某项,可能意味着它确实不存在、被截断、改名、移走或因分页而未显示。

因此:绝不要依据摘要中的否定结果删除或改写内容。 在动文件之前,先用确定性检查验证每一项否定结果。

最省事的验证方式是不再做摘要。通过 defuddle.md 获取页面,然后自行搜索完整 Markdown——这样答案就是匹配次数,而不是模型的回忆:

L="volatility3.readthedocs.io/en/latest/volatility3.plugins.linux.html"
for p in pslist pstree psaux sockstat; do
  echo "$p: $(curl -sL "https://defuddle.md/$L" | grep -c "linux\.$p module")"
done
# pslist: 1   pstree: 1   psaux: 1   sockstat: 1  -- all four present

这正是推翻摘要器对这四项“未列出”判断的准确检查方式。如果存在针对每个符号的 URL,下面的状态探测方式会更有力。如果两种方法都无法使用,就不要改动内容,并将其标记为未经确认——未验证的行可以恢复,有把握地删掉正确内容则无法挽回。

按后果权衡优先级

并非所有错误的代价都相同。把精力花在出错后不易察觉的地方。

失效模式示例代价
明显——首次运行就失败CLI 标志错误、插件不存在几分钟。工具会告诉你。
隐蔽——给出看似可信的错误答案误读事件 ID、保留期限错误、工件含义错误律师会查看的事件时间线。

优先、彻底地验证隐蔽失效类的断言。实际上,这意味着防御和取证内容——事件 ID、日志架构、工件语义、保留期限——比进攻性工具语法更重要,尽管后者看起来更危险。

举个具体例子:M365 技能声称 E3 的 Unified Audit Log 保留 90 天。实际上,自 2023 年 10 月起,默认保留期限一直是 180 天。分析师若相信该技能,就会把空白的 90 天窗口解读为“没有活动”,并结束调查,而实际上仍有六个月的历史记录可供查看。

流程

  1. 盘点可检查的断言。 用 grep 搜索技能中上述类别的内容——命令、标识符、ID、字段名称、带单位的数字。任何读者可能无法察觉其错误的内容都要纳入。
  2. 先解决类别级断言(工具名称、版本、命名方案)。据此重新界定下方所有内容,只保留经确认的部分。
  3. 对每个标识符进行批量探测,使用确定性检查。
  4. 在第 1 或第 2 层级验证其余断言,并记录来源。通过 defuddle.md 获取页面,以较低成本阅读全文,而非摘要。
  5. 进行更正,并在提交说明中写明变更内容及原因——后续读者需要知道某项断言已经过检查,而不只是某行发生了移动。
  6. 进行对抗性复查。 重读更正内容,试着推翻它们。对本仓库的首次检查发现了一些初读时漏掉、二次检查时才发现的错误;假设你的检查也会如此。
  7. 仅在全部完成后盖章——见下文。

verified: 印章

verified: 2026-07-26    # ISO 8601; validate.py parses and counts this

该印章表示:**此技能所有可核查的内容均已在该日期追溯至一手来源。**它不代表该技能很好、永远保持最新或内容完整。

规则:

  • **部分核查不盖章。**本仓库中的三个防御类技能经过实质性修正后,仍有意未盖章,因为每次核查都只涵盖了主要声明类别,而非整个文件。将部分核查标为完整,比不贴标签更糟,因为这会把诚实的未知变成虚假的保证。
  • **重新核查时重新盖章,而非编辑时。**新增章节不会更新日期。
  • 没有印章不代表有缺陷。validate.py 会将缺少印章视为未经核查的草稿,这是文档规定的默认状态。日期格式错误则属于错误,因为这会导致计数不准确。

随时查看当前状态:

python3 scripts/validate.py --strict   # prints "Fact-checked ...: N/72"

应当拒绝的自我安慰

  • **“CI 通过了,所以没问题。”**CI 检查的是格式。本仓库发现的每个错误,都出现在 CI 通过的文件中。
  • **“我对这项内容有把握。”**对于标识符和特定版本的默认设置,信心与准确性并不相关。Mango Markets 的说法当时写得信心十足,结果却完全相反。
  • **“如果不对,工具会报错,读者自然会注意到。”**语法问题确实如此,解读问题却不是——而解读错误才是会出现在交付物中的那类错误。
  • **“文档没提到,所以我删掉了。”**摘要式获取内容时,经常会遗漏内容。删除任何内容之前,应先将否定结果转换为状态码,或用精确字符串进行 grep。
  • **“我会把所有内容都交给 defuddle 处理,这样能节省令牌。”**它适用于公开文本。结构化 JSON 返回时会被破坏,状态探测需要访问真实主机,而且绝不能为了节省令牌而将互动链接或客户端 URL 交给第三方服务。
  • **“写下这段内容时它是对的。”**保留期限、许可门槛和默认路径都会发生变化。核查日期正是为此而设;“2024 年时正确”不能成为 2026 年的辩护理由。
  • “我修复了发现的错误,所以可以盖章了。”只有在检查完所有可核查内容后才能盖章,而不是在没有发现更多错误时就盖章。
  • **“差不多就行,读者会自行调整。”**读者往往是正在执行命令的模型,或是在事件压力下工作的分析师。两者都不会自行调整。

参考资料

  • authoring-security-skills — 编写新技能;盖章前使用此技能进行核查
  • CONTRIBUTING.md — 合并标准和项目风格
  • scripts/validate.py — 结构检查和已核查数量报告

评论

支持 Markdown