Conalh/CapabilityEcho

GitHub: Conalh/CapabilityEcho

CapabilityEcho 是一个代码 diff 能力偏移检测器,用于在 PR 中标记由 AI agent 生成代码新引入的网络、子进程、eval、生命周期脚本和 workflow 权限等可执行能力。

Stars: 0 | Forks: 0

# CapabilityEcho [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Node](https://img.shields.io/badge/node-%E2%89%A522-339933?logo=node.js&logoColor=white)](package.json) [![仅限本地](https://img.shields.io/badge/runs-local%20only-2ea44f)](#工作原理) [![Release](https://img.shields.io/github/v/release/Conalh/CapabilityEcho)](https://github.com/Conalh/CapabilityEcho/releases) **一个用于 AI agent pull request 的代码 diff 能力检测器。** CapabilityEcho 会标记由代码本身(而非 agent 配置)引入的新的网络、subprocess、eval、生命周期、依赖项、Dockerfile 和 workflow 权限信号。 即使 agent 配置保持不变,diff 也可能添加一个 `fetch('https://...')`、一个 `postinstall` 脚本、一个 `contents: write` 的 workflow,或者一个 subprocess 路径,使得 agent 的输出比任务所暗示的更强大。CapabilityEcho 能够在确切添加的代码行上,让这种可执行能力的偏移变得可见。 ``` flowchart LR Diff["PR diff
added lines"] --> Echo Source["Source code
JS · TS · Python"] --> Echo Manifests["Manifests + workflows
package · lockfiles · Actions · Docker"] --> Echo Echo[("CapabilityEcho
capability drift scan")] --> Report["Review output
annotations · markdown · JSON"] Report --> Reviewer["Reviewer sees
new executable power"] classDef input fill:#1e293b,stroke:#334155,color:#e2e8f0 classDef engine fill:#0f172a,stroke:#1e293b,color:#e2e8f0,stroke-width:2px classDef output fill:#0c4a6e,stroke:#0369a1,color:#e0f2fe class Diff,Source,Manifests input class Echo engine class Report,Reviewer output ``` **另请参阅:** [ScopeTrail](https://github.com/Conalh/ScopeTrail) 用于检测配置偏移 · [TaskBound](https://github.com/Conalh/TaskBound) 用于检测任务与 diff 之间的范围蔓延 · [GovVerdict](https://github.com/Conalh/GovVerdict) 用于生成合并后的套件判定。 **快速开始** — 在内置的测试用例上运行它: ``` git clone https://github.com/Conalh/CapabilityEcho && cd CapabilityEcho npm install && npm run build node dist/index.js diff --old test/fixtures/capability-drift/old --new test/fixtures/capability-drift/new --format markdown ``` 更倾向于使用 CI?下面的快速入门和示例输出中提供了一个开箱即用的 GitHub Action(默认为建议模式)以及真实的测试用例运行结果。 ## 适用场景 CapabilityEcho 是**能力偏移**检测器——它会在 PR 添加的确切代码行上标记获得了新可执行能力的代码。 | 工具 | 输入 | 捕获 / 判定 | 输出 | 适用场景 | |---|---|---|---|---| | [warden](https://github.com/Conalh/warden) | policy + tool action | 允许 / 拒绝 / 询问 | 判定 | 您需要确定性的 runtime policy 决策 | | [barbican](https://github.com/Conalh/barbican) | MCP tools/list + tools/call | 被拒绝的调用、询问处理、工具投毒 | 强制执行的 MCP proxy + 报告 | 您需要 MCP runtime 强制执行 | | [ScopeTrail](https://github.com/Conalh/ScopeTrail) | PR base/head agent config | 权限/配置偏移 | 批注 + 报告 | PR 更改了 agent config | | [PolicyMesh](https://github.com/Conalh/PolicyMesh) | 当前仓库 policy/config 文件 | agent 表面之间矛盾的规则 | 报告 / SARIF | 当前 policy 不一致 | | **CapabilityEcho** | PR diff | 新的可执行能力 | 批注 + 报告 | 代码获得了网络/subprocess/eval/生命周期/workflow 能力 | | [TaskBound](https://github.com/Conalh/TaskBound) | 声明的任务 + PR diff | 范围蔓延 | 批注 + 报告 | agent 可能偏离了任务 | | [SessionTrail](https://github.com/Conalh/SessionTrail) | Cursor/Claude/Codex JSONL 记录 | 高风险的 runtime 行为 | 报告 / SARIF | agent 会话已经运行过 | | [GovVerdict](https://github.com/Conalh/GovVerdict) | JSON 报告 | 去重后的套件判定 | 合并后的报告 | 您想要一个最终的审查判定 | | [AgentPulse](https://github.com/Conalh/AgentPulse) | 实时会话事件 | 轨迹状态 | 终端仪表板 | 您想要实时会话观察 | | [agent-gov-core](https://github.com/Conalh/agent-gov-core) | 共享的 schemas/parsers | 通用 Finding/Report 模型 | 库 | 工具需要共享的报告原语 | ## 为什么需要它 一个 PR 不需要编辑 `.mcp.json` 或 `.claude/settings.json` 就能扩展 agent 生成的更改所能做的事情。它可以直接在代码中添加网络调用、subprocess 执行、生命周期脚本、workflow 权限或高能力的依赖项。 CapabilityEcho 的存在是为了让这些新的可执行能力变得可审查。它不决定一种能力是否总是有害的;它只是将审查者引导到 diff 获得新能力的确切代码行。 ## 它能捕获什么 | 偏移类别 | 示例 | | --- | --- | | **网络能力** | 添加了 `fetch`、HTTP client、动态 endpoint 调用、workflow/composite-action `curl` 或涉及网络的 npm 脚本。 | | **Subprocess 能力** | 添加了 shell/process 执行、动态命令构造、shell pipeline 或无扩展名的 shebang 脚本。 | | **生命周期能力** | `postinstall`、发布脚本、通过管道传递给 shell 的安装程序或 package hook。 | | **Workflow 能力** | 新的写入权限、外部请求、密钥暴露模式、高风险的 PR 目标流程。 | | **依赖项能力** | 新的高能力 package 或引入敏感行为的 lockfile 更改。 | ## 它的捕获效果如何(以及这些数字*并不代表*什么) CapabilityEcho 内置了一个包含 34 个 PR 前/后快照的标注语料库 —— 20 个恶意 (悄悄添加了新能力)和 14 个良性的对抗性险情(同源的 `fetch`、`yaml.safe_load`、普通的依赖项添加、重构)。 | 指标 | 值 | | --- | --- | | 用例 | 34(20 个恶意,14 个良性) | | 此提交语料库上的检测召回率(任何发现) | 100.0% | | 此提交良性语料库上的误报率 | 0.0% | | 此提交语料库上的精确率 | 100.0% | | 在 `--fail-on=high` CI 门限下的召回率 | 85.0% | | 识别出的正确主要能力 | 20/20 | **请将此视为规范和回归测试套件,而不是针对 独立真实情况的评估。** 检测器和测试用例是同一个作者编写的,因此 100% 的精确率 / 0% 的误报率意味着检测器完成了它们被设计用来做的事情,并且 在更改中继续保持这样做 —— 它*并不能*证明它们能捕获真实 agent 或 攻击者在现实中产生的行为。每个恶意的测试用例也是一个单一的、教科书式的 模式实例;真实的 PR 更加杂乱。请将这些数字视为“工具行为符合 规范”,并阅读 [威胁模型与局限性](#threat-model-and-limits),了解该规范 有意覆盖和未覆盖的内容。 在 `high` 门限下的 85% 是校准设置,而不是遗漏:三个恶意用例(外部 `fetch`、Python `requests.get`、`wget` 下载)确实是*中等*严重性 —— 在 `medium` 上设置门限,即可在语料库中的每个恶意用例上使 CI 失败。 `npm run benchmark` 是一个门限回归检查:如果遗漏了恶意测试用例、 标记了良性测试用例、丢失了预期的类型/严重性、 测试用例生成器与提交的测试用例发生偏移,或者 git 模式 / 内置的 Action 探针失败,它就会报错。 使用 `npm run benchmark` 进行复现。方法论和完整的语料库位于 [`benchmark/`](benchmark/README.md);重新生成的报告位于 [`benchmark/RESULTS.md`](benchmark/RESULTS.md)。 ## 快速入门 ### 作为 GitHub Action(最常用) ``` name: CapabilityEcho on: pull_request permissions: contents: read jobs: capabilityecho: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: fetch-depth: 0 # required: PR base + head are compared - uses: Conalh/CapabilityEcho@v0.3.3 with: fail-on: none # start advisory, raise to high/critical later ``` 这会将 Markdown 报告写入 Actions 步骤摘要,并在风险代码行上发出 PR 可见的 `::warning` 批注。 ### 本地 CLI ``` git clone https://github.com/Conalh/CapabilityEcho cd CapabilityEcho npm install npm run build # 比较两个目录(在 bundled fixture 上尝试的最快方式) node dist/index.js diff ` --old test/fixtures/capability-drift/old ` --new test/fixtures/capability-drift/new ` --format markdown # 在 real repo 中比较两个 git refs node dist/index.js diff --repo . --base main --head HEAD --format text ``` CapabilityEcho 需要 Node 22 或更高版本。CI 会测试 Node 22 和 24。 ## 示例输出 来自内置测试用例的真实输出,`--format text`: ``` CapabilityEcho capability drift: CRITICAL Scanned executable surfaces: source code, package manifests, GitHub workflows. Excluded surfaces: AI-agent config. Signals: GitHub Actions workflow-level write permissions, workflow external network requests, external network fetch calls, npm lifecycle scripts, pipe-to-shell install scripts, network or publish npm scripts Top recommendations: Replace remote pipe-to-shell patterns with pinned, reviewable install steps. | Use the narrowest permission scope required for this job. | Review lifecycle scripts carefully; they run automatically on install. [HIGH] GitHub Actions workflow-level write permission (contents) — contents:write applies to every job [MEDIUM] Workflow external request — step performs an external network request [MEDIUM] External network fetch — added code performs an external HTTP request [HIGH] package.json postinstall script — added or changed npm lifecycle script [CRITICAL] package.json postinstall pipe-to-shell — script pipes remote content into a shell [MEDIUM] package.json postinstall network command ``` `--format json` 输出标准的 [agent-gov-core](https://github.com/Conalh/agent-gov-core) `Report` 封装 —— 套件中每个工具输出的结构都相同,因此 [GovVerdict](https://github.com/Conalh/GovVerdict) 可以将它们合并: ``` { "schemaVersion": "1.0", "tool": "capability_echo", "rating": "critical", "findings": [ { "tool": "capability_echo", "kind": "capability_echo.script_pipe_to_shell", "severity": "critical", "message": "Script downloads and pipes content directly into a shell.", "location": { "file": "package.json", "line": 12 }, "salientKey": "package.json postinstall pipe-to-shell", "data": { "subject": "package.json postinstall pipe-to-shell", "recommendation": "Replace remote pipe-to-shell patterns with pinned, reviewable install steps.", "surface": "package" }, "fingerprint": "..." } ], "data": { "changedFileCount": 3, "scannedSurfaces": ["source", "package", "workflow"] } } ``` ## 工作原理 - 针对**检出的仓库**运行 —— 无需上传,无需托管扫描器,无遥测。 - 解析 diff(`--old`/`--new` 目录,或 `--base`/`--head` git refs)并检查跨源代码(`.js/.ts/.mjs/.cjs/.mts/.cts`、Python、shell、无扩展名的 shell shebang)、package 清单 + lockfile、GitHub workflows/composite actions 以及 Dockerfile/Containerfile 构建的**新增行**。 - 触发小而明确的检测器来识别扩展能力的模式:外部网络调用、subprocess/shell 生成、动态 `eval`/`exec`、不安全的反序列化、高能力依赖项、npm 生命周期和通过管道传递给 shell 的脚本、workflow 写入权限和外部请求、包含密钥的 exfil 模式。 - Workflow 会经过结构化的 YAML 处理,并辅以对 `run:` 块内 shell 文本的逐行检查。 - 发现结果包含严重性、文件 + 行号以及建议。仅当满足 `fail-on` 条件时,该 Action 才会以非零状态退出。 - 来自受信任基础修订版本的已签入例外基准可以抑制已知的发现结果。PR 本地的例外策略更改会被显眼地报告,并且只有在合并后才生效。 CapabilityEcho **不**扫描诸如 `.mcp.json` 或 `.claude/settings.json` 之类的 agent 配置文件;那是 [ScopeTrail](https://github.com/Conalh/ScopeTrail) 的职责范围。两者设计为可一起运行。 ## 例外基准 CapabilityEcho 自动从 diff 的受信任基础端(目录模式下的 `--old`,git 模式下的 `--base`)加载 `.capabilityecho-exceptions.json`。您可以使用 `--exceptions ` 或 Action 的 `exceptions-file` 输入覆盖该路径。 如果 PR 添加、删除或编辑了例外文件,则该候选策略不会应用于同一分析。该更改将作为 `capability_echo.exception_policy_changed` 进行报告,并仅在合并后生效,即当它成为受信任基础修订版本的一部分时。 ``` { "exceptions": [ { "kind": "capability_echo.external_fetch_added", "pathPrefix": "src/vendor/", "expires": "2026-12-31", "reason": "Legacy vendor updater is approved until replacement lands." } ] } ``` 规则使用共享的 `agent-gov-core` 例外结构:`kind` 是必需的;`salientKey` 和 `pathPrefix` 缩小匹配范围;`expires` 使过期的例外可见;`reason` 是 CapabilityEcho 必需的,因此每次抑制都有一个已签入的理由。活动匹配项将从发现结果中移除,计为 `suppressedFindingCount`,并记录在 `data.suppressedFindings` 中,包含指纹、类型、位置、原因和到期时间。过期的匹配项不会降低原始发现结果的严重性;原始发现结果仍以其原始严重性保持可见,并且一个单独的低严重性 `capability_echo.exception_expired` 发现结果会通过 `data.exceptionReason` 解释过期的例外。 无效的例外文件不会抑制任何内容。它们会标记 `analysisIncomplete`,添加 `exception_config_error` 诊断,并保持所有发现结果可见。候选端的无效例外更改会被报告但不会被应用。输入读取和解析器诊断不属于发现结果,不能被例外规则抑制。 ## 威胁模型与局限性 **CapabilityEcho 是为粗心但诚实的作者构建的,而不是为了蓄意逃避者。** 它捕获来自幼稚 agent 和普通错误的能力偏移 —— 即获得了真正新能力且以显而易见的方式编写的代码。它*不是*一种 对抗性控制:任何想要绕过它的人都可以轻而易举地击败基于新增行的正则表达式检测(间接调用、别名、计算出的成员访问、字符串 拼接、base64、通过变量进行 `require`)。如果您的威胁是故意逃避的作者,那么此工具就是错误的防御层 —— 请将强制执行推送到 runtime ([warden](https://github.com/Conalh/warden)、[barbican](https://github.com/Conalh/barbican)), 在 runtime 中,能力是被观察到的,而不是被模式匹配出来的。 在您信任某个判定之前,值得了解的具体限制: - **严重性和可逃避性在此成反比。** 最高严重性的 类别是最容易被漏过的。Secret-exfil 是最明显的例子:最 自然的真实模式是在*未更改的*文件中定义的 URL 或 token,并且仅在 diff 中被引用 —— 而 CapabilityEcho 不进行跨文件 taint 分析,因此它 永远看不到源。一个“干净”的 CapabilityEcho 运行并不能证明不存在 exfiltration 路径;它只能证明没有以显而易见的、 单文件、新增行的方式引入该路径。 - **JS/TS 和 Python 是基于文本匹配的,而不是结构化匹配。** Workflow 会经过结构化的 YAML 处理,但源代码检测器是基于模式的。它们可以捕获字面量的 外部 URL、添加的动态 endpoint 变量以及一些同文件的后续 参数,但别名导入、解构和复杂的成员表达式调用 目标仍然可能被漏掉。解决此问题已在路线图上(见下文)。 - **设计上的新增行偏差。** 基础中已经存在,或者 只能通过未更改的代码访问的能力,是故意超出范围的。 - **目前仅支持 npm 的 `package-lock.json` 作为 Package lockfile。** Yarn、 pnpm、Bun、Poetry 和 uv lockfile 格式是积压工作;依赖项 清单扫描仍然涵盖 `package.json`、`requirements*.txt` 和 `pyproject.toml` 的直接声明。 ## 值得注意的设计选择 - **代码,而非配置。** 即使 agent policy 表面没有改变,该工具也能捕获由可执行工件引入的能力。 - **新增行偏差。** 发现结果始终与 PR 引入的内容绑定,这使审查集中关注当前的更改。 - **小型检测器。** 扫描器是有意保持明确和可解释的,而不是假装是一个完整的语义安全引擎。 - **套件形式的输出。** JSON 使用共享的 `Finding` 契约,因此 GovVerdict 可以将其与 agent-gov 工具的其余部分合并。 - **路线图:结构化源代码解析。** Workflow 已经实现了结构化解析;接下来是 JS/TS(`typescript` 是一个依赖项)和 Python 源代码,以解析别名导入、解构和成员表达式调用目标,而不是通过文本方式匹配它们。这可以一次解决一类单文件绕过问题 —— 它不能解决跨文件 taint 问题,那是一项独立的、更庞大的工作。 ## 选项 ### CLI 标志 (`capabilityecho diff ...`) | 标志 | 默认值 | 用途 | | --- | --- | --- | | `--old ` / `--new ` | — | 目录模式 diff。 | | `--repo ` / `--base ` / `--head ` | repo = cwd | 真实仓库中两个 ref 之间的 Git 模式 diff。 | | `--exceptions ` | 存在时为 `.capabilityecho-exceptions.json` | 从旧目录或基础 ref 加载的相对于仓库的 JSON 例外基准。 | | `--format` | `text` | `text`、`markdown`、`json`(标准封装)、`github`(批注)。 | | `--fail-on` | `none` | 如果最高严重性的发现结果达到此级别,则以非零状态退出:`none`、`low`、`medium`、`high`、`critical`。 | ### GitHub Action 输入 | 输入 | 默认值 | 用途 | | --- | --- | --- | | `repo` | `$GITHUB_WORKSPACE` | 要检查的检出路径。 | | `base` / `head` | PR base / head | 覆盖正在比较的 ref。 | | `fail-on` | `none` | 导致作业失败的严重性。 | | `max-findings` | `0`(无限) | 按严重性将 Action 输出 + 步骤摘要截断为前 N 个。评级和 `fail-on` 仍然使用完整的集合。 | | `max-output-bytes` | `0`(无限) | 抑制超过此大小的 `report-markdown` / `report-json` Action 输出(保留步骤摘要)。 | | `report-file` | _空_ | 写入完整 Markdown 报告的路径(加上同级的 `.json`)。与 `actions/upload-artifact` 配合使用。 | | `exceptions-file` | 存在时为 `.capabilityecho-exceptions.json` | 从受信任的基础 ref 加载的相对于仓库的 JSON 例外基准。 | ### GitHub Action 输出 `rating`、`has-findings`、`finding-count`、`changed-file-count`、`analysis-incomplete`、`analysis-diagnostic-count`、`analysis-diagnostics`、`suppressed-finding-count`、`expired-exception-count`、`surface-summary`、`severity-summary`、`capability-summary`、`top-recommendations`、`adoption-evidence`、`report-markdown`、`report-json`。 ## agent-gov 套件的一部分 仅限本地的开源工具,用于审查 AI agent PR 和编码会话是否存在配置偏移、policy 不匹配和范围蔓延。每个工具都涵盖一种正交的故障模式;它们共享标准的 `Finding` schema,并且可以合并为单个判定。 | 仓库 | 捕获内容 | | --- | --- | | [ScopeTrail](https://github.com/Conalh/ScopeTrail) | PR base 和 head 之间的 agent 配置偏移。 | | [PolicyMesh](https://github.com/Conalh/PolicyMesh) | 矛盾的 agent 指令和导致行为不可重现的配置偏移。 | | **CapabilityEcho** *(本仓库)* | 由代码、清单、workflow 和 Dockerfile 引入的能力偏移。 | | [TaskBound](https://github.com/Conalh/TaskBound) | 声明任务与实际 diff 之间的范围蔓延。 | | [SessionTrail](https://github.com/Conalh/SessionTrail) | Cursor / Claude Code / Codex 会话记录中的高风险 runtime 行为。 | | [GovVerdict](https://github.com/Conalh/GovVerdict) | 将上述工具的 JSON 报告合并为一个去重的审查。 | | [agent-gov-core](https://github.com/Conalh/agent-gov-core) | 共享解析器、标准的 `Finding` schema 和 `mergeFindings`。 | | [agent-gov-demo](https://github.com/Conalh/agent-gov-demo) | 带有恶意 PR 的 Demo 沙箱,该 PR 会触发所有五个审查工具。 | MIT。欢迎通过 [Issues](https://github.com/Conalh/CapabilityEcho/issues) 提交错误报告和误报报告。
标签:DevSecOps, GitHub Actions, MITM代理, 上游代理, 云安全监控, 人工智能, 代码审查, 用户模式Hook绕过, 网络信息收集, 自动笔记, 自定义脚本, 静态分析