关于 verifying-skill-accuracy
用于对照一手资料审查 LLM 辅助编写的技术技能。它提供来源层级和确定性检查方法,核验命令、标识符及具体说法,并特别关注可能悄然扭曲安全分析的错误。
主要功能
- 01依据一手资料核验说法
- 02优先排查系统性错误
- 03探测文档与标识符
- 04避免根据截断结果下结论
- 05重点检查静默错误
适用场景
- 01审查现有 SKILL.md
- 02核验新写或修订的技术内容
- 03复查报告中的命令或标识符问题
- 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 条命令都无法复制使用——其影响远大于后来发现的两个不存在的插件,而且只需替换一次即可修复。
按以下顺序检查:
- 工具身份——二进制文件/入口点名称,以及语法所属的主版本。Volatility 2 和 3 几乎没有相同的命令接口。
- 命名方案——工具如何命名其插件、模块或子命令。确认命名方案是否正确,可以一次性验证或否定数十行内容。
- 路径和文件约定——相关版本中,实际的产物存放位置。
- 单项声明——现在才逐条核对。
类别错误修复成本低,但漏掉的代价很高。单项错误则恰好相反。
来源层级
使用可用的最高层级,并记录使用了哪个层级。
| 层级 | 来源 | 用途 |
|---|---|---|
| 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 都可用。
原因有两点,第二点比第一点更重要:
- 减少 token 用量。 根据本仓库各轮处理中使用的页面测量:对于一篇说明文档(Microsoft Learn),内容缩减了 78%;对于一个含大量链接的 API 索引,缩减了 33%。说明性文字的压缩效果很明显;链接表格则不那么明显。
- 确定性地返回全文。 你会得到完整页面的 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 天窗口解读为“没有活动”,并结束调查,而实际上仍有六个月的历史记录可供查看。
流程
- 盘点可检查的断言。 用
grep搜索技能中上述类别的内容——命令、标识符、ID、字段名称、带单位的数字。任何读者可能无法察觉其错误的内容都要纳入。 - 先解决类别级断言(工具名称、版本、命名方案)。据此重新界定下方所有内容,只保留经确认的部分。
- 对每个标识符进行批量探测,使用确定性检查。
- 在第 1 或第 2 层级验证其余断言,并记录来源。通过
defuddle.md获取页面,以较低成本阅读全文,而非摘要。 - 进行更正,并在提交说明中写明变更内容及原因——后续读者需要知道某项断言已经过检查,而不只是某行发生了移动。
- 进行对抗性复查。 重读更正内容,试着推翻它们。对本仓库的首次检查发现了一些初读时漏掉、二次检查时才发现的错误;假设你的检查也会如此。
- 仅在全部完成后盖章——见下文。
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— 结构检查和已核查数量报告