mcpField
S
safeinstall

safeinstall

本地服务
github.com/Mickdownunder安全34 周前更新

关于 safeinstall

SafeInstall 是面向 npm 和 pnpm 的本地优先安装策略门禁,适用于安装包的开发者和 AI Agent。它会在安装前检查策略与制品绑定,也可监控 Agent 配置文件。

主要功能

  1. 01在 npm 或 pnpm 安装前检查策略
  2. 02验证锁文件版本与制品哈希
  3. 03拦截发布过新及生命周期脚本风险
  4. 04可选的拼写仿冒和来源检查
  5. 05防护 Agent 安装命令
  6. 06跟踪 Agent 配置变更

适用场景

  1. 01管控 AI Agent 发起的软件包安装
  2. 02按本地策略审查直接依赖
  3. 03核对软件包来源是否符合可信发布者
  4. 04检测已提交 Agent 配置的偏移

MCP 相关

提供供 Agent 查询 SafeInstall 策略的 MCP 工具。还提供 shell 防护,可拦截 Claude Code、Codex 和 Cursor 中的安装命令。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"mickdownunder-safeinstall": {
"command": "npx",
"args": [
"-y",
"safeinstall-cli"
]
}
}
}
启动命令npx -y safeinstall-cli

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmsafeinstall-cli0.12.0

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

SafeInstall
面向 AI Agent 时代的供应链安全。

npm 版本 许可证 星标 TypeScript 文档 信任面

面向输入 install 的用户、替他们运行命令的 AI Agent,以及用于配置这些 Agent 的配置文件,为 npm 和 pnpm 提供与制品绑定的安装闸门。
先运行策略,再运行包管理器。顺序不能颠倒。

本地优先 · 无云端 · 无需账号 · MIT · 永久免费


功能介绍

SafeInstall 先于包管理器运行策略——在本地执行,默认阻止操作。一个工具,三层防御:

  • 🧑‍💻 面向安装软件包的用户 — 为受支持的安装命令添加前缀:safeinstall pnpm add axios。先运行策略;然后在冻结安装前,根据已批准的版本和 SHA-512 检查解析后的锁文件。发布时间、安装脚本、不可信来源、仿冒包名和加密来源证明均为策略检查。
  • 🤖 面向替你安装软件包的 AI Agent — Agent 可调用的 MCP 工具,以及一个命令行防护程序,它会在 Claude Code、Codex 和 Cursor 中的安装命令运行之前拦截这些命令。Shell 拦截尽力而为——它是一层即使 Agent 不配合也会触发的防御,并非万无一失的保证。
  • 🔒 面向用于配置 Agent 的文件 — Agent Trust Surface:对配置、钩子、规则和 MCP 文件建立已提交的哈希基线,在本地协调并在 CI 中重新验证。这样,规则被篡改时会显示为偏移,而不是在之后的每次会话中悄无声息地取得控制权——但如果篡改后重写内容完全一致,仍需人工审核差异才能发现,自动检查本身无法发现。

为什么选择 SafeInstall

AI 编码工具几秒内就能推荐软件包。它们不会检查发布时间,不会阅读安装脚本,也不会验证来源。你输入“是”,然后继续。

SafeInstall 是建议与执行之间的闸门。

$ safeinstall pnpm add [email protected]

Install blocked.
- [email protected]
  Blocked: release too new (published 3 hours ago; minimum is 72 hours).
  Blocked: install script present (has postinstall).

无需仪表板,无需账号,也无需云端。只需在命令前加一个前缀——策略在本地运行,默认阻止操作,然后调用实际工具。


防范维护者账号被攻陷的攻击

有效的 Sigstore 签名并不足够。攻击者若攻陷 npm 维护者账号,就能发布你已信任的软件包的恶意版本,而该恶意版本上的证明也会通过密码学验证——签名来自攻击者在真实代码库的分叉中控制的 GitHub Actions 工作流。

SafeInstall 可以防范这种情况。使用 provenance.trustedPublishers 固定预期的源代码库,任何来自其他位置的构建都会被拦截,即使签名有效:

$ safeinstall check
Using config: ./safeinstall.config.json
Check blocked.
- [email protected]
  Blocked: publisher mismatch for axios (expected axios/axios, got evil-org/axios).
  Suggestion: Verify the package source. Update provenance.trustedPublishers only if the change is intentional.

这是安装时策略门禁中唯一此类检查。CVE 扫描器用于查找已知漏洞,内容分析器用于查找可疑代码。SafeInstall 会确保密码学信任链指向你同意信任的代码库,并拒绝其他一切内容,无论它看起来多么可信。

SafeInstall 自身通过 Sigstore 证明发布。你可以亲自实践:启用来源验证,将 safeinstall-cli 固定到 Mickdownunder/SafeInstall,然后观察 SafeInstall 如何根据公开的 Sigstore 透明度日志验证自身的信任链。

$ safeinstall check
Using config: ./safeinstall.config.json
Info: safeinstall-cli: provenance verified from Mickdownunder/SafeInstall via .github/workflows/release.yml.
Check passed: no direct dependency policy violations found.

安装

npm install -g safeinstall-cli

Node.js >=20 · macOS、Linux、Windows · 命令:safeinstall

快速入门

safeinstall init                      # config + agent guard hooks + trust lock, in one command
safeinstall pnpm add axios            # policy runs, then pnpm
safeinstall npm install               # lockfile-aware project install
safeinstall check                     # audit direct deps against policy

工作原理

Resolve → Policy → Lockfile-only preparation → Verify artifact binding → Frozen install
                      Any failed policy or binding check → Exit 2 (blocked)
  1. 解析请求的版本,并记录注册表中的 tarball URL 和 SHA-512
  2. 获取注册表元数据(发布时间、声明的脚本)
  3. 评估策略规则
  4. 对于显式添加的软件包,固定已评估的版本,并运行禁用脚本、仅处理锁文件的准备步骤
  5. 检查受影响锁文件范围内的每个直接注册表依赖项,核对其已评估版本、SHA-512,以及有记录时的 tarball URL;应用已配置的传递依赖项检查
  6. 在禁用脚本并强制使用已配置注册表的情况下运行 npm ci 或 pnpm install --frozen-lockfile;若评估或安装期间清单/锁文件发生变化,则拒绝操作

没有注册表代理。不会重写 tarball。无云端依赖。


默认策略

规则默认值拦截消息
发行版本等待时间至少 72 小时Blocked: release too new
生命周期脚本禁止 preinstall、install、postinstallBlocked: install script present
源类型允许 registry、workspace、file、directoryBlocked: untrusted source
信任级别降级更新时检测 registry→git/url 或新增脚本Blocked: trust level dropped
拼写仿冒检测默认关闭;通过 typoSquat.mode 启用Blocked: suspected typo-squat
来源验证默认关闭;通过 provenance.mode 启用Blocked: attestation missing/invalid/publisher mismatch
传递依赖默认关闭;通过 transitive.mode 启用Blocked: transitive install script / untrusted source
来源连续性默认关闭;通过 continuity.mode 启用Blocked: provenance downgrade / identity discontinuity

所有规则均可配置。元数据含糊不清或不完整时,会拦截而非放行。

来源连续性——捕捉 npm 默认机制无法发现的问题

这是 SafeInstall 最具特色的检查,也是其他面向使用者的工具都不具备的能力。npm 会在发布时验证来源证明,并将软件包绑定到源代码仓库,但不会强制要求不同版本之间保持连续性。遭到入侵的维护者账户可以发布一个没有来源证明的新版本(使用被盗令牌发布),也可以从不同的仓库发布,而 npm 不会发出任何警报。这正是 2026 年攻击浪潮(Mastra、休眠账户重新发布)的典型特征。

连续性检查会根据近期版本的来源证明身份,为每个软件包建立信任基线,然后拦截偏离基线的情况:

  • provenance-downgrade — 近期版本都有来源证明,但当前版本没有。这是使用个人令牌发布、账户遭入侵的典型信号。(Mastra 案例即属此类。)
  • identity-discontinuity — 当前版本的来源证明来自与既定基线不同的源代码仓库。

由于基线是按软件包建立的,因此绝大多数从未采用来源证明的软件包不会产生误报——它们没有基线,检查就会保持静默。无需采用全局性的“强制要求来源证明”硬性措施。

该功能会读取 npm 发布的来源证明元数据,因此无需安装可选的 sigstore 软件包即可运行。将 continuity.mode 设为 "warn" 或 "block" 即可启用。

如实说明限制: 如果攻击是通过合法但已遭入侵的 CI 工作流实施,且来源证明有效并来自真实仓库(例如 Shai-Hulud 蠕虫这一类攻击),连续性检查无法发现。因为这里不存在可检测的身份不连续。SafeInstall 提高了抵御 2026 年主要攻击模式的门槛,但无法堵住所有漏洞。

传递依赖

默认情况下,SafeInstall 会检查直接依赖。不过,大多数供应链攻击都是通过传递依赖入侵你的项目——也就是你从未主动选择、由其他依赖在多层嵌套中引入的软件包。启用 transitive 模式即可遍历完整的锁文件依赖树。

有两项检查会沿传递依赖执行,且都直接读取锁文件,不会额外调用注册表:

  • install-script — 标记声明了生命周期脚本的传递依赖(即 ua-parser-js 这类攻击:深层嵌套的依赖在安装时运行代码)。npm 会在锁文件中记录此信息;pnpm 锁文件不会,因此目前此检查仅适用于 npm。
  • untrusted-source — 标记从 git、url 或 tarball 来源解析,而非从注册表解析的传递依赖。适用于 npm 和 pnpm。

为避免产生大量噪声或需要为每个软件包往返查询注册表,发布时效、拼写仿冒和来源证明检查不会沿传递依赖执行。传递依赖检查适用于 safeinstall check、项目安装,以及显式添加 npm/pnpm 依赖时准备的锁文件。

{
  "transitive": {
    "mode": "warn",
    "checks": ["install-script", "untrusted-source"]
  }
}
拼写仿冒检测

标记与知名热门软件包名称近似但不完全相同的安装请求。这是最常见的供应链攻击模式:用 lodsh 替代 lodash、用 axois 替代 axios、用 raect 替代 react,等等。

  • 算法: Damerau-Levenshtein 距离(相邻字符换位按一次编辑计算)
  • 目标列表: 精选的热门生态系统软件包列表,构建时嵌入(运行时不从网络获取)
  • 默认模式: "off"——通过 typoSquat: { "mode": "warn" } 或 "block" 启用
  • 误报缓解: 与列表完全匹配的名称永远不会被标记;跳过短名称(少于 4 个字符);支持按项目配置 ignore 列表
来源验证

通过密码学验证 registry 安装的 npm 来源证明,并可选择通过每个包的可信发布者模式固定源代码仓库。

  • 从 npm registry 的 /-/npm/v1/attestations/<pkg>@<version> 端点获取证明包
  • 通过官方 sigstore 包验证 Sigstore 包(签名、Rekor 透明日志、Sigstore 公共信任根)
  • 从 SLSA v1 来源证明声明中提取源代码仓库、提交引用和工作流路径
  • 将源代码仓库标识与每个包的 trustedPublishers 模式进行匹配——不仅能发现被篡改的 tarball,还能防范维护者遭入侵攻击:攻击者从其控制的 fork 重新发布一个看似合法的包
  • 发布者不匹配时始终会阻止操作,即使处于 "warn" 模式也不例外,因为来自错误仓库的有效签名正是维护者遭入侵的典型表现

默认模式为 "off"。将 provenance.mode 设为 "warn" 或 "require" 即可启用。


用法

# Ad-hoc installs
safeinstall pnpm add axios
safeinstall npm install [email protected]

# Project installs (lockfile-aware for npm/pnpm)
safeinstall pnpm install
safeinstall npm ci

# Monorepo — select a package; shared-lockfile installs review all importers
safeinstall pnpm -C packages/app install
safeinstall npm --prefix packages/app ci

# Utilities
safeinstall check                     # direct dependency audit
safeinstall check --json              # machine-readable
safeinstall init                      # one-command onboarding: config, guard hooks for
                                      # detected agents (.claude/, .codex/, .cursor/), trust lock
safeinstall init --force              # overwrite an existing config (hooks/lock stay idempotent)
safeinstall init --client codex       # force specific agents instead of detection
safeinstall init --no-guard --no-lock # config only (the old behavior)
safeinstall mcp                       # MCP server for AI coding agents
safeinstall guard install             # register agent shell hooks (Claude Code, Codex, Cursor)
safeinstall trust lock                # baseline the Agent Trust Surface
safeinstall trust lock --ci github    # ...and scaffold the CI re-verification workflow
safeinstall trust status              # reconcile the trust surface (exit 2 on drift; read-only)
safeinstall trust approve             # review drift and re-baseline (interactive)
safeinstall trust unlock              # remove the baseline (lock, ledger, head mirror)
safeinstall --help
safeinstall --version

# JSON output (CI/automation)
safeinstall --json pnpm add axios

# Explicit config file (skips upward discovery)
safeinstall --config ./ci/safeinstall.config.json check

项目安装

对于 pnpm install 和 npm install / npm ci,依赖版本取自 lockfile,而不是 package.json 中的宽泛版本范围。

  • pnpm 使用 pnpm-lock.yaml
  • npm 使用 package-lock.json 或 npm-shrinkwrap.json
  • lockfile 条目过时、缺失或不匹配时,会默认拒绝
  • 如果 package.json 中设置了 packageManager,则会阻止使用不同的 CLI
  • 会阻止指定工作区的标志(--filter、--workspace)——请使用 -C 或 --prefix
  • 在实现与制品绑定的 Bun 安装前,bun add / bun install 会默认拒绝;check 和 MCP 策略查询仍可用
与制品绑定的安装约定

安装要求已检查的 registry 清单中包含一个规范的 SHA-512 摘要,且 lockfile 中有匹配的条目。仅包含 SHA-1 或缺少完整性信息的条目会被阻止。使用不同的命令行 registry、启用脚本的标志、全局安装以及未知安装选项都会被阻止。环境变量或作用域 .npmrc 设置中的 registry 选择无法授权使用其他制品:最终 lockfile 仍必须匹配已检查的绑定。

显式添加的依赖会先解析为包含经过评估的精确版本的 lockfile,然后再进行安装。即使后续绑定/策略检查阻止了操作,准备阶段仍可能更新 package.json 和 lockfile,并填充包管理器的缓存。这些文件会保留供检查,不会自动回滚。npm 的冻结阶段使用 ci,会替换 node_modules。共享工作区 lockfile 会使系统根据所选策略审查其中记录的所有导入方的直接依赖,而不仅是所选包。

始终禁用生命周期脚本,即使 packageManagerDefaults.*.ignoreScripts 为 false 也不例外。allowedScripts 仅允许策略例外,不会执行脚本。pnpm 的可执行 .pnpmfile.cjs 钩子也会被禁用。当前会阻止显式添加非 registry 依赖;现有本地/工作区依赖仍受 allowedSources 管理,但不声称这些依赖已与 registry 制品绑定。


MCP 服务 / AI Agent

CLI 面向输入 safeinstall 的用户。MCP 服务面向 AI 编码 Agent——Claude Code、Codex、Cursor、Windsurf、Cline——因此,策略引擎可以在 Agent 建议或运行安装命令之前接受查询,无需任何人输入内容。

safeinstall mcp 会通过标准输入/输出启动一个 Model Context Protocol 服务,并提供一个工具 check_package。该工具由与 CLI 完全相同的引擎提供支持(版本发布时间、安装脚本、不可信来源、仿冒包、Sigstore 来源证明和来源证明连续性)。

重要

MCP 工具仅供参考。安装服务后,check_package 会变为可用,但不会强制 Agent 调用它。“安装一次,永久保护” = 一段 MCP 配置 + 一段规则片段(mcp/agent-rule.md),告知 Agent 在每次安装前调用该工具。

将其接入你的 Agent

Claude Code / Claude Desktop — 添加到你的 MCP 配置(claude_desktop_config.json 或 .mcp.json)中:

{
  "mcpServers": {
    "safeinstall": { "command": "npx", "args": ["safeinstall-cli", "mcp"] }
  }
}

Cursor — 将相同的配置块添加到 .cursor/mcp.json。

然后将 mcp/agent-rule.md 中的规则粘贴到你的 CLAUDE.md / Cursor Rules 中。完整设置详情:mcp/README.md。

Agent 会看到什么

check_package 接受 name(必填)、version(可选,默认为最新版本)和 manager(可选,仅供参考),并返回 JSON 判定结果:

{
  "verdict": "block",
  "name": "raect",
  "version": null,
  "reasons": [
    { "code": "typo-squat-suspected", "message": "Blocked: Suspected typo-squat: \"raect\" is 2 edit(s) away from popular package \"react\".", "suggestion": "Verify you meant to install \"react\"." }
  ],
  "warnings": [],
  "infos": [],
  "sourceRepository": null,
  "ageHours": null
}

当任何策略原因阻止安装时,verdict 为 "block";否则为 "allow"。

未找到 safeinstall.config.json 时,MCP 服务会使用推荐的安全预设——内置默认值,并将 typoSquat 和 continuity 提升为 "block",因为 Agent 使用场景需要最大程度地提供风险信号。找到配置文件时,则会严格遵循该文件(与 CLI 使用相同的解析方式)。

MCP SDK 是可选依赖项,仅在运行 safeinstall mcp 时才会延迟加载——仅使用 CLI 的用户无需安装它。如果缺少该依赖项,命令会显示安装提示并以非零状态码退出。


Agent 防护——强制执行,而非提供建议

MCP 工具仅供参考:Agent 可以查询它。防护层级更高:它会接入 Agent 的 shell 层,在命令运行之前进行筛查,即使 Agent 不知道 SafeInstall 的存在,也会触发。这是尽力而为的 shell 拦截——是纵深防御的一层,并非完整的安全边界;其命令解析并不全面(参见防护限制)。

safeinstall guard install                  # Claude Code, Codex, and Cursor
safeinstall guard install --client codex   # or just one client

这会写入项目级钩子配置(以非破坏性方式合并,重复运行时保持幂等):

  • Claude Code — 在 .claude/settings.json 中为 Bash 工具配置 PreToolUse 钩子;原始安装命令会通过 SafeInstall CLI 使用 updatedInput 原地重写,且不设置 permissionDecision,因此 Claude 仍会显示常规权限提示,并向用户展示重写后的命令以供批准
  • Codex — 在 .codex/hooks.json 中为 Bash 配置 PreToolUse 钩子;原始安装命令会通过 Codex updatedInput 使用 SafeInstall CLI 原地重写。安装后,在 Codex 中打开 /hooks 并信任新的项目钩子,然后它才会运行。
  • Cursor — 在 .cursor/hooks.json 中配置 beforeShellExecution 钩子,并以 failClosed: true 注册,因此防护程序崩溃或超时时会阻止操作,而不是悄然放行

注册后,CLI 会报告与项目状态相符的 Trust Surface 步骤。没有基线的项目会被引导运行 safeinstall trust lock。如果基线已存在且防护程序对其进行了更改,请使用 safeinstall trust status 查看完整差异,并且仅在确认所有更改都是有意为之时运行 safeinstall trust approve。重复运行已注册的防护程序无需重新批准,除非状态报告存在差异。

防护程序的决策方式

防护程序本身不会评估策略——它会检测软件包安装操作,并将其转交给负责决策的 CLI:

Agent 执行的命令防护程序的响应
git status、npm test,……允许——不是安装操作
npm install axios(也包括 npm i、pnpm add、bun a、npm ci、corepack pnpm add、pnpm --dir app add,……)Claude/Codex:执行前就地改写为 safeinstall npm install axios(权限提示中会显示转交后的命令)。Cursor:使用 SafeInstall 替代命令拒绝
cd app && npm i axios && npm test仅改写安装命令片段前缀;Claude/Codex 会直接应用改写,Cursor 会将其返回给 Agent
safeinstall npm install axios允许——已转交给策略引擎处理
npm install $(cat list.txt)、bash -c "npm install ..."拒绝——无法安全分析(默认拒绝)
yarn add axios拒绝——SafeInstall 无法对 yarn 进行策略检查
已在本地安装 typescript 时执行 npx tsc允许——npx 会解析本地二进制文件,不会下载任何内容
npx create-next-app、pnpm dlx ...、bunx ...、yarn dlx ...Claude/Cursor:询问。Codex:拒绝,因为 Codex PreToolUse 目前不支持 ask 决策

将操作转交给 CLI,比简单地允许或拒绝更重要:即使未经 SafeInstall 处理的 npm install 已通过审查,仍会执行生命周期脚本。通过 SafeInstall 执行时,同一安装操作会经过完整的策略评估,并且会在禁用安装脚本的情况下运行。

Claude 和 Codex 通过 updatedInput 就地应用改写,因此用户看到的常规权限提示会显示转交给 SafeInstall 的命令,而不是原始安装命令。Cursor 会在拦截消息中返回完整的改写后命令,以便行为良好的 Agent 一步完成自我修正。防护程序无需访问网络,响应时间仅为几毫秒。

防护程序的局限性
  • safeinstall 二进制文件必须位于 Agent 的 PATH 中(npm install -g safeinstall-cli)。
  • Codex 要求用户通过 /hooks 审查并信任新的或已更改的项目钩子;在此之前,Codex 会跳过该钩子。Codex 目前只会拦截受支持的简单 Shell 调用,而非每条 unified_exec 路径,因此该钩子仍是纵深防御措施,而不是完整的安全边界。
  • npx 及类似工具不会经过策略检查:SafeInstall 引擎评估的是安装操作,而不是一次性执行。Claude/Cursor 会请求批准;Codex 则默认拒绝,因为其 PreToolUse 钩子目前不支持 ask。本地二进制文件的快速路径与运行器自身的解析方式一致(最近的 node_modules/.bin),因此经批准的项目工具不会触发远程运行器门控。
  • 有意绕过检查的攻击者可以避开 Shell 层分析——安装操作可以通过其他方式运行(例如先写入脚本文件、使用 node -e 等),而且命令行解析本身也无法穷尽所有情况。不要只依赖 Shell 层:它作为纵深防御的一层提高了攻击门槛——应结合 MCP 工具和 CI 检查使用。

Agent 信任表面——自我防御策略

防护机制会阻止直接安装。但遭到提示词注入的 Agent 不必对抗防护机制——它可以直接改写规则:削弱 safeinstall.config.json、删除 Agent 钩子、在 .codex/config.toml 中禁用 Codex 钩子、在 AGENTS.md 中植入不可见 Unicode 指令,或注册恶意 MCP 服务。这些文件会为下一次 Agent 会话编写程序;在 Agent 时代,它们就是持久化机制,是新的 crontab。

safeinstall trust lock 会记录该信任表面的哈希基准。随后,SafeInstall 会在做出防护决策之前以及每次安装/检查之前,将实际状态与基准进行核对,并可在 CI 中重新验证。

safeinstall guard install       # register the hooks
safeinstall trust lock --ci github  # baseline the surface AND scaffold CI re-verification
safeinstall trust status        # reconcile — exit 2 on drift (use in CI); read-only
safeinstall trust approve       # review drift and re-baseline (interactive only)
safeinstall trust unlock        # remove the baseline (lock, ledger, head mirror)

--ci github 会写入 .github/workflows/safeinstall-trust.yml,在每个拉取请求上通过运行 safeinstall trust status --require-lock 重新验证已提交的基准;该工作流会将 CLI 固定到一个精确版本,并且固定到其 sha512 内容哈希,该哈希在生成脚手架时从注册表记录(首次使用时建立信任)——如果注册表之后为该版本提供了不同的字节内容,检查就会失败,而不会静默替换验证器。该工作流使用 pull_request_target,因此 GitHub 会从受保护的基分支加载其定义;它会检出提议的修订版本,但不使用凭据,将其仅视为数据,且绝不执行 PR 代码。请一并提交 .safeinstall/ 和该工作流。已有的工作流文件绝不会被覆盖。因此,PR 无法削弱负责审查它的验证器——但如果策略和基准被完全一致地改写,仍需人工审查差异(见下方的 CODEOWNERS 说明)。

要让检查真正发挥约束作用,你必须完成以下两项设置,因为 CLI 无法替你设置:

  • 在分支保护中将此工作流设为必需状态检查——否则,失败的检查不会阻止合并。
  • 要求审查对 .safeinstall/ 和 .github/workflows/ 的更改(例如通过 CODEOWNERS)。自动检查会捕获不一致的篡改;如果在一个 PR 中完全一致地改写了整个基准,则只能通过人工审查 .safeinstall/ 的差异来发现。
三个区域
区域文件发生偏移时
强制执行safeinstall.config.json、.claude/settings.json、.codex/hooks.json、.codex/config.toml、.cursor/hooks.json、.github/workflows/safeinstall-trust.yml、.safeinstall/拒绝每条 Agent 命令,直到获得人工批准
指令AGENTS.md、CLAUDE.md、.cursorrules、.cursor/rules/**、.github/copilot-instructions.md始终阻止隐藏 Unicode;内容偏移会发出警告(在 --mode strict 下会阻止)
工具.mcp.json、.cursor/mcp.json、.claude/settings.json 中的 MCP 区块拒绝安装和运行器,直到获得人工批准;标记未固定版本的 MCP 服务

指令内容偏移默认只会发出警告,因为 Claude Code 会自行写入 CLAUDE.md(其记忆功能)——阻止这种行为会破坏正常工作流。另一方面,这些文件中的隐藏 Unicode 没有任何正当用途,因此始终会被阻止。

它如何保护自身
  • 验证器来自受保护的基础分支;有意重新建立基线仍由人工审核把关。 运行 safeinstall trust lock --ci github,即可搭建一个工作流:在每个拉取请求中,由 Agent 无法控制的机器使用精确版本及其 sha512 内容哈希固定的 CLI,将候选信任面与已提交的锁文件重新哈希比对。pull_request_target 会使验证器定义不受 PR 控制,而候选检出内容则是不会执行、也不含凭据的数据。这能阻止 PR 修改工作流来豁免自身的失败。需要坦诚说明的边界:PR 仍可一致地重写策略和基线,因此这些差异必须经过独立审核。(直接接入 Action 也可行,但要固定其 version: 输入;默认值 latest 会浮动。)
  • 在拦截之上进行对账。 防护钩子是快速防护层;每次 safeinstall 安装或检查也都会与基线对账。删除钩子来让防护静默,或编辑受保护文件,都会在下次本地运行 SafeInstall 时显示为偏移;无论如何,都会导致 CI 重新验证失败。
  • 变更须经人工批准。 放宽强制措施的两条命令都需要人工操作:safeinstall trust approve 会从控制终端(/dev/tty)读取确认,而不会从 stdin 读取;trust unlock 同样需要控制终端。两者在 CI 或已知的 Agent 钩子上下文中都会拒绝运行,因此 Agent 无法悄悄重设基线或移除信任面。trust status 为只读(可在 CI 中安全运行,绝不会修改仓库)。
  • 哈希链式账本。 基线决策会链接记录在 .safeinstall/ledger.jsonl 中;锁文件绑定到一条账本记录(包括其强制执行 mode),因此,编辑已提交的锁文件以删除文件或将 strict 降级为 warn 会破坏这种绑定。~/.safeinstall/ 下的本地头部镜像可捕捉简单或意外的历史改写;如果镜像消失,下次无误运行时会根据已验证的头部自动修复,因此新克隆会自动建立镜像,不产生额外提示。
信任面的局限——请仔细了解
  • 最强的防护层是经过 PR 审核的已提交锁文件,而不是本地镜像。 在本地,这套机制只能防止可察觉的错误和非定向篡改,无法防止篡改。 在你自己的用户账户下运行的、了解该机制的 Agent 可以绕过拦截(例如通过 node -e 驱动安装),将仓库内的锁文件和账本重写为相互一致的状态,并删除本地头部镜像——用户空间无法阻止这种行为,因为 Agent 可以读取并删除你能读取和删除的任何内容。CI 重新验证会在另一台机器上运行,并由基础分支拥有的工作流驱动,因此 PR 无法禁用自己的验证器。但策略和基线被一致重写后,仍可能自动通过。对于有能力的对手,剩余的保障措施是在每个依赖项 PR 中,对信任面差异(工作流、锁文件和配置)进行独立人工审核;待有第二位可信审核者后,通过规则集强制执行。请运行 CI 检查并要求进行此类审核;两者涵盖不同的故障模式。
  • 它监视的是 Shell 命令和文件,而非意图:如果 Agent 编写并运行某个程序来执行安装,该操作会在下次 SafeInstall/CI 运行时通过对账被发现,而不是在调用发生时被阻止。
  • 本地头部镜像缺失的原因并不明确(新克隆也没有镜像),因此干净验证时会根据已提交的账本头部自动修复,而不会发出警告或阻止操作——镜像只是尽力提供的本地便利,绝非保障。

配置

可选的 safeinstall.config.json——从项目目录开始向上查找,或通过 --config <path> 显式指定。无法读取显式指定的路径时会报错(退出码为 1),绝不会静默回退到默认值。

{
  "minimumCliVersion": "0.12.0",
  "minimumReleaseAgeHours": 72,
  "registryUrl": "https://registry.npmjs.org",
  "allowedScripts": {
    "esbuild": ["postinstall"]
  },
  "allowedSources": ["registry", "workspace", "file", "directory"],
  "allowedPackages": [],
  "packageManagerDefaults": {
    "npm": { "ignoreScripts": true },
    "pnpm": { "ignoreScripts": true },
    "bun": { "ignoreScripts": true }
  },
  "typoSquat": {
    "mode": "warn",
    "minNameLength": 4,
    "ignore": []
  },
  "provenance": {
    "mode": "warn",
    "requireFor": [],
    "trustedPublishers": {
      "axios": "axios/axios"
    },
    "offlineBehavior": "fail-closed",
    "toolingUnavailable": "warn"
  },
  "transitive": {
    "mode": "warn",
    "checks": ["install-script", "untrusted-source"]
  },
  "continuity": {
    "mode": "warn",
    "baselineSize": 5
  }
}
字段用途
minimumCliVersion可选。此项目的保护措施所依赖的最低 safeinstall-cli 版本(精确的 semver)。运行中的 CLI 版本更旧时会发出警告——在 guard、install/check 和 trust status 中均如此——但绝不会硬性失败
minimumReleaseAgeHours注册表版本的最小发布时长(小时)
registryUrl用于获取元数据和安装的兼容 npm 的注册表 URL(镜像、Artifactory、Verdaccio)
allowedScripts针对各个包的生命周期脚本例外
allowedSources允许的来源类型
allowedPackages跳过发布时长、安装脚本和拼写仿冒检查的名称(会发出警告)。来源、信任降级和来源证明检查仍会执行。
packageManagerDefaults旧版配置;即使 ignoreScripts 为 false,生命周期脚本仍会保持禁用
typoSquat.mode"off" / "warn" / "block"——如何处理疑似拼写仿冒
typoSquat.minNameLength执行检查所需的最短包名称长度(更短的名称将跳过检查)
typoSquat.ignore应跳过的已知合法近似名称;加载时会转换为小写
provenance.mode"off" / "warn" / "require"——是否验证 Sigstore 证明
provenance.requireFor即使处于 "warn" 模式,也必须提供来源证明的包名称(支持 glob)
provenance.trustedPublishers包名称模式 → 预期的 owner/repo 标识符映射;不匹配时始终阻止
provenance.offlineBehavior"fail-closed" 在获取失败时阻止操作;"allow-cached" 则回退到缓存的证明
provenance.toolingUnavailable"warn"(默认)在未安装 sigstore 验证器时发出醒目警告并继续;"fail-closed" 将缺少验证器视为可疑,并阻止所有安装,直到验证器可用(安装 sigstore 本身仍为例外,因此绝不会陷入死锁)
transitive.mode"off" / "warn" / "block"——评估完整的锁文件依赖树,而不只是直接依赖
transitive.checks要对传递依赖执行的检查:"install-script" 和/或 "untrusted-source"
continuity.mode"off" / "warn" / "block"——根据学习到的各包基线,检测来源证明降级和源代码仓库变更
continuity.baselineSize学习基线时要抽样的近期版本数(默认值为 5)
固定最低 CLI 版本

不同版本的 guard 判定会有所变化(修复误报、新增检查),但 CLI 通常是全局安装的——因此,项目可能依赖 0.12.0 的行为,而 Agent 的机器上仍运行着 0.11.0。将 minimumCliVersion 设为策略所依赖行为的最低版本;所有更旧的 CLI 都会醒目地提示版本不匹配:

Warning: This project's safeinstall.config.json expects safeinstall-cli >= 0.12.0, but version 0.11.0 is running — protections this project relies on may be missing or behave differently. Update with `npm install -g safeinstall-cli@latest`.

此检查完全离线(将运行中的版本与 semver 进行比较),并且有意只发出警告,绝不硬性失败:硬性失败会导致每次发布后,在全局 CLI 更新之前,所有 Agent 会话都无法运行。请注意以下两个易踩的地方:

  • 此字段必须是精确版本("0.12.0"),不能是范围——它是最低版本,而不是约束条件。任何其他格式都会导致配置解析失败并采取拒绝策略。
  • 在此字段加入之前发布的 CLI 会拒绝未知配置键,因此会拒绝整个配置(失败时采取拒绝策略),而不是发出警告。设置此字段实际上要求协作者使用支持该字段的 CLI。

运行 safeinstall init 以生成初始配置。


退出码

代码含义
0已允许/检查通过
1运行时或配置错误
2被策略阻止

在 CI 流水线中,像处理其他失败步骤一样处理退出码 2。

JSON 输出

在命令中的任意位置传入 --json。结构化输出会发送到标准输出。

safeinstall --json pnpm add axios

字段:command、commandString、packageManager、decision、summary、reasons、warnings、affectedPackages、exitCode、exitCodeMeaning。允许安装时会包含 execution.stdout 和 execution.stderr。


示例

想查看每项检查的实际效果?运行 bash demo/run.sh——这是一个可复现脚本,针对真实软件包,为每项策略检查演示一个拦截场景。请参阅 demo/README.md 了解操作步骤。

想看看它如何应对真实攻击?运行 pnpm replay mastra——它将 2026 年 6 月 Mastra 遭入侵时记录的攻击状态输入 SafeInstall 的真实策略引擎,并展示哪些检查本可以拦截攻击。请参阅 demo/replay/README.md。

拦截新发布版本
$ safeinstall pnpm add axios
Using config: built-in defaults
Install blocked.
- [email protected]
  Blocked: release too new ([email protected] is 6 hours old; minimum is 72 hours).
  Suggestion: Retry later or lower minimumReleaseAgeHours if this package is intentionally urgent.
拦截 Git 源
$ safeinstall npm install github:axios/axios
Using config: built-in defaults
Install blocked.
- github:axios/axios
  Blocked: untrusted source (git).
  Suggestion: Use a registry release or allow this source intentionally.
包管理器不匹配
$ safeinstall npm install
Using config: built-in defaults
Install blocked.
- Project install blocked: package.json declares pnpm as packageManager, but this command uses npm.
锁文件过期
$ safeinstall pnpm install
Using config: built-in defaults
Install blocked.
- Project install blocked: axios is declared in package.json but missing from pnpm-lock.yaml.
识别仿冒包名
$ safeinstall pnpm add raect
Using config: ./safeinstall.config.json
Install blocked.
- raect
  Blocked: Suspected typo-squat: "raect" is 1 edit(s) away from popular package "react".
  Suggestion: Verify you meant to install "react". If this package is intentional, add "raect" to typoSquat.ignore.
发布者不匹配(防范维护者账号遭入侵)
$ safeinstall pnpm add [email protected]
Using config: ./safeinstall.config.json
Install blocked.
- [email protected]
  Blocked: publisher mismatch for axios (expected axios/axios, got evil-org/axios).
  Suggestion: Verify the package source. Update provenance.trustedPublishers only if the change is intentional.

GitHub Action

在每个拉取请求上自动运行 SafeInstall 策略检查。只需 5 行 YAML,无需配置——默认设置会立即生效。

# .github/workflows/safeinstall.yml
name: SafeInstall Policy Check
on: [pull_request]
jobs:
  policy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - uses: Mickdownunder/SafeInstall@v1

此 Action 会针对直接依赖运行 safeinstall check,并将作业状态设为通过或失败。被拦截的依赖会显示在 GitHub Actions 作业摘要中,其中包含确切的拦截原因和建议。

安装模式

若要在实际安装步骤中强制执行策略(而不仅仅是检查):

      - uses: Mickdownunder/SafeInstall@v1
        with:
          mode: install
          package-manager: pnpm
          args: "--frozen-lockfile"
输入
输入默认值描述
modecheckcheck 审计依赖;install 通过 SafeInstall 运行包管理器
package-managerpnpm安装时使用 npm 或 pnpm;bun 会失败关闭
args传递给包管理器的其他参数
config-pathsafeinstall.config.json 的显式路径(若省略,则自动发现)
versionlatest要安装的 SafeInstall CLI 版本
输出
输出描述
decisionallow 或 block
summary人类可读的单行摘要
exit-code0(允许)、2(拦截)或 1(错误)
jsonSafeInstall 的完整 JSON 结果

局限性

  • 不是 CVE 扫描器 — 请搭配 npm audit 或 Snyk 获取漏洞数据
  • 启用 transitive.mode 时,会检查传递依赖的安装脚本和不受信任的来源。发布时长、仿冒包和来源验证仍仅适用于直接依赖。
  • 传递依赖安装脚本检测仅支持 npm — pnpm lockfile 不会记录安装脚本是否存在。传递依赖的不受信任来源检测则同时支持两者。
  • **原生构建(binding.gyp)**会通过安装脚本检查捕获:npm 会在发布时将 binding.gyp 规范化为 node-gyp rebuild 安装脚本,因此 SafeInstall 已读取的注册表元数据中会包含该脚本 — 无需下载 tarball。剩余的边缘情况是:通过非标准客户端发布的软件包未进行此规范化,但仍包含 binding.gyp;要检测这种情况,需要检查 tarball 内容,这超出了本项目的范围(参见 不做的事)。
  • 除非同时声明为直接依赖,否则不会检查 peerDependencies
  • 信任降级检测需要 node_modules 中存在先前的安装状态
  • Bun 安装会以失败关闭;尚未实现制品绑定适配器
  • 可信执行边界 — 这不是操作系统沙箱。它依赖可信的 npm/pnpm 二进制文件来强制执行 frozen-lockfile 完整性、选定的注册表元数据,并假定本地文件系统不受并发攻击者控制。它无法证明软件包代码是良性的,无法根据注册表策略检查所有传递制品,也无法隔离运行时执行
  • 仿冒包目标列表由人工整理,并在各个版本发布之间手动更新;过去一天内新发布的软件包可能尚未列入
  • 来源验证仅支持公共 Sigstore 根上的 GitHub Actions 可信发布者(GitLab CI、自托管 Sigstore 目前不在支持范围内)
  • Git 来源会按 URL 识别,以用于允许列表,而不是推断软件包名称 — 将注册表中的 axios 与 github:any-fork/axios 混为一谈会很危险
  • 遇到有歧义的元数据时会阻止操作,而不是猜测 — 这是有意为之

不做的事

  • 漏洞扫描或 CVE 数据库
  • 注册表代理或 tarball 重写
  • 恶意软件检测或软件包内容分析
  • 选择性执行生命周期脚本(始终强制使用 --ignore-scripts)

适用工具

SafeInstall 适用于任何会运行包管理器命令的工具 — 包括 AI 编码助手:

Cursor · Codex · GitHub Copilot · Cline · Claude Code · Windsurf · Aider · Devin · Continue

只需在安装命令前加上 safeinstall。工作流不变,多一层安全保障。


参与贡献

pnpm install
pnpm typecheck
pnpm test
pnpm build

欢迎提交问题和 PR。作者会自行决定是否合并 — 本项目由个人维护。

许可证

MIT — 参见 LICENSE。

免责声明

SafeInstall 按照 MIT 许可证按原样提供。它是一款策略工具,可在软件包安装时强制执行可配置的规则。它不保证任何软件包的安全性,不会检测所有供应链攻击,也不能替代专业的安全审查。使用风险由您自行承担。作者不对因使用本软件而产生的任何损害承担责任。

最后验证时间:2026-10-01


safeinstall.dev · npm · GitHub

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。