euthyna
面向 AI 编码代理的确定性安全审计事实与结论门禁:euthyna history 把每行被删代码归因到提交并标记安全修复类删除(--origins 追到最初引入者),euthyna coverage 报告哪些改动符号从未被测试调用,euthyna gate 用六道门禁逐条校验结论,拿不出证据的一律降级为观察。
安装
dsh plugin --profile web add euthyna
沙箱测试结果
| 阶段 | 结果 |
|---|---|
| 1 安装 | ✓ |
| 2 列装 | ✓ |
| 3 挂载 | ✓ |
| 4 启动 | — |
四阶段在隔离沙箱里跑:安装(有网)→ 列装 → 挂载 → 启动(后三步断网)。
README
<p align="center">
<img src="assets/euthyna.png" width="150" alt="euthyna logo">
</p>
<h1 align="center">euthyna</h1>
εὔθυνα — in classical Athens, the audit every outgoing official had to submit.
You did not get to simply walk away from office. You handed over your accounts and they
were examined. Pass, and you left with your standing intact. Fail, and you faced trial.
<p align="center">
<a href="https://github.com/slow-stack/euthyna/actions/workflows/ci.yml"><img src="https://github.com/slow-stack/euthyna/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
<a href="https://www.npmjs.com/package/euthyna"><img src="https://img.shields.io/npm/v/euthyna?style=flat&color=007EC6" alt="npm version"></a>
<a href="https://www.npmjs.com/package/euthyna"><img src="https://img.shields.io/npm/dm/euthyna?style=flat&color=007EC6" alt="npm downloads"></a>
<a href="https://www.npmjs.com/package/euthyna"><img src="https://img.shields.io/npm/l/euthyna?style=flat&color=007EC6" alt="license: Apache-2.0"></a>
<a href="https://search.sigstore.dev/?logIndex=2915999059"><img src="https://img.shields.io/badge/npm%20provenance-verified-007EC6?style=flat" alt="npm provenance verified"></a>
<a href="https://awesome-dsh-plugin.com/p/slow-stack/euthyna/"><img src="https://img.shields.io/badge/DSH%20plugin-listed-blue" alt="Listed in the awesome-dsh-plugin catalog"></a>
</p>
euthyna is a code security audit framework for AI coding agents. It is not another
scanner. It does two things: it **produces the facts an agent cannot compute by reading
code, and it forces every security claim the agent makes through gates before it
counts as a finding**.
📖 The problem, in plain words
When an AI coding agent touches security, it fails in two specific ways:
1. It reports things that are not real. Code that looks dangerous gets called a
vulnerability, without tracing the data. In validation runs on real codebases — a
JavaScript one, a Python one, a Go one, and a Rust one — the pattern-matched
"vulnerabilities" were mostly false: 5 out of 5 refuted at the gates on the first run;
2 out of 3 refuted, the third unresolved (INCONCLUSIVE, supply-chain-dependent) on the
second; 4 out of 4 refuted on the third. The fourth run is the one that tests the other
half: it faced a real vulnerability, and the gates let it through — 1 TRUE POSITIVE
(fourth gate evidenced by a PoC run against both the fixed and the unfixed release), 3
refuted, 1 INCONCLUSIVE. A sieve that only ever says no would have refuted that one too. See
the
2. Its reassurances cannot be checked. "I'm done." "The tests cover this." "It's
safe now." These are assertions. You cannot tell a done-claim from a done-deal.
Neither is fixed by telling the agent to be more careful. euthyna changes the handshake
between you and the agent:
**The agent saying "I'm done" does not count. The accounts get handed over, and the
gates decide.**
⚖️ The two things it does
1. It measures what a model cannot
Three fact producers — a zero-dependency Node CLI:
history— for every line a change deletes, it attributes the line to a commit
and classifies that commit from its message, its own diff, and the deleted line's
content (a deleted if (!authorized) is flagged even under a "tweaks" subject). By
default the attribution traces the commit that first introduced the content with
git log -S (--no-origins falls back to blame's "last touched", which is faster
but mis-attributes a security line later moved by a formatting commit). If the
deleted code came from a security fix, that is flagged. This is git archaeology no
model can do from reading a diff.
coverage— was this symbol ever actually invoked by a test? It has exactly two
answers: never invoked (established), or entered but that proves nothing about any
specific call site (unknown). It never reports "executed" — V8 coverage marks
unreachable code as covered, and "line covered → call ran" is wrong in exactly the
direction an audit cannot afford. The reasoning is in
docs/fact-contract.md §6.2.
deps— what version is a dependency actually pinned to? It reads the lockfile
(package-lock.json / Cargo.lock / go.mod) and reports the resolved versions, or that a
dependency is absent from the tree entirely. This is the fact that resolves
supply-chain claims ("the app uses a vulnerable version of X"): the version-to-CVE
mapping is left to the adjudication layer, exactly as the fact contract requires. See
docs/fact-contract.md §6.3.
2. It gates what the agent claims
The skill is the audit discipline itself, as loadable
Markdown. Every security claim must pass six gates — reachability, trust boundary, real
impact, and their counterparts. A claim that cannot produce evidence is **downgraded to
an observation**, not reported as a finding. "I'm done" becomes a package: claims,
evidence, and the commands that reproduce both.
The gates are not only prose. euthyna gate <report> reads an adjudication report and
mechanically checks every finding against its verdict — evidence down to path:L123, a
reproduce command, an impact statement, consistent gate statuses — and downgrades
…(truncated)
以上内容来自 npm 上的 README,我们只做格式化,不改内容。
版本
npm 上共发布:14,最后更新 2026-11-04