- 开发工具
- CLI
- 前端构建
【免费下载链接】extension-create
The cross-browser extension framework.
Extension.js(本仓库即其源码)的每一个发布版本都使用 Sigstore 进行无密钥(keyless)签名,npm 包携带来源证明(provenance),GitHub Release 附带 SLSA 证明文件。本文基于 docs/VERIFYING_RELEASES.md,完整讲解"为什么可以无密钥签名"以及"如何用npm audit signatures、gh attestation verify、slsa-verifier三步验证发布产物",并结合本仓库的发布流水线源码(.github/workflows/release-attest.yml、.github/workflows/publish-release.yml)说明校验背后的实现细节。
读完本文,你将掌握:如何审计项目依赖中 Extension.js 四个 npm 包的签名与来源证明、如何核对某个版本的 attestation 详情、如何验证 GitHub Release 上 tarball 与 SLSA 证明文件的对应关系,以及新旧两种证明文件(provenance.intoto.jsonl与multiple.intoto.jsonl)各自对应的校验工具与参数差异。
为什么可以"无密钥"签名:Sigstore 信任模型
官方文档首先解释了整个签名体系的设计前提:
Every Extension.js release is signed with Sigstore. Signing is keyless.
所谓 keyless(无密钥)签名,核心是不存在长期有效的私钥:
- Sigstore 的Fulcio组件会为构建发布版本的 GitHub Actions 工作流签发一个短期证书(short-lived certificate);
- 签名记录同时写入Rekor透明日志(transparency log),任何人都能追溯;
- 由于没有长期私钥,npm、GitHub 或任何分发站点上都没有可泄露的签名密钥;
- 公共信任根(public trust roots)属于 Sigstore 本身,下面所有校验工具都会自动为你获取这些信任根。
这种设计把"密钥管理"问题转化为"工作流身份 + 透明日志"问题:攻击者无法通过窃取某个长期密钥来伪造发布,因为签名身份是每次构建临时产生的,且记录公开可查。在本仓库的 docs/ASSURANCE_CASE.md 中,这一机制被明确列为"源代码仓库与发布包之间"信任边界(Trust boundary 5)的保障:发布工作流运行在 GitHub 托管的 runner 上,配合 OIDC 发布与 Sigstore 来源证明;同时它也是 CWE-494(未经验证下载的代码)的对策之一——"npm 完整性哈希 + 包签名来源证明"。
校验 npm 包:来源证明(npm provenance)
Extension.js 一共发布四个公共 npm 包,发布范围在 docs/RELEASING.md 中列明:
| 源码目录 | npm 包名 | 用途 |
|---|---|---|
programs/extension | extension | CLI 元包,聚合其余三个包 |
programs/create | extension-create | 独立的脚手架创建引擎 |
programs/develop | extension-develop | 开发、构建、预览与打包 |
programs/install | extension-install | 浏览器运行时安装支持 |
这四个包均以 npm provenance 发布。发布侧的证据在 .github/workflows/publish-release.yml 中:
- 工作流环境变量
NPM_CONFIG_PROVENANCE: "true"(第 29 行); - 每个包的发布命令均带
--provenance标志,例如npm publish --access public --tag ${{ steps.validate.outputs.tag }} --provenance(第 530–535、571–576 行,四个包各有一次); - 发布不读取任何 npm token,而是通过 GitHub OIDC 可信发布(trusted publishing,
id-token: write),与 docs/RELEASING.md 中"npm authentication: the workflow publishes through npm trusted publishing with GitHub OIDC"的描述一致。
审计项目内已安装的包
在安装了这些包的任意项目里运行:
npm audit signatures输出中必须报告这四个包的 verified registry signatures(已验证的注册表签名)与 verified attestations(已验证的来源证明)。这一步直接校验node_modules中已安装产物的完整性,是最快的日常检查手段。
查看某个版本的 attestation 详情
如需查看某一版本的来源证明内容,例如 4.1.18:
npm view extension@4.1.18 dist.attestations该命令输出该版本发布时的证明元数据(包含签名的 build 信息等)。此外,npm 的包页面也会显示一个Provenance区块,其中链接到构建该发布的确切工作流运行记录与 commit——也就是说,你可以从包页面一路追溯到"这个 tarball 是哪次 CI 运行构建出来的"。
校验 GitHub Release tarball
除了 npm,每个 GitHub Release 还会附带四个包的 tarball 和一份已签名的 SLSA 证明文件。官方文档特别强调两个关键事实:
- Release 上的 tarball 与 npm 包字节完全一致("The release tarballs have the same bytes as the npm packages");
- 发布工作流在上传之前,会先对照注册表完整性哈希(registry integrity hashes)逐一核对它们。
这两点在 .github/workflows/release-attest.yml 中有对应的实现细节:subjects任务通过npm pack <name>@<version>从注册表取回 tarball,然后用npm view <pkg>@<ver> dist.integrity取期望值,再以openssl dgst -sha512计算实际哈希,两者不一致立即失败("does not match the registry integrity")。也就是说,Release 资产在生成时就已被证明与 npm 分发的内容同源。
使用方式:从仓库的 Releases 页面下载 tarball 与对应版本的证明文件,然后根据该 Release 上的证明文件名选择匹配的校验命令——这取决于该版本由哪条工作流签名。
新版本:provenance.intoto.jsonl,用gh attestation verify
较新的 Release 携带provenance.intoto.jsonl,由release-attest.yml工作流配合 GitHub 的 attestation action(actions/attest-build-provenance)签名。校验命令(需安装 GitHub CLI):
gh attestation verify extension-X.Y.Z.tgz \ --repo extension-js/extension.js \ --bundle provenance.intoto.jsonl \ --signer-workflow extension-js/extension.js/.github/workflows/release-attest.yml参数说明:
extension-X.Y.Z.tgz:替换为你实际下载的 tarball 文件名;--repo extension-js/extension.js:声明产物来源仓库,防止跨仓库混淆;--bundle provenance.intoto.jsonl:指定本地下载的 Sigstore bundle 文件;--signer-workflow extension-js/extension.js/.github/workflows/release-attest.yml:将签名身份钉死到这条工作流文件,避免"任何工作流签名都能通过"的宽泛校验。
官方文档还说明:如果不带--bundle,gh attestation verify会直接从 GitHub 拉取同一份 attestation 进行校验——因为 GitHub 同时把证明存储在仓库的 attestation API 中(对应release-attest.yml中attestations: write权限与Attest build provenance步骤)。
旧版本:multiple.intoto.jsonl,用slsa-verifier
较早的 Release 携带multiple.intoto.jsonl,由 SLSA GitHub generator(slsa-github-generator 可复用工作流)签名。校验前先安装 slsa-verifier,然后运行:
slsa-verifier verify-artifact extension-X.Y.Z.tgz \ --provenance-path multiple.intoto.jsonl \ --source-uri github.com/extension-js/extension.js两点注意事项(官方文档明确强调):
- 命令必须打印
PASSED才算通过; - 必须使用
--source-uri而不是--source-tag:因为证明文件记录的 source ref 是main——attest 工作流并不在 tag 上运行,用--source-tag会校验失败。
从源码角度看,这条历史差异的来龙去脉记录在 .github/workflows/release-attest.yml 的注释中:旧的 slsa-github-generator 拒绝 commit 固定(pin),所以早期版本携带multiple.intoto.jsonl;迁移到 GitHub 官方attest-build-provenanceaction(按 commit 固定)后,新版本改带 Sigstore bundle(provenance.intoto.jsonl),并且同一份证明同时保存在 GitHub 上,因此两种校验方式(带--bundle或不带)都能验证同一个 tarball。
背后的发布与证明流水线
理解校验命令之后,再看流水线本身会更容易把握"证明从哪里来"。
发布流水线(publish-release.yml)
发布由Release – Publish工作流驱动,支持三条通道:
| 通道 | npm dist-tag | 说明 |
|---|---|---|
| stable | latest | 正式版,需纯净 semver(如4.1.33) |
| next | next | 预发布,需-next.*形式 |
| canary | canary | 静默发布,自动版本号<base>-canary.<run_number>.<short_sha>,不建 GitHub Release |
对 stable/next,CI 构建后生成 release notes、提交版本号、打v<version>tag、创建 GitHub Release 与 Deployment;canary 则全部跳过。发布时通过 OIDC 可信发布 +--provenance,整个过程不读取 npm token。
证明流水线(release-attest.yml)
该工作流与发布工作流刻意解耦("decoupled from publish-release.yml on purpose"),由三个事件触发:
workflow_run:发布工作流成功完成后触发(常规路径);releasepublished:手动把草稿 Release 转为公开时触发;workflow_dispatch:手动为某个 tag 补签(backfill),例如gh workflow run release-attest.yml -f tag=v4.1.17。
其内部由四个任务串成:
- resolve:解析出要签名的 release tag,拒绝 draft 状态;
- subjects:从 tag 自身的 package.json 读取四个包名,等待 npm 复制完成后拉取 tarball,用 sha512 对注册表 integrity 做逐字节核对,再算出
sha256sum作为证明的 subject 列表; - provenance:用 OIDC 令牌调用
actions/attest-build-provenance做 keyless Sigstore 签名,把 bundle 以provenance.intoto.jsonl上传到 Release; - assets:重新拉取 tarball、再次核对 integrity,用
gh attestation verify --bundle provenance.intoto.jsonl逐一验证后才把 tarball 上传到 Release。
这个"先传证明、后传产物"的顺序是有意为之的保证:任何失败都只会让 Release 停留在"未评分"或"仅含证明"的状态,绝不会出现"有 tarball 却没有证明"的不可校验状态(注释中提及 OpenSSF Scorecard 的 Signed-Releases 检查会据此评分)。
校验实践建议与常见注意点
综合文档与仓库源码,可以总结出几条可落地的操作要点:
- 日常依赖审计:在项目里定期运行
npm audit signatures,确认四个 Extension.js 包均为 verified registry signatures + verified attestations; - 核对单个版本:用
npm view extension@<version> dist.attestations查看来源证明,再结合 npm 包页面的 Provenance 区块追溯到具体工作流运行与 commit; - 验证 Release tarball:先看该 Release 携带的是哪种证明文件——
provenance.intoto.jsonl用gh attestation verify(记得带--signer-workflow收紧签名身份),multiple.intoto.jsonl用slsa-verifier verify-artifact(用--source-uri而非--source-tag,并确认输出PASSED); - 新旧版本差异:同一仓库的不同版本可能使用不同的证明格式与签名工作流,务必"按文件匹配命令",不要拿新命令去验旧文件;
- canary 与草稿 Release:canary 通道不创建公开 Release;草稿状态的 Release 不会被 attest 工作流处理(会直接拒绝),需要先公开再触发校验。
延伸阅读
- docs/VERIFYING_RELEASES.md:本文的原始依据文档;
- docs/RELEASING.md:发布通道、版本规则与 npm 可信发布细节;
- docs/ASSURANCE_CASE.md:将签名与来源证明置于整体安全模型中的论证;
- .github/workflows/release-attest.yml:SLSA 证明与 tarball 上传的实现;
- .github/workflows/publish-release.yml:
--provenance发布与 OIDC 可信发布的实现; - 四个发布包的
publishConfig(public 访问与 npmjs 注册表)可见于 programs/extension/package.json、programs/create/package.json、programs/develop/package.json、programs/install/package.json。
- 开发工具
- CLI
- 前端构建
【免费下载链接】extension-create
The cross-browser extension framework.
相关推荐
CCX 发布产物完整性验证指南:Sigstore 无密钥签名、cosign 校验与 Windows Authenticode 双重保障
CCX 发布产物完整性验证指南:Sigstore 无密钥签名、cosign 校验与 Windows Authenticode 双重保障 CCX 的正式 Rele
API网关LLM 网关后端CCX 发布产物安全验证实战:Sigstore keyless 签名与 Windows Authenticode 双重校验
CCX 发布产物安全验证实战:Sigstore keyless 签名与 Windows Authenticode 双重校验 本文围绕 CCX(Claude /
API网关LLM 网关后端release-it 发布 npm 包完全指南:版本提升、发布校验、2FA 与 CI/CD 实战
release it 发布 npm 包完全指南:版本提升、发布校验、2FA 与 CI/CD 实战 在 release it https://link.gitco
开发工具DevOps
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考