Communitygithub.com

npm 包发布加固

一个智能体技能,可在 npm 包公开发布前进行加固——检测泄露的密钥、运行时环境不匹配以及 tarball 中的垃圾文件。适用于任何兼容智能体的平台。

npm 包发布加固 是什么?

该技能旨在帮助 AI 智能体在 npm 包发布前进行系统性的安全加固和完整性检查。它从确认代码仓库状态开始,依次审查包的合约(package.json 字段)、运行时环境规则、CI 配置、GitHub 仓库设置、Dependabot 配置及其 PR 分类,最后生成发布清单。技能支持 npm、pnpm、yarn 三种包管理器,并能根据锁文件自动适配命令。其核心价值在于将发布流程从“本地能跑就行”升级为“可审计、可复现、安全可信”,特别关注 tarball 内容过滤、Node 版本承诺验证、以及 GitHub Actions 工作流的安全最佳实践。该技能适用于 Claude Code、Cursor、Codex 等支持 agent skills 的 AI 编码助手。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add SSBrouhard/npm-package-release-hardening

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

npm 包发布加固 是做什么的?

目标

将 npm 包从“本地能跑”升级为“诚实、可检查、安全可发布”。优先进行真实验证,而非仅凭感觉:检查包 tarball 内容、运行时契约、CI、GitHub 设置以及依赖更新卫生。

从这里开始

  1. 确认确切的仓库:
pwd
git status --short --branch
git remote -v
  1. 检查包体表面:
cat package.json
ls -la
find .github -maxdepth 3 -type f -print 2>/dev/null
  1. 根据锁文件检测包管理器:
  • package-lock.json -> npm
  • pnpm-lock.yaml -> pnpm
  • yarn.lock -> yarn

默认使用下方 npm 命令,必要时根据检测到的包管理器进行转换。

包合约

确保 package.json 真实准确:

  • name:发布目标是有意且可用的
  • version:当前预期的发布版本
  • description、keywords:足以支持注册表搜索
  • license:存在且与 LICENSE 文件一致
  • repository、homepage、bugs:指向公共仓库。如果公共仓库 URL 尚未最终确定,将其标记为显式的发布前待办事项,而非默默省略
  • type、exports、main、bin:与用户将要导入或运行的内容匹配
  • files:限制 tarball 仅包含可发布工件
  • engines.node:包实际支持的最低运行时版本
  • prepublishOnly 或等效脚本:防止发布过时的构建输出

对于 CLI 包,确认 bin 目标已构建、在相关情况下可执行,并通过用户将运行的命令进行冒烟测试。

运行时最低版本规则

Node 引擎是一种承诺,而非装饰。

  • 如果 engines.node 指定了 >=20,CI 必须测试 Node 20。
  • 保持 @types/node 与最低支持运行时位于同一主版本线上。
  • 优先将 @types/node 精确固定到该运行时线上的最新补丁版本。
  • 除非同时提升 engines.node,否则不要合并 @types/node 高于运行时最低版本的主版本升级。
  • engines.node 是建议性的:npm 只发出警告,除非用户设置了 engine-strict,否则不会硬性阻止安装。因此,在最低版本上运行 CI 才是真正强制履行承诺的方式。

原因:Node 类型仅在开发中使用,但它们会改变 TypeScript 允许的行为。@types/node@26 可能会让代码编译通过,同时使用的 API 在 Node 20 的用户那里却会失败。

验证命令

对于 npm 项目:

npm ci
npm run build --if-present
npm test --if-present
npm audit --omit=dev
npm pack --dry-run

对于 pnpm/yarn,使用等效的 install/build/test/audit/pack 命令。如果审计支持不同,请如实说明,而不要假装支持。

对于 CLI,还需至少运行一次外部冒烟测试:

node dist/path/to/cli.js --help
npm pack --dry-run

如果可行,将打包后的 .tgz 安装到临时目录,并完全按照用户的方式运行二进制文件。

Tarball 审查

npm pack --dry-run 是注册表真相的来源。检查内容:

  • 包含构建输出
  • 包含 README.md 和 LICENSE
  • 排除仅用于源代码的垃圾文件、临时文件、测试文件、私有文档、测试夹具、本地数据库、密钥
  • 包大小合理
  • CLI 的 bin 路径出现在 tarball 中

如果缺少 files 字段,请添加它。不要依赖 .gitignore 作为发布策略。

GitHub 基础配置

添加或验证:

  • .github/workflows/ci.yml
  • .github/dependabot.yml
  • .github/CODEOWNERS
  • .github/pull_request_template.md
  • SECURITY.md

对 GitHub 拥有的操作使用当前支持的主版本。如果 Actions 警告某操作运行时已弃用,请在 CI 通过后更新该操作的主版本。

CI 通常应包括:

permissions:
  contents: read

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@CURRENT_MAJOR
      - uses: actions/setup-node@CURRENT_MAJOR
        with:
          node-version: 20
          cache: npm
      - run: npm ci
      - run: npm run build --if-present
      - run: npm test --if-present

将 node-version 调整为包的最低支持运行时版本,并将每个 @CURRENT_MAJOR 替换为操作当前的 major 标签(例如 actions/checkout@v4)——固定到真正的 major,绝不要提交占位符。

Dependabot 基础配置

同时配置包和 GitHub Actions 更新:

version: 2
updates:
  - package-ecosystem: npm
    directory: /
    schedule:
      interval: weekly
    open-pull-requests-limit: 5
    commit-message:
      prefix: deps
      include: scope

  - package-ecosystem: github-actions
    directory: /
    schedule:
      interval: weekly
    open-pull-requests-limit: 5
    commit-message:
      prefix: deps
      include: scope

当 Dependabot 在配置生效后立即打开 PR 时,将此视为配置已生效的证据。对 PR 进行分类;不要盲目合并所有通过检查的改动。

Dependabot PR 分类

使用以下经验法则:

  • 运行时依赖的补丁/次版本:如果检查通过且发布说明平淡,则合并。
  • 运行时依赖的主版本:检查发布说明并对包的实际行为进行冒烟测试。
  • 开发工具的主版本:如果构建/测试/打包通过且不改变运行时契约,则合并。
  • 高于运行时最低版本的 @types/node:关闭或替换为固定到运行时最低版本。
  • GitHub Actions 的主版本:如果 CI 通过且移除了弃用警告,则合并。

如果两个 PR 在某个小配置文件中冲突,合并一个并更新另一个,或者直接应用第二个并附上清晰的注释说明已被取代。

GitHub 远程加固

应用仓库/账户支持的内容,然后使用 gh 或 API 读取来验证实际设置。

有用的设置:

gh repo edit OWNER/REPO \
  --delete-branch-on-merge \
  --enable-squash-merge \
  --enable-merge-commit=false \
  --enable-rebase-merge=false \
  --allow-update-branch

gh api --method PUT repos/OWNER/REPO/vulnerability-alerts --silent
gh api --method PUT repos/OWNER/REPO/automated-security-fixes --silent
gh api --method PUT repos/OWNER/REPO/actions/permissions/workflow \
  -f default_workflow_permissions=read \
  -F can_approve_pull_request_reviews=false --silent

对于 Actions 白名单,当工作流仅使用 GitHub 拥有的操作时,优先仅允许这些操作。如果需要第三方操作,则明确允许最小的集合。

公共仓库通常会解锁更多安全控制。免费计划上的私有仓库可能会阻止:

  • 分支保护或规则集
  • 代码扫描
  • 密钥扫描
  • 推送保护

请明确说明被阻止的内容。不要报告未启用的控制项为已启用。

分支保护

如果可用,请使用以下方式保护默认分支:

  • 合并前要求拉取请求
  • 要求 CI 状态检查通过
  • 如果适合项目,要求分支保持最新
  • 要求讨论已解决
  • 阻止强制推送
  • 阻止删除
  • 如果使用仅压缩合并,要求线性历史

启用保护后,除非用户明确要求并理解绕过风险,否则停止直接推送到 main。

门控 PR 工具

如果使用自动化审查/门控工具,请确保远程仓库已有默认/基础分支,然后再要求门控创建 PR。空的远程仓库通常需要先进行一次初始的 main 推送;否则,门控可以验证并推送功能分支,但由于没有基础引用,PR 创建会失败。

发布清单

在 npm publish 之前:

  • npm whoami 显示预期账户
  • npm view PACKAGE_NAME name version 确认名称状态
  • 待发布提交的 CI 为绿色
  • npm pack --dry-run 已审查
  • README 中的安装/使用示例与实际行为一致
  • repository、homepage 和 bugs 指向真实的公共仓库(如果 URL 未最终确定,则明确标记为 TODO)
  • 没有阻碍发布的 Dependabot/安全告警残留
  • 版本号有意提升、已在 git 中打标签(git tag vX.Y.Z && git push --tags)并在 CHANGELOG 或 GitHub Release 中反映
  • 作用域包(@scope/name)使用 --access public 发布(作用域包默认为受限/私有)

对于使用 Web/通行密钥/双因素认证的账户:

npm publish --auth-type=web

优先从 CI 使用来源证明发布,以获得可验证的供应链链接。来源证明需要公共包、受支持的 CI(如 GitHub Actions)以及作业上的 id-token: write 权限:

npm publish --provenance --access public

发布后:

npm view PACKAGE_NAME version
npm view PACKAGE_NAME bin files engines repository --json
npx -y PACKAGE_NAME --help

如果使用来源证明发布,请确认注册表在包页面上显示了来源证明 /“Built and signed” 认证信息。

最终响应

仅报告实际验证的内容:

  • 已运行的包检查
  • Tarball 结果
  • 依赖 PR 决策
  • 已创建或合并的提交/PR
  • 已确认的 GitHub 设置
  • 被计划/可见性阻止的控制项
  • 已发布的版本标签和来源证明状态(如果已发布)
  • 如果仍由用户操作,则发布命令或发布后验证

相关技能