360 Secure SkillHub
360 安全技能中心

euthyna

提供代码安全审计的判定纪律与交付门禁:用确定性测量补齐调用方、测试覆盖与 git 来源三类事实,以 6 道门禁判定每条疑似漏洞结论真假,拿不出证据的一律降级为「观察」;自带零依赖 Node CLI 的 audit、history、coverage、gate,报告须落盘并通过 gate 校验。

modusensus
modusensus
热度 65.83
最近更新 2026-10-18 05:11:34
分类:安全依赖:代码执行 / 读写文件应用场景:代码审查 / 报告撰写 / 工作流

安装技能

复制下面的话术发给你的 Agent,它会通过 secure-skills 检索并安装该技能。

执行命令安装此 skill: npx secure-skills-cli add euthyna
Metadata
name
euthyna
description
代码安全审计的判定纪律与交付门禁。当用户要求对一段代码、一个 PR、一次变更或一条已有的漏洞结论做安全审查与验证时使用。它做三件事:补上模型算不准的确定性事实(调用方、测试覆盖、git 历史来源),用 6 道门禁判定每条结论真假,拿不出证据的一律降级为「观察」而不是「发现」。不适用于:仅询问某个 CVE 的详情、仅要求跑一次现成扫描器、纯文档或格式化改动、以及用户只要一句快速摘要且明确接受风险时。
whenToUse
用户要求审计代码或仓库的安全面、验证一条疑似漏洞结论真假、判定某个发现是否可利用、审查一次变更是否引入安全回归,或要求「审完再交付」时使用。只查密钥、只查依赖版本这类单一主题,直接用对应专项技能,不要触发本技能。
disable-model-invocation
true
user-invocable
true
license
Apache-2.0

euthyna — 代码安全审计的判定纪律

εὔθυνα:雅典官员离任时必须交出账目接受审查,通不过就无法体面离任。 本技能的核心机制即此:说"审完了"不算数,证据交齐、门禁通过,才算交付。

本技能不找 bug,它管住结论。 找 bug 由模型与现成工具做;本技能负责: 每条结论必须过门禁,过不了就只能降级为「观察」。


何时用 / 何时不用

用:

  • 用户要求审计代码、仓库、一次变更(PR / commit / diff)的安全面
  • 用户拿来一条疑似漏洞,问「这是真的吗」「是否可利用」
  • 用户要求审查某次变更是否把已有的安全防护改弱了
  • 用户要求「验证完再交付」

不用(直接拒绝或转其他技能):

  • 只问某个 CVE / 某个依赖的详情 → 用依赖审计类工具
  • 只要求跑一次现成扫描器并贴出结果 → 那是工具调用,不是审计
  • 纯文档、格式化、lint 类改动 → 无安全面
  • 用户明确只要快速摘要并接受风险 → 说明这是「非审计模式」,不套用本技能的裁定格式

三条不可跳过的规则

这三条是本技能与「让模型仔细点」的全部区别。任何阶段、任何路径都不许跳过。

规则 1:可测量的量一律由命令产出,模型只做判读

调用方数量、测试覆盖率、依赖版本、行号、提交哈希——这些一律不许靠印象回答。 模型可以在命令不可用时说「不可测量」,但不许估算后当成事实写进结论。

这条来自一次实测翻车:某包在 GitHub 上显示 5+ 人维护,而 npm 的实际权限列表只有 1 人; 另一个每周下载 1.6 亿次的包在某个数据源里显示零下载。手感估数字必错。

规则 2:证据强制到 file:line,禁止占位符

  • 每条主张引用具体位置,格式 path:L123,有版本时写 path:L123 (commit abc1234)
  • 禁用 probably / likely / 大概是 —— 去追真实代码,追不了就明说追不了
  • 演示代码(PoC)里禁止 TODO、...、// 攻击者在这里做某事
  • 一旦发现 PoC 用了人为绕过(mock、stub、禁用检查),该 PoC 判定无效

规则 3:缺数据 ≠ 干净

每个判据只能落到三态:已评估-干净 / 已评估-标记 / 因某原因不可评估。

  • 一次什么都没测到的运行,产出的是「不可评估」,不是「没发现问题」
  • 引用任何「干净」结论之前,先看覆盖表
  • 数字必须逐字引用,不得重新推导、四舍五入或修饰
  • 没发现不等于背书

入口决策树

用户给了什么?
│
├─ 一条已有的疑似漏洞断言 ──────────► 阶段 C:结论面验证
│   (「这是真的吗」「可利用吗」)        读 references/verification-gates.md
│                                       ★ 结论依赖「测过没有」→ 跑 coverage
│
├─ 一次变更(PR / diff / commit)────► 阶段 B:变更面审计
│   (「这个 PR 安全吗」)               读 references/change-audit.md
│                                       ★ 第一条命令:euthyna audit --base <分叉点>
│                                         (history + deps 一次跑完,见下节)
│                                       ★ 要谈测试覆盖   → 再跑 coverage
│
├─ 一个仓库 / 依赖面 ────────────────► 阶段 A:依赖面审计
│   (「依赖有没有问题」)               读 references/dependency-audit.md
│
└─ 一个仓库的整体安全审计 ───────────► 阶段 B 为主,A 与 C 作为子步骤

★ = 在动手人工读代码之前先跑,不是读完之后的补充说明。 产出器给的确定性事实,决定了人工阅读该往哪里使劲。

可以做多个阶段,但阶段之间不许互相替代。 特别是:阶段 B 报出的疑似发现,必须逐个走阶段 C 验证,不许直接写进报告当结论。


本技能自带的两个确定性测量

调用方、覆盖率、依赖漏洞、密钥这些不重造(见文末分工表)。但有两件事没人做、 且模型自己算不准,所以本技能自带一个零依赖 CLI:

测量 回答的问题 什么时候必须跑
audit 上面两个问题一次跑完(history 默认追溯最初引入者 + deps 枚举 lockfile 全部依赖),合并一份报告 每次审计的第一条命令(阶段 B 开场)
history 这次变更删掉的代码来自哪个提交?那个提交是不是安全修复? 需要单独重跑 history 时(如换 --base)
coverage 某个符号在测试运行中到底有没有被调用过? 报告里要说「测过 / 没测过」时(阶段 B / C)
# 唯一必需的参数是 --base(改前版本 / PR 分叉点):
node <euthyna 仓库>/bin/euthyna.js audit --base <改前版本> --repo <被审计的仓库>

# history / coverage 按需单独跑:
node <euthyna 仓库>/bin/euthyna.js history  --base <改前版本> --repo <被审计的仓库>
node <euthyna 仓库>/bin/euthyna.js coverage --coverage <覆盖率文件绝对路径> --symbol <符号名>

⚠️ node bin/euthyna.js 这种相对路径一定失败。 审计时当前目录是被审计的项目, 不是 euthyna 仓库,会报 Cannot find module。要用 euthyna 仓库的路径, 并把被审计仓库用 --repo 显式传进去(--base / --head 相对它解析)。

退出码——2 与 0 的区别就是这套东西存在的意义:

码 含义 怎么办
0 已测量,无安全相关发现 继续,但要读「未评估的判据」一节
10 已测量,存在 security 分类的事实 提高优先级,逐条走阶段 C,不要直接当结论
1 用法错误 修命令。这不是测量结果
2 完全无法测量 不得当作干净。换测量方式,或把判据标为「不可评估」

命令不可用时(没装、没有检出):把判据标为「不可评估」,不要手工估算顶上。

操作手册(命令、真实输出样例、失效情形):references/fact-producers.md。

执行纪律(复现命令与 PoC)

--verify 与 PoC 会在用户的机器上执行命令。跑任何复现/验证命令前:

  • 先取得用户明确批准,把要跑的命令原文列出来——不跑没报备的命令
  • 能在沙箱/临时容器里跑的(如 Docker),优先沙箱;无法沙箱时说明风险再跑
  • 报告里的 PoC 是给「拿到报告的人」复现用的;裁定过程中 agent 自己执行的每条命令同样适用本节

事实契约(测量与判定之间的接口)

本技能定义的是测量层与判定层之间的接口:docs/fact-contract-zh.md。

要点:

  • 契约只装事实,不装分数、不装严重性等级、不装修复建议
  • 每条事实必须带复现命令或产出工具+版本
  • 分数不得用作门禁(这是从 dsh-trust-check 学到的:它的契约明写「不要拿 score 或 band 做门禁」)
  • approximate 的事实不得参与门禁
  • unknown 不是「干净」——三态里最常被误用的就是它

事实的读法(能推出什么、不能推出什么):references/fact-contract.md。


裁定格式

无论走哪条路径,产出必须落到这个形状:

BUG #N TRUE POSITIVE — [一句话描述]
  门禁全部通过。证据:path:L123 (commit abc1234)
  复现:<可重跑的命令或 PoC 位置>
  可利用性:EASY / MEDIUM / HARD
  影响:具体到资金量 / 被提升的权限 / 被暴露的数据

BUG #N FALSE POSITIVE — [拒绝理由]
  门禁 N(门禁名)FAIL:<具体证据>
  例:第 98 行校验保证 packet_size >= 16,故 (packet_size - header_size) >= 8。
      下溢在数学上不可能。

BUG #N INCONCLUSIVE — [无法判定的原因]
  门禁 N(门禁名)未评估:<为什么>

第三态 inconclusive 必须存在。 Trail of Bits 的原始 fp-check 只有两态,因为它靠 Stop hook 无限强制继续直到补完; DSH 上做不到(Stop hook 必须自带限流,见 docs/dsh-stop-gate-zh.md)。 所以必须有这个诚实出口——不许把「没查完」写成 FALSE POSITIVE。


门禁的程序化校验(euthyna gate)

上面的格式不是摆设:euthyna gate <报告文件> 会机械地逐条核对——

  • TRUE POSITIVE:证据必须到 path:L123、必须有复现命令、必须说明影响、六门禁必须全过;
  • FALSE POSITIVE:至少一条门禁 FAIL 且带具体证据;
  • INCONCLUSIVE:至少一条门禁未评估,且没有任何 FAIL。

缺证据、缺复现、或门禁与裁定互相矛盾(如 TRUE POSITIVE 却带 FAIL 门禁)的 finding, 会被**降级为「观察」**并以退出码 10 报出——不需要裁定者自觉,校验器替你卡住。

node <euthyna 仓库>/bin/euthyna.js gate <报告文件>
# 重跑每条复现命令核验(白名单工具、按 argv 执行不走 shell):
node <euthyna 仓库>/bin/euthyna.js gate <报告文件> --verify --cwd <被审计仓库>
  • 退出码 0 = 全部通过;10 = 有 finding 被降级;2 = 报告无法读取 / 没有可校验的 finding。
  • --verify 下复现命令跑不通同样构成降级——「说能复现」不算数,跑通才算。

报告交出去之前,先过一遍 euthyna gate。 它校验的是报告自己的自我声明, 不测量目标仓库——因此它不能代替裁定,只能保证「拿不出证据的结论不会以已确证的面貌出去」。


报告必须落盘

  • 只输出到聊天算失败。必须写报告文件
  • 文件名:<PROJECT>_EUTHYNA_AUDIT_<YYYY-MM-DD>.md
  • 写文件失败的降级顺序:当前工作目录 → 用户桌面 → 临时目录 → 最后手段才输出到聊天并提示用户手动保存

交付前的自检(逐条打勾,缺一条不许说「审完了」)

  • 每个疑似发现都走了完整的阶段 C,不是「看一眼觉得是假的」
  • 跑过 audit(或 diff 有删除行时跑过 history);删自安全修复提交的代码已按最高风险处理
  • 报告里每句「测过 / 没测过」都有 coverage 输出支撑,或已标为「不可评估」
  • 退出码为 2 的判据没有出现在「干净」一栏
  • 每条结论都带 file:line
  • 每条结论都标了三态之一(干净 / 标记 / 不可评估)
  • 覆盖表写清了哪些判据没评估、为什么
  • 恶魔代言人 13 问逐条答过(含防假阴性的第 12、13 问)
  • 报告文件已落盘
  • 报告已通过 euthyna gate 校验(退出码 0;有降级要读降级原因)
  • 没有把「缺数据」写成「干净」

参考文件

按需加载。不要一次全读——渐进披露是这套东西能在长会话里保持有效的原因。

文件 内容 什么时候读
references/verification-gates.md 阶段 C:路由、6 门禁、误报清单、恶魔代言人、PoC 规则 判定一条已有断言时(最常用)
references/change-audit.md 阶段 B:基线、深度 vs 风险、来源归属、爆炸半径、对抗建模 审一次变更时
references/dependency-audit.md 阶段 A:清单/锁文件、三态、文体规范、禁止事项 审依赖面时
references/bug-classes.md 9 类缺陷的专项检查与默认假设方向 定了缺陷类别之后
references/meta-mechanisms.md 六个元机制:让流程不退化成走过场 审计开始时读一次
references/fact-producers.md 操作手册:两个测量何时跑、命令怎么写、退出码怎么处理、真实输出样例、失效情形 跑产出器之前(阶段 B / C)
references/fact-contract.md 拿到事实之后能推出什么、以及它不能推出什么 读完产出器输出之后

技能文本自包含:以上文件都在 references/ 内,不依赖技能目录之外的路径, 可以整目录复制到任何技能根使用。

⚠️ 但产出器本身是独立程序,不在技能目录里。复制技能不会把 CLI 一起带走。 新环境里要单独安装、或提供本项目的一份检出,否则相关判据只能标为「不可评估」—— 那也是一个诚实的结果,比手工估算强。见 references/fact-producers.md 第一节。


与既有生态的分工(避免重复劳动)

已经有人做的事 euthyna 怎么做
调用图 / 爆炸半径(dsh-tool-lens、dsh-blast-radius) 不重造,消费其输出
覆盖率(dsh-code-coverage) 不重造,消费其输出
依赖漏洞(dsh-dep-vuln-scan 等) 不重造,消费其输出
密钥扫描(dsh-code-security) 不重造,消费其输出
交付门禁机制(dsh-doublecheck、formalswarm) 机制可借鉴;euthyna 的门禁内容是安全专属的 6 门禁
证据生命周期与 PoC 实验室(dsh-omv) 可并存:dsh-omv 可作为事实契约的消费方

euthyna 只补两件没人做的确定性测量(见 docs/positioning-zh.md §7.2): git 安全回归的机械判定,以及「符号 × 真实执行覆盖」的 join。