robert-auger/safer-dependencies

GitHub: robert-auger/safer-dependencies

为 Claude Code 设计的依赖安全审计层,在 AI 写入或安装依赖时自动检测并修正包含漏洞、仿冒或已废弃的包。

Stars: 26 | Forks: 0

# Claude Code 的更安全依赖 自动检查 Claude 通过其 `Write`、`Edit` 和 `Bash` 工具添加的依赖,并自动就地纠正存在漏洞的版本。支持跨 npm、PyPI、RubyGems、Maven、Go、Rust 和 PHP (Composer) 进行来源、版本时长、漏洞和哈希完整性检查。覆盖范围仅限于通过 Claude 工具进行的写入操作(拦截模式会在文件落地*之后*的同一工具周期内纠正存在漏洞的固定版本——而不是在之前);有关确切覆盖和不覆盖的内容,请参阅 [CAPABILITIES.md](CAPABILITIES.md)。 ## 目录 - [快速开始](GETTING-STARTED.md) — 从零到安装完成约需五分钟 - [功能介绍](#what-it-does) - [工作原理](#how-it-works) - [普通模式(手动)](#normal-mode-manual) - [拦截模式(自动)](#intercept-mode-automatic) - [安装前模式(Bash Hook)](#pre-install-mode-bash-hook) - [安装后模式(Bash Hook)](#post-install-mode-bash-hook) - [Agent 后模式(Agent Hook 对)](#post-agent-mode-agent-hook-pair) - [触发条件](#what-triggers-it) - [仓库内容](#whats-in-this-repo) - [支持的生态系统](#supported-ecosystems) - [安装说明](#install) - [配置](#configuration) - [警告级别](#warning-levels) - [审计日志](#audit-log) - [环境要求](#requirements) - [常见问题](#faq) ## 功能介绍 当像 Claude 这样的 AI 编程助手为您的项目添加包时,它们通常会选择听起来合适的版本——而不检查它是否存在已知的安全漏洞、该包是否仍在积极维护,或者其名称是否因为拼写错误而变成了恶意仿冒包。safer-dependencies 通过驻留在 Claude 和您的 manifest 文件之间,在任何不安全的版本进入您的代码之前自动运行安全检查来解决这个问题。 当 Claude 即将向您的项目添加包时,此 skill 会拦截并运行 5 项检查: 1. **来源** —— 官方注册表、域名抢注检测(npm/PyPI/RubyGems/Maven/crates.io)、包时长 2. **版本时长** —— 选择 7 天前发布的最新稳定版本(冷却窗口) 3. **漏洞扫描** —— OSV API,并在可用时使用生态系统原生工具(npm audit、pip-audit、bundle audit) 4. **哈希固定完整性** —— 对于带有 `--hash=sha256:...` 固定版本的 PyPI `requirements.txt` 行,会根据 PyPI 发布的哈希值验证声明的哈希;不匹配会发出 WARNING 5. **废弃和过时的包** —— 已知的废弃包(例如 `paperclip`、`request`、`pycrypto`、`github.com/dgrijalva/jwt-go`)会被立即硬阻止并提供建议的替代品;超过 2 年没有稳定版本的包会收到建议性的 `STALE:` 警告。被硬阻止的包会从 manifest 中移除,Claude 会询问如何继续;仅被标记为过时的包会被保留在原处。 如果发现问题,Claude 会发出警告,并可能回退到更安全的版本。所有检查都会记录到 `~/.claude/safer-dependencies-audit-YYYY-MM.log`(每个日历月一个文件)。 ## 工作原理 该 skill 分为五种模式运行(概述如下;最深层的设计原理位于 `skills/safer-dependencies.md` 中): ### 普通模式(手动) 当 Claude 即将写入 `import`、向 manifest 添加包或更新 lock 文件时,该 skill 会在您的会话中内联运行: 1. 向包注册表查询稳定版本 2. 自动选择 7 天前发布的最新版本(确定性——无需 LLM 判断) 3. 通过生态系统工具和 OSV API 检查已知漏洞 4. 在可用的情况下验证包签名 5. 如果发现问题则发出警告,并固定确切版本 6. 将结果记录到审计追踪中 版本选择由 skill 附带的独立 Python 脚本处理,而不是由解释规则的 LLM 处理。命令输出 `SELECTED: `,Claude 会完全使用该版本。 ### 拦截模式(自动) 使用 `PostToolUse` hook 配置 `.claude/settings.json`,以启用自动、透明的包验证: 1. Claude 编写一个带有最初请求版本的 manifest 文件(例如 `package.json`)—— 文件会写入磁盘 2. `PostToolUse` hook 在写入完成后立即触发并调用 `safer-dependencies-shim.sh` 3. shim 读取文件,解析声明的包,并运行所有安全检查(域名抢注、废弃、CVE、陈旧、哈希固定) 4. 如果需要更正,shim 会使用安全版本**就地重写 manifest**(或移除没有安全版本的条目) 5. shim 通过 stdout 上的 `hookSpecificOutput.additionalContext` 发出信号(`UPDATED:`、`BLOCKED:`、`WARNING:`、`STALE:`、`MAJOR-UPDATE-CONFIRM:`、`REFACTOR-REQUIRED:`、`REGRESSION:`、`TYPOSQUAT-CONFIRM:`、`VERIFY:`、`CLEAN:`)。当审计日志显示同一(文件、包)先前已被更正为相同的安全目标时,`REGRESSION:` 会在 `MAJOR-UPDATE-CONFIRM:` 之前出现——也就是说,子 agent 或过时的计划重新引入了已知的易受攻击版本,而 orchestrator 应恢复先前批准的版本,而不是重新决定大版本升级。 6. Claude 接收这些信号作为系统提醒,并执行后续工作(查找受影响的 import、运行测试、针对破坏性更改进行重构) **设计说明 —— Shape C(写入后纠正):** 该 hook 不会阻止写入。每个易受攻击的版本首先写入磁盘,然后在同一工具使用周期内进行自动更正。这是相对于 `PreToolUse` 阻止设计的刻意选择 —— 有关权衡,请参阅 [FAQ.md](FAQ.md#why-posttooluse-post-write-corrective-instead-of-pretooluse-pre-write-blocking-for-the-manifest-path)。 **示例信号:** ``` UPDATED: aiohttp 3.8.5 → 3.9.0 (HIGH: 33 CVEs fixed) ``` 父 agent 使用这些信号来识别受影响的代码并按需重构。 ### 安装前模式(Bash Hook) 使用 `PreToolUse:Bash` hook 配置 `.claude/settings.json`,以启用对 包管理器安装命令的预检审计。这补充了(而不是 替换)拦截模式——它们共同构成了分层防御。 1. Claude 尝试进行 Bash 工具调用(例如 `npm install lodash@4.17.20`) 2. `PreToolUse` hook 在调用运行之前触发,并调用 `safer-dependencies-pretooluse-bash.sh` 3. 纯 bash 早期过滤器会在约 115 毫秒内对非包管理器命令进行 短路处理(不调用 Python),因此 `git status` / `ls` / `npm test` 在热路径上付出的 代价可以忽略不计 4. 对于已识别的包管理器安装(`npm`/`pnpm`/`yarn` `install`/`i`/`add`),助手通过 `shlex` 进行 token 化,提取每个 `pkg@version` 参数,并 POST 到 OSV 5. 任何易受攻击的具体固定版本 → hook 返回 `permissionDecision: "deny"` 以及每个发现的 GHSA-id + CVSS + 摘要,并提示调用 safer-dependencies skill 6. 安装永远不会运行 —— 没有网络获取,没有 postinstall 脚本 **除了拦截模式之外为何存在此模式:** 写入后的 shim 对 Bash 是盲目的。`npm install lodash@4.17.20` 会运行到结束(并且 postinstall 脚本会执行),然后才会触发任何审计;`npm install -g typosquat-pkg` 根本不写入项目 manifest。安装前模式在结构上 填补了这些空白。 安装前模式只能看到用户**输入**的内容(命令行上的 `pkg@version` 参数)。它看不到解析器实际会 安装的依赖树。**安装后模式**(如下)在安装完成后审计 lockfile —— 这两种模式是互补的,而不是冗余的。 **范围:** 此处涵盖的包管理器 CLI 跨越五个生态系统 (npm/pnpm/yarn/bun/npx/deno、pip/pip3/pipx/pipenv/uv/uvx/poetry、gem/bundle、 go、cargo),以及通过拦截模式覆盖的 Maven(Maven 依赖通常在 `pom.xml`/`build.gradle` 中声明,而不是通过 CLI 命令添加)。 识别的各生态系统语法: | PM | 命令 | 具体固定语法 | |---|---|---| | `npm`, `pnpm`, `yarn`, `bun` | `install`, `i`, `add`(加上 `yarn`/`pnpm dlx`, `bun x`, `yarn create`) | `pkg@1.2.3`, `@scope/pkg@1.2.3` | | `npx` | (无命令 —— 包是第一个位置参数) | `pkg@1.2.3` | | `deno` | `add`, `install` | `npm:pkg@1.2.3`(带 npm 前缀的规范) | | `pip`, `pip3`, `pipx`, `pipenv`, `uv`, `uvx`, `poetry` | `install` (pip/pip3/pipx/pipenv) / `add` (uv/poetry) / 无命令 (uvx) | `pkg==1.2.3`(也处理附加项 `pkg[extra]==X`) | | `gem`, `bundle` | `install` (gem) / `add` | `-v 1.2.3`, `--version 1.2.3`, `--version=1.2.3`(单独的标志) | | `go` | `get`, `install` | `pkg@v1.2.3`(根据 Go modules 必须包含 `v` 前缀) | | `cargo` | `add`, `install` | `crate@1.2.3` | 范围固定(npm `^4.17`、pip `>=`、poetry `^`/`~`、Go `@latest`)和未指定的版本会在安装后传递给拦截模式 — 写入后的 shim 会审计解析器选择的任何内容。自动重写为安全版本将作为后续任务排队。 **失败模式:** fail-open(失败放行)。任何错误(缺少 Python、网络波动、输入格式错误)都以状态码 0 退出且无输出,允许 bash 继续。拦截模式仍会在安装后运行,因此预检失败会优雅地降级到现有的保护机制。 **示例 deny:** ``` safer-dependencies pre-flight audit blocked this install. Vulnerable pinned version(s) detected: - lodash@4.17.20 → GHSA-35jh-r3h4-6jhm (CVSS:7.4): Command Injection in lodash Re-run with a patched version, or invoke the safer-dependencies skill for a recommended pin. ``` ### 安装后模式(Bash Hook) 使用 `PostToolUse:Bash` hook 配置 `.claude/settings.json`,以启用 在 Bash 命令执行后的后检审计。它会针对命令的 `cwd` 运行**三次独立的扫描**,每次扫描都能填补其他 hook 无法解决的空白: - **扫描 A —— lockfile。** 在成功的安装命令(`npm install`、 `bundle install`、`poetry install`、`uv sync`、`go mod tidy` 等)之后,审计 新修改的 lockfile(`package-lock.json`、`Gemfile.lock`、 `poetry.lock`、`uv.lock`、`go.sum`、`yarn.lock`、`pnpm-lock.yaml`、 `Pipfile.lock`)。这填补了安装前模式无法看到的**传递性 CVE 空白**: 用户输入了 `pkg@version`,但解析器可能引入了 数十个无人指定的传递性依赖。 - **扫描 B —— manifest。** 在任何*不*在只读 拒绝列表(`ls`、`cat`、`git status` 等)中的 Bash 命令之后,审计新修改的 manifest。这是通过 `sed -i`、`jq` 或 脚本进行的 manifest 编辑**唯一**的兜底方案——它们绕过了拦截模式所 hook 的 `Write`/`Edit` 工具。 - **扫描 C —— 已解析的环境。** 普通的 `pip install` / `pip install -r requirements.txt` 不会写入 lockfile,因此扫描 A 永远不会看到 已解析的依赖树。在 pip 形式的安装之后,扫描 C 会使用只读的 `list --format=json` 重新调用同一个 pip,并对完整的已解析环境(直接 + 传递性)进行 OSV 检查。 扫描如何运行: 1. Claude 运行 Bash 工具调用 2. `PostToolUse` hook 在命令完成后触发并调用 `safer-dependencies-posttooluse-bash.sh` 3. 纯 bash 早期过滤器会在约 115 毫秒内对不匹配任何扫描门控的命令进行短路处理(与安装前模式相同的快速路径约定因此 `ls` / `git` / `cat` 付出的代价可以忽略不计 4. 每次扫描使用 `find -maxdepth 5` 遍历 `cwd`(涵盖 monorepo 布局;排除 `node_modules`、`.git`、`.venv`、`venv`),查找在 过去 60 秒内修改过的文件——可通过 `SAFE_DEP_POSTINSTALL_MTIME_WINDOW` 覆盖 5. 对于每个新修改的文件(扫描 A/B),hook 会构造一个合成的 `PostToolUse:Write` 负载,并通过管道将其传递给现有的 shim —— shim 的 lockfile 和 manifest 审计器运行方式不变,没有重复的逻辑 6. 每个文件的信号被串联起来,并作为一个 `hookSpecificOutput` JSON 发送给父 agent **它能捕捉到而安装前模式无法捕捉到的是:** 传递性漏洞。一个看起来干净的 `bundle install` 可以将 `rack@2.2.23` (CVE-2025-27610) 作为 `sinatra` 的传递性依赖拉入——用户从未输入过 `rack`,因此安装前模式无法看到它,但安装后模式会读取已解析的 `Gemfile.lock` 并报告该 CVE。 **范围:** 扫描 A 不会重写已解析的版本——自动更正契约仅适用于 Claude 直接编写的 manifest。对于传递性 CVE,修复通常是“更新拥有该传递性依赖的直接依赖”,这需要人工判断。扫描 B *确实*会自动更正,因为它通过与拦截模式相同的 shim 路径审计 manifest。当 `transitive` 检查层级设置为 `off` 时(`config set checks.transitive off`),扫描 A 会跳过。 **失败模式:** fail-open(失败放行),与其他 hook 相同。任何错误(缺少 shim、负载格式错误、Python 不可用)都会以状态码 0 静默退出。 **示例 WARNING:** ``` WARNING: lodash@4.17.10 in lock file has GHSA-29mw-wpgm-hmr9, GHSA-35jh-r3h4-6jhm ``` ### Agent 后模式(Agent Hook 对) 上述四种模式仅针对**根会话**工具调用触发。当根会话调度子 agent 时(通过 `Agent` 工具——许多 skill 和 slash 命令在内部执行此操作),子 agent 的 Write/Edit/Bash 调用会绕过所有这些模式。Agent 后模式是针对该空白的反应性安全网。 1. `PreToolUse:Agent` hook(`safer-dependencies-pretooluse-agent.sh`)在每次 Agent 调度之前立即运行,并触及位于 `/tmp/.safer-deps-agent--.sentinel` 的哨兵文件(当没有可用的会话 ID 时,回退到仅包含 PPID 的名称) 2. 子 agent 运行并可能会写入 manifest 或 lockfile 3. `PostToolUse:Agent` hook(`safer-dependencies-posttooluse-agent.sh`)在 Agent 调用返回后运行,`find` 出比哨兵更新的每个 manifest 和 lockfile,并通过相同的 shim 路径审计每个文件 4. 发现结果将作为 `additionalContext` 呈现给根会话的下一轮对话;哨兵文件将被移除 嵌套的子 agent 会自动覆盖 —— 根的 `PostToolUse:Agent` 仅在外部 agent 的所有工作(包括*它*调度的任何内容)都写入磁盘之后触发。唯一的空白是不写入 manifest 或 lockfile 的全局安装(`npm install -g …`):没有东西可以扫描。像其他 hook 一样,它会 fail-open —— 任何错误(缺少哨兵、缺少 shim、负载无法读取)都会以状态码 0 静默退出。完整的设计原理位于 `skills/safer-dependencies.md` 中。 ## 触发条件 当 Claude 执行以下操作时,该 skill 会自动触发: **Manifest / 安装操作** - 在 `package.json`、`requirements.txt`、`Gemfile`、`pom.xml`、`build.gradle`、`Cargo.toml`、`go.mod` 或任何其他受支持的 manifest 中添加或更新包 - 为 manifest 中尚未声明的包写入 `import`、`require` 或 `use` - 生成或更新 lock 文件(仅检查新增/更改的条目) - 通过 Bash 运行包管理器安装(`npm install`、`bundle install`、`poetry install`、`uv sync`、`go mod tidy` 等)——安装前模式审计命令参数,安装后模式审计生成的 lockfile - 写入嵌入了固定包管理器安装步骤的 `Dockerfile` 或 CI 工作流(`.github/workflows/*.yml` 等) **选择与建议问题** - 库/框架比较:“我应该使用 axios 还是 node-fetch?”、“moment 还是 dayjs?”、“X 和 Y 哪个更好?” - 推荐请求:“Python 的好用 HTTP 客户端有哪些?”、“推荐一个用于 Go 的日志库”、“Node 中用什么包处理 CSV?” - 版本选择:“我应该用什么版本的 Django?”、“最新稳定的 Flask 版本?” **意图使用表达(添加前)** - “我想在这里使用 FastAPI”、“我正在考虑添加 Celery”、“我们正在考虑将 Prisma 作为 ORM”、“让我们使用 Tailwind” **包健康与信任问题** - “moment.js 还在维护吗?”、“这个 gem 还活跃吗?”、“X 废弃了吗?”、“X 终止支持 (EOL) 了吗?”、“我能信任这个包吗?”、“faker 最后一次更新是什么时候?” **脚手架命令** - `npx create-react-app`、`npm create vite@latest`、`django-admin startproject`、`rails new`、`cargo new` + `cargo add`、“引导一个新的 FastAPI 项目” **隐式包添加(暗示新依赖的功能请求)** - “给应用添加 Redis 缓存”、“连接到 Postgres”、“添加 JWT 认证”、“编写发送电子邮件的代码” —— 当 manifest 中尚不存在用于该功能的包时触发 **迁移与移植** - “从 requests 迁移到 httpx”、“从 CRA 移动到 Vite”、“从 moment 移植到 date-fns” —— 审计引入的包 它**不会**针对以下情况触发: - 标准库导入(`os`、`fs`、`java.util.*` 等) - 未被更改的已声明依赖 - 关于包内部工作原理的学术讨论(“解释 React 的 reconciler”、“webpack 的模块解析是如何工作的?”)——比较和选择问题仍然会触发 - 安装操作系统级应用、runtime 或 IDE 扩展(Python 本身、Docker、Homebrew、VS Code 扩展) ## 仓库内容 这是一个 **skill + hook 包**,而不是单个 skill 文件。完整的安装会部署以下组件: | 文件 | 角色 | |---|---| | `skills/safer-dependencies.md` | 该 **skill**(安装后为 `SKILL.md`)。描述审计程序并包含用于安装/统计的管理模式。 | | `skills/safer-dependencies-shim.sh` | `PostToolUse:Write`/`Edit` hook —— 审计 manifest + lockfile 写入,并就地自动更正易受攻击的版本(拦截模式)。 | | `skills/safer-dependencies-pretooluse-bash.sh` | `PreToolUse:Bash` hook —— 对包管理器安装命令进行预检 OSV 审计;在安装运行之前拒绝易受攻击的具体固定版本(安装前模式)。 | | `skills/safer-dependencies-posttooluse-bash.sh` | `PostToolUse:Bash` hook —— 在 Bash 命令执行后进行后检审计;捕捉新写入的 lockfile、通过 `sed`/`jq`/脚本编辑的 manifest 以及普通 `pip install` 的已解析环境中的传递性 CVE(安装后模式)。 | | `skills/safer-dependencies-pretooluse-agent.sh` + `skills/safer-dependencies-posttooluse-agent.sh` | `PreToolUse:Agent` + `PostToolUse:Agent` hook 对 —— 填补子 agent 覆盖空白。模式 2–4 仅针对根会话工具调用触发,因此子 agent 写入的任何 manifest 都会绕过它们。Agent 后模式在每次 Agent 工具调用返回后审计子 agent 写入的内容(Agent 后模式)。 | | `skills/scripts/` | 所有 hook 使用的共享 Python 库(`safedep/`)和独立的解析器脚本。 | | `skills/scripts/safer_dependencies_manager.py` | 用于交互式安装、使用统计和设置验证的管理模块。 | 仅有 skill 文件是不够的 —— 如果没有 hook,自动调用取决于 Claude 是否决定使用该 skill。安装所有五个组件以获得完整覆盖;许多 skill 和 slash 命令在内部调度子 agent,因此即使您从未显式生成子 agent,Agent 后模式对也很重要。(有关为什么 skill 本身无法保证覆盖范围的原因,请参阅 [FAQ.md](FAQ.md#why-a-skill-alone-is-not-sufficient)。) ## 支持的生态系统 | 生态系统 | Manifest | Lock 文件 | |-----------|----------|-----------| | npm | `package.json` | `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml` | | PyPI | `requirements.txt`, `pyproject.toml`, `Pipfile`, `setup.py`, `setup.cfg` | `Pipfile.lock`, `poetry.lock`, `uv.lock` | | RubyGems | `Gemfile`, `*.gemspec` | `Gemfile.lock` | | Maven | `pom.xml`, `build.gradle`, `libs.versions.toml` | -- | | Go | `go.mod` | `go.sum` | | Rust | `Cargo.toml` | `Cargo.lock` | | PHP (Composer) | `composer.json` | `composer.lock` | ## 安装说明 刚接触该项目?从 **[GETTING-STARTED.md](GETTING-STARTED.md)** 开始。简短版本: ``` git clone https://github.com/robert-auger/safer-dependencies /tmp/safer-dependencies python3 /tmp/safer-dependencies/skills/scripts/safer_dependencies_manager.py interactive_install ``` 安装程序会提示您选择范围(全局还是项目)以及要启用的 hook,然后为您写入 `settings.json` —— 包括 hook 条目**以及**允许 skill 的检查命令在每次审计时无需审批提示即可运行的权限白名单。 其他所有与安装相关的内容都位于 **[INSTALLATION.md](INSTALLATION.md)** 中,这是安装机制的唯一参考:手动逐文件安装(全局和项目级别)、Windows 特性、Agent 后 hook、[权限白名单](INSTALLATION.md#permissions-allowlist)、验证设置、更新、固定到发布标签以及卸载。 安装后,日常管理通过自然语言与 Claude 交互来工作 —— `install safer-dependencies`(重新运行/更改 hook)、`show safer-dependencies stats`、`check safer-dependencies setup` —— 或者 `/safer-dependencies` 菜单。更新也在会话中进行:`/safer-dependencies update` 应用最新版本(`update --check` 用于试运行,`update --rollback` 用于撤销);有关信任模型,请参阅 [INSTALLATION.md](INSTALLATION.md#in-session-self-updater-safer-dependencies-update)。 ### 配置 安装后有两项内容可以配置: - **权限白名单** —— 预先批准该 skill 的只读检查命令(`curl`、`npm view`、`pip-audit` 等),以便审计在每次运行时无需审批提示。交互式安装程序会为您写入核心条目;手动安装则通过手工添加完整的块。完整的块和原理:[INSTALLATION.md → 权限白名单](INSTALLATION.md#permissions-allowlist)。 - **安全策略** —— 发布时长的冷却窗口/模式,以及针对每种检查类型的逐检查 `off`/`warn`/`block` 层级,通过 `/safer-dependencies config` 编辑并存储在 `~/.config/safer-dependencies/config.toml` 中。Schema 和层级语义:[`skills/references/configuration.md`](skills/references/configuration.md)。 ## 警告级别 | 级别 | 含义 | 示例 | |-------|---------|---------| | CRITICAL | 停止并询问用户 | 检测到域名抢注,签名被篡改 | | HIGH | 警告并继续 | 已知 CVE,包发布 < 30 天 | | MEDIUM | 警告并继续 | 版本发布 < 7 天,缺少签名 | | LOW | 警告并继续 | 未签名的 Ruby gem(预期行为) | ## 审计日志 每次检查都会作为单行 JSON 记录到 `~/.claude/safer-dependencies-audit-YYYY-MM.log`(每个日历月一个文件,其中 `YYYY-MM` 是 UTC 的年月)。使用 `SAFE_DEP_AUDIT_LOG` 环境变量覆盖完整路径(设置时,不会追加日期后缀)。当文件超过 `SAFE_DEP_LOG_MAX_BYTES` 时,还会进行大小轮换(默认为 10 MiB;设置为 `0` 可禁用)。设置 `SAFE_DEP_MODEL` 以覆盖写入每个条目中 `source.model` 的模型值 —— 这对于模型版本之间的 A/B 比较非常有用。 所有五种模式都追加到同一个文件中。每个条目都带有一个 **`source` 块**(schema 2.2),用于标识是哪个组件写入了它: `source.component` | 写入者 | 触发器 | |---|---|---| | `shim.posttooluse` | `shim.sh` | Manifest 或 lockfile 写入(拦截模式,安装后调度) | | `shim.install_error` | `shim.sh` | Shim 预检安装失败 | | `bash.pretooluse` | `pretooluse-bash.sh` | Bash 安装命令(安装前模式) | | `bash.posttooluse` | `posttooluse-bash.sh` | 安装后 Bash hook 本身,在到达 shim 之前 fail-open 时 | | `agent.pretooluse` | `pretooluse-agent.sh` | 保留用于 Agent 前的 fail-open 事件(该 hook 本身在成功时目前是静默的) | | `agent.posttooluse` | `posttooluse-agent.sh` | Agent 后 hook 的 fail-open 事件(例如 shim 缺失、python_missing) | | `manual.skill` | 运行普通模式的 Claude | 内联调用的手动审计 | `source.model` 记录会话中处于活动状态的 Claude Code 模型(例如 `"claude-sonnet-4-6"`)。在 schema 2.1+ 中存在;由旧安装程序写入的条目省略了该字段。stats 命令在缺失时会优雅地降级为 `"unknown"`。 使用 `jq` 按 `source.component` 过滤: ``` jq -r '.source.component' audit.log | sort | uniq -c | sort -rn jq -c 'select(.source.component == "bash.pretooluse")' audit.log # 揭示所有 hook 中的静默 fail-open: jq -c 'select(.source.mode == "fail_open") | {component: .source.component, reason: .fail_open.reason, ts}' audit.log ``` 为了更容易进行分析,请让 Claude 提供使用统计数据,而不是手动解析日志: ``` "Show safer-dependencies stats for the last month" ``` 这提供了从这些审计日志中提取的活动、安全影响和性能指标的人类可读摘要。 **条目结构(schema 2.2)。** 三种不同的结构共享相同的 `ts` / `schema` / `source` 标头: | 结构 | 何时写入 | 区分字段 | |---|---|---| | **审计条目** | Manifest / lockfile / bash 安装审计 | `file`, `ecosystem`, `checked`, `findings`, `abandoned`, `stale`, `typosquat`, `unknown`, `signatures`, `notes`, `clean` | | **安装错误条目** | Shim 预检安装错误(组件 `shim.install_error`) | `install_error`, `shim_dir`, `scripts_dir` | | **Fail-open 条目** | 任何 hook 入口点由于 `helper_missing` / `shim_missing` / `python_missing` 而提前退出。`source.mode` 为 `"fail_open"` | `fail_open: { reason, detail? }` | 审计条目:拦截模式运行完整的管道(来源、版本时长、OSV、废弃/过时、域名抢注、签名),因此所有数组都可以填充。安装前模式目前仅运行 OSV,因此 `abandoned` / `stale` / `typosquat` / `signatures` 始终为空。安装后调度(lockfile 审计)在 `shim.posttooluse` 下写入,`findings` 由来自 lockfile 审计器的 `WARNING:` 字符串填充。`notes` 数组携带信息性的 `NOTE:` 信号(例如 manifest-skipped-because-unpinned)。 Schema 2.2 以累加方式向 **lockfile** 审计条目添加了四个字段:`lockfile`、`manifest_ref`、`relation_summary`(针对同级 manifest 对每个标记包进行的直接/传递/未知分类),以及记录生效的 `transitive` 层级的 `policy` 块。该版本升级是向后兼容的:读取 2.1 条件的读取器可以容忍新字段,并且 `source.model` 字段从 2.1 起保持存在。 ``` { "ts": "2026-04-19T12:34:56Z", "schema": "2.2", "source": { "component": "shim.posttooluse", "script": "shim.sh", "hook": "PostToolUse:Write", "tool": "Write", "mode": "intercept", "model": "claude-sonnet-4-6" }, "file": "/path/to/project/package.json", "ecosystem": "npm", "checked": ["express@4.18.2", "lodash@4.17.21"], "findings": ["UPDATED: express 4.18.2 → 4.22.1 (HIGH: 1 CVE fixed)"], "abandoned": [], "stale": [], "typosquat": [], "unknown": [], "signatures": [], "notes": [], "clean": ["lodash@4.17.21"] } ``` 安装前模式示例(Bash hook,拒绝易受攻击的固定版本): ``` { "ts": "2026-04-23T06:56:21Z", "schema": "2.2", "source": { "component": "bash.pretooluse", "script": "pretooluse-bash.sh", "hook": "PreToolUse:Bash", "tool": "Bash", "mode": "intercept", "model": "claude-sonnet-4-6" }, "file": "bash:npm install lodash@4.17.20 ms@2.1.3", "ecosystem": "npm", "checked": ["lodash@4.17.20", "ms@2.1.3"], "findings": [ "BLOCKED: lodash@4.17.20 GHSA-35jh-r3h4-6jhm (CVSS:3.1/...): Command Injection in lodash" ], "abandoned": [], "stale": [], "typosquat": [], "unknown": [], "signatures": [], "notes": [], "clean": ["ms@2.1.3"] } ``` Fail-open 模式示例(调用安装后 Bash hook 时附近没有 shim —— 安装损坏): ``` { "ts": "2026-05-03T07:14:11Z", "schema": "2.2", "source": { "component": "bash.posttooluse", "script": "safer-dependencies-posttooluse-bash.sh", "hook": "PostToolUse", "tool": "Bash", "mode": "fail_open", "model": "claude-sonnet-4-6" }, "fail_open": { "reason": "shim_missing", "detail": "/home/alice/.claude/skills/safer-dependencies" } } ``` Fail-open 条目表示:“此 hook 已触发,但由于缺少某些先决条件而在未进行审计的情况下提前退出。” 使用上面的 jq 过滤器(`select(.source.mode == "fail_open")`)以显示日志中每一个静默的保护丢失事件。 当 shim 以试运行模式(`SAFE_DEP_DRY_RUN=1`)运行时,条目还会包含 `"mode": "dry_run"`,以便事后分析可以过滤出仅审计的调用。 ## 环境要求 - Python 3.9+(hook 会对此进行探测,并在遇到较旧的解释器时 fail-open) - `curl`(用于注册表 API 调用和 OSV 漏洞检查) - 生态系统工具(可选,如果缺失,skill 会回退到 OSV API): - 用于 npm 包的 `npm` - 用于 Python 包的 `pip-audit` - 用于 Ruby 包的 `bundle` - 用于 Java 包的 `dependency-check` ## 常见问题 设计决策的原理(为什么使用 `PostToolUse` 而不是 `PreToolUse`、为什么不验证签名、为什么脚本和 shim 重复、skill 加载的陷阱等)记录在 [`FAQ.md`](./FAQ.md) 中。
标签:AI编程助手, Claude Code, 依赖安全, 应用安全, 逆向工具