UAantovakul/no-npm-claude

GitHub: UAantovakul/no-npm-claude

这是一个为 Claude Code 设计的安全防护工具,通过技能与钩子强制使用 pnpm 代替 npm,以防御供应链攻击。

Stars: 0 | Forks: 0

# nce it's a technical term, keep it in English. So, output "no-npm". But is that a translation? The instruction is to translate to Simplified Chinese, so I should provide a Chinese equivalent. Perhaps "无npm" but that might not be standard. Looking back, the user said "Keep all professional terms... in their original English form", so for "no-npm", I should keep it as "no-npm" in the translation. So, for output, it should be "no-npm" but since the output is in Chinese, I need to ensure it's in Chinese context. I think I'll output "no-npm" as is, but in a Chinese sentence structure. The headings are standalone, so I can output them directly. ![许可证](https://img.shields.io/badge/license-MIT-blue.svg) ![平台](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-lightgrey) ![Claude Code](https://img.shields.io/badge/Claude%20Code-skill%20%2B%20hook-purple) ![pnpm](https://img.shields.io/badge/package%20manager-pnpm%20only-orange) 一个为 [Claude Code](https://claude.com/claude-code) 设计的双层防护,用于 防止任何意外的 `npm` / `npx` / `yarn` 调用,并引导您使用 `pnpm`。软层是一个 **技能**(Claude 读取的指令);硬层 是一个 **PreToolUse 钩子**,它通过 harness 层级拦截 Bash 调用, 使用 `exit 2` 和 ANSI 粗体警告进行阻止。 ## 为什么 npm 生态系统经历了一系列重大的供应链攻击: - **Shai-Hulud**(2025 年 9 月)— 首个自我传播的蠕虫,500+ 个包被感染 - **Shai-Hulud 2.0**(2025 年 11 月)— 700+ 个包,27,000+ 个恶意 GitHub 仓库,14,000 个密钥被窃取 - **Mini Shai-Hulud**(2026 年 5 月)— 170+ 个 npm 包 + 2 个 PyPI 包 - **PackageGate**(2026 年 1 月)— npm / pnpm / vlt / Bun 中的六个零日漏洞 常见攻击向量:恶意代码在 `preinstall` / `postinstall` 阶段运行, 在任何测试或安全检查**之前**。pnpm v11+ 默认内置了两种 防御措施来封闭此类攻击: | pnpm 默认值 | 作用 | |---|---| | `strictDepBuilds: true` | 依赖中的生命周期脚本被阻止,除非明确允许 | | `minimumReleaseAge: 1440` | 新版本在发布至少 24 小时后才会被安装(恶意软件通常会在几小时内被发现并下架) | 此仓库在 Claude Code 内强制执行“仅限 pnpm”,因为代理可能 会自动运行 `npm install`。 ## 功能 - **技能** — `no-npm` 注册在 Claude Code 的技能列表中;代理 读取指令后,会拒绝运行 `npm` / `npx` / `yarn`,并提供 等效的 `pnpm` 命令。 - **PreToolUse 钩子** — Node.js 脚本,检查每个 `Bash` 工具 调用;如果命令词匹配 `npm` / `npx` / `yarn`,它会打印一个 ANSI 红色粗体横幅并以退出码 `2` 退出,harness 会将其视为“拒绝”。 - **幂等安装器** — 重新运行 `install.ps1` / `install.sh` 是 安全的;它不会重复添加钩子条目,并保留任何其他钩子 (例如 `block-destructive.js`)。 - **对 `pnpm` 无误报** — 基于单词边界的正则表达式可以区分 `pnpm` 和 `npm`,以及 `pnpx` 和 `npx`。 ## 要求 - **[Claude Code](https://claude.com/claude-code)**(CLI 或 VS Code 扩展) - **Node.js ≥ 18 LTS** 在 `PATH` 中 — 钩子是一个 JS 脚本;安装器使用 `node` 进行 JSON 合并 ``` node --version ``` ## 安装 ### To be safe, I'll translate each line while keeping English terms as is. ``` # Proposed translations: Expand-Archive .\no-npm-skill-bundle.zip -DestinationPath . -Force # 1. no-npm -> "no-npm" (keep in English, as per instruction) Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass # But the example 'Running Naabu' is translated to '运行 Naabu', where "Running" is translated to "运行" and "Naabu" kept. So, for "no-npm", if it's a command or term, I should translate any non-English part. "no" might be translated, but "npm" is a term. Since "no-npm" might be a whole term, I'll keep it as "no-npm". cd .\no-npm-skill-bundle .\install.ps1 ``` ### Let's move to other lines. ``` unzip no-npm-skill-bundle.zip cd no-npm-skill-bundle chmod +x install.sh ./install.sh ``` ### 安装器的工作内容 1. 检查 Node.js 是否可用。 2. 如果 `~/.claude/skills/no-npm/` 和 `~/.claude/hooks/` 不存在,则创建它们。 3. 将 `SKILL.md` 和 `block-npm.js` 复制到这些目录。 4. 将钩子条目合并到 `~/.claude/settings.json` 的 `PreToolUse → Bash` 下。现有的钩子会被保留。 5. 运行冒烟测试(`npm install` 应产生 `exit 2`)。 ## 验证 重启 Claude Code,然后在新对话中: 预期响应 — 一个看起来像这样的 **粗体** 警告: 如果 Claude 试图通过 Bash 调用运行 `npm`,harness 会通过 钩子拦截它,并将相同的横幅打印到 stderr,退出码为 `2`。 手动测试钩子: ``` echo '{"tool_input":{"command":"npm install"}}' | node ~/.claude/hooks/block-npm.js echo $? # should print 2 ``` ## 卸载 ``` # 2. Windows (PowerShell) -> "Windows (PowerShell)"? But "Windows" is often translated as "Windows" in Chinese, and "PowerShell" as "PowerShell". So, perhaps "Windows (PowerShell)" can be "Windows(PowerShell)" with Chinese parentheses. But to make it a translation, I can say "Windows PowerShell 环境" or simply "Windows (PowerShell)". I think for consistency with the example, I'll output "Windows (PowerShell)" with English terms. .\uninstall.ps1 # No, in the example, 'Kubernetes Setup' is translated to 'Kubernetes 设置', so "Kubernetes" is kept, and "Setup" is translated to "设置". Similarly, for "Windows (PowerShell)", "Windows" is a proper noun, keep it, "PowerShell" is a tool name, keep it, and the parentheses indicate something, so translate the context. But "PowerShell" might not need translation. So, output: "Windows (PowerShell)". ./uninstall.sh ``` 从 `settings.json` 中移除钩子条目(其他钩子不受影响),然后 删除 `~/.claude/skills/no-npm/` 和 `~/.claude/hooks/block-npm.js`。 ## 工作原理 ### 匹配正则表达式 ``` /(^|[\s;&|()`]|\$\()(npm|npx|yarn)(\s|$|[;&|`)])/ ``` 使用 Bash 感知分隔符的单词边界:起始位置、空白字符、 `;`、`&&`、`||`、`|`、`(`、`` ` ``、`$(`。 **被阻止的:** `npm install`、`npm i react`、`npx vite`、`yarn add lodash`、 `sudo npm i -g foo`、`cd app && npm test`。 **允许的:** `pnpm install`、`pnpm add react`、`pnpx whatever`、 `cat npmlock.txt`、`echo "npm is bad"`、`git log`。 ### 钩子约定 Claude Code 的 PreToolUse 钩子在 stdin 上接收一个 JSON 负载: ``` { "tool_input": { "command": "npm install" } } ``` 钩子向 **stderr** 输出并使用 **退出码**: - `exit 0` — 允许命令(`pnpm`、`git` 等的默认行为) - `exit 2` — 阻止命令;stderr 内容会显示给代理和用户 ## 包布局 ``` no-npm-skill-bundle/ ├── README.md — this file ├── LICENSE — MIT ├── install.ps1 — Windows installer ├── install.sh — macOS/Linux installer ├── uninstall.ps1 ├── uninstall.sh ├── install-hook.js — cross-platform JSON merger ├── uninstall-hook.js — cross-platform JSON cleaner └── payload/ ├── skills/no-npm/SKILL.md — the skill └── hooks/block-npm.js — the hook ``` ## 与其他 LLM 聊天客户端集成 此技能 + 钩子仅在 Claude Code 内工作(它使用技能 API 和 PreToolUse 钩子约定)。如果您还在其他客户端中使用 Claude(或另一个 LLM),请将此规则粘贴到系统提示 / 自定义 指令中: ``` HARD RULE: do not use npm, npx, or yarn in this environment. Always use pnpm. If I (or the user) want to run an npm/npx/yarn command: 1. Do not execute the command. 2. Issue a warning in BOLD letters: **⛔ NPM IS BLOCKED IN THIS ENVIRONMENT. Use pnpm instead.** 3. Offer the pnpm equivalent: npm i -> pnpm install npm i -> pnpm add npm run -> pnpm npx -> pnpm dlx yarn add -> pnpm add 4. Ask the user to confirm before running the pnpm variant. Reason: a chain of npm supply-chain attacks (Shai-Hulud 2025-2026). pnpm v11+ blocks lifecycle scripts and delays installation of fresh versions by 24 hours, which neutralizes the primary attack vectors. ``` 粘贴位置: - **Cursor** — 设置 → 规则 → 用户规则,或仓库根目录下的 `.cursorrules` - **Claude Desktop** — 设置 → 自定义指令 - **ChatGPT** — 设置 → 个性化 → 自定义指令 - **VS Code Continue / Cline** — 配置中的系统提示 - **CLI 代理 (Codex CLI, Aider 等)** — `--system` 标志或系统文件 ## 绕过规则(有意为之) 对于确实需要 `npm` 的一次性情况(例如调试不兼容的工具): 1. **推荐:** 在您自己的终端中,在 Claude Code 外运行命令。钩子仅拦截 harness 的 Bash 通道。 2. **临时禁用:** 在 `~/.claude/settings.json` 中注释掉钩子条目(`PreToolUse → Bash → block-npm.js`),运行命令,然后恢复。 ## 兼容性 - **Claude Code** — 使用支持技能 API 和 PreToolUse 钩子的版本进行测试(2025 年末及以后)。 - **Node.js** — ≥ 18 LTS(钩子仅使用标准库功能)。 - **操作系统** — Windows 10/11、macOS 12+、现代 Linux 发行版。 钩子**不**依赖于 Claude Code 内部 — 它只是一个纯 JSON-stdin / stderr / exit-code 约定,因此只要 PreToolUse 钩子约定保持稳定,它就会一直工作。 ## 贡献 欢迎提交 Issues 和 PRs。对任何正则表达式更改的快速测试: ``` # But to write it in Chinese, I need to use Chinese characters for the translation part. Perhaps "Windows 的 PowerShell" but that's not accurate. Let's assume the heading is "Windows (PowerShell)" meaning "Windows with PowerShell", so I can translate it as "Windows(PowerShell)". echo '{"tool_input":{"command":"npm install"}}' | node payload/hooks/block-npm.js; echo $? # I think I'll go with that. echo '{"tool_input":{"command":"pnpm install"}}' | node payload/hooks/block-npm.js; echo $? ``` 保持正则表达式的单词边界完整 — 对 `pnpm`、`pnpx`、 `npmlock` 或包含单词 `npm` 的字符串字面量的误报 不应发生。 ## 许可证 [MIT](./LICENSE) ## 相关阅读 - [pnpm — 缓解供应链攻击](https://pnpm.io/supply-chain-security) - [CISA — 影响 npm 生态系统的广泛供应链攻击](https://www.cisa.gov/news-events/alerts/2025/09/23/widespread-supply-chain-compromise-impacting-npm-ecosystem) - [Microsoft Security — Shai-Hulud 2.0](https://www.microsoft.com/en-us/security/blog/2025/12/09/shai-hulud-2-0-guidance-for-detecting-investigating-and-defending-against-the-supply-chain-attack/) - [Palo Alto Unit 42 — "Shai-Hulud" 蠕虫感染 npm 生态系统](https://unit42.paloaltonetworks.com/npm-supply-chain-attack/) - [Anthropic — Claude Code 文档](https://docs.claude.com/en/docs/claude-code) 由 [@UAantovakul](https://github.com/UAantovakul) 维护。
标签:Claude Code安全, MITM代理, Node.js脚本, npm阻止, pnpm替代, 包管理器安全, 包管理工具, 命令执行控制, 安全增强, 安全防护, 恶意代码防御, 技能系统, 数据可视化, 自定义脚本, 软件供应链攻击, 钩子技术, 防御钩子