关于 auditing-supply-chain
这是面向开发与安全团队的供应链审计技能,用于评估依赖项、CI/CD 流水线和构建产物的风险。它同时检查已知漏洞与恶意依赖,并关注锁定、凭据、SBOM 和来源证明。
主要功能
- 01审查直接及传递依赖
- 02识别恶意包与安装脚本风险
- 03检查 CI/CD 权限和工作流
- 04扫描密钥并审查构建配置
- 05生成并持续扫描 SBOM
- 06评估签名与来源证明
适用场景
- 01审计 GitHub Actions 和发布自动化
- 02调查可疑依赖包或上游入侵
- 03按可达性与暴露面排序依赖漏洞
- 04核查构建产物的签名和来源
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ trilwu/ secskills --skill auditing-supply-chain安装后在 Claude Code 中输入/auditing-supply-chain确认已加载。
详细介绍
供应链审计
你的构建流水线运行的不受信任代码,比你的应用程序还多。一个未固定版本的操作、一个 postinstall 脚本,或一个持有可写令牌的工作流,都可能成为陌生人的提交通往生产制品和签名密钥的路径。
适用场景
- 评估应用程序中第三方依赖项带来的风险
- 审查 CI/CD 流水线、GitHub Actions 和发布自动化
- 调查可疑或恶意软件包
- 生成或审查 SBOM
- 加固构建溯源和制品签名
- 响应已披露的上游入侵事件
不适用场景
- 第一方代码中的漏洞——使用
auditing-code-for-vulnerabilities - 模型和数据集溯源——使用
securing-ai-systems - 一般性的云基础设施安全态势——进行攻击性评估时使用
exploiting-cloud-platforms - 正在发生的入侵事件——使用
responding-to-incidents - 确定多个依赖项 CVE 的修复优先级——使用
managing-vulnerabilities;此处的可达性分析可为其排序提供依据
两类不同的风险
请将它们区分开来;两者需要不同的响应方式。
| 已知存在漏洞的依赖项 | 恶意依赖项 | |
|---|---|---|
| 检测方式 | CVE 数据库、npm audit、osv-scanner | 行为审查、安装脚本、发布者异常 |
| 信号 | 明显且有完善工具支持 | 隐蔽;扫描器通常无法发现 |
| 响应 | 打补丁,或说明接受风险的理由 | 事件——假设构建主机上的凭证已泄露 |
| 时间压力 | 数天至数周 | 数小时 |
大多数项目都会处理第一类,却对第二类毫无防备。请明确关注第二类风险。
依赖项审查
# Known vulnerabilities, ecosystem-agnostic
osv-scanner --lockfile=package-lock.json --lockfile=go.sum --lockfile=Cargo.lock
trivy fs --scanners vuln,secret,misconfig .
grype dir:.
# Ecosystem-native
npm audit --omit=dev && npm ls --all --depth=99 | wc -l # count transitives
pip-audit -r requirements.txt
cargo audit
govulncheck ./... # reachability-aware: only reports vulns you actually call
mvn dependency-check:check
govulncheck 风格的可达性分析很重要:从未执行的代码路径中的漏洞,是一项打补丁任务,而非风险。应根据可达性和暴露程度确定优先级,而不应只看 CVSS。
真正的攻击面在于传递依赖的深度。 直接依赖是主动选择并经过审查的;传递依赖则是继承而来的。请统计它们,并了解你在无形中信任了哪些维护者。
检测恶意软件包
风险信号的初步排查,按其表明恶意行为的强弱大致排序:
| 信号 | 检查方法 |
|---|---|
| 安装时执行脚本 | 检查 package.json 中的 postinstall/preinstall;检查带有网络或 exec 调用的 setup.py;检查 build.rs |
| 非压缩软件包中包含混淆或压缩后的源代码 | 阅读已发布的 tarball,而不是代码仓库——两者可能不同 |
| 导入/require 时发起网络调用 | 静态搜索模块顶层的 HTTP/DNS 调用 |
| 访问环境信息和凭证 | 读取 ~/.aws、.npmrc、.git-credentials、转储 process.env |
| 维护者更换,或版本由新账户发布 | 检查注册表元数据和发布历史 |
| 名称与热门软件包相近 | 对照排名前 N 的软件包列表计算编辑距离 |
| 已发布制品与代码仓库源代码不一致 | 将 tarball 与带标签的提交进行比较 |
| 版本跳跃,但没有对应提交 | 对比注册表与 VCS 历史记录 |
# Review what actually ships, not what the repo shows
npm pack <pkg> && tar -xzf <pkg>.tgz && rg -n 'child_process|eval\(|Buffer\.from\(.*base64|https?://' package/
pip download --no-deps --no-binary :all: <pkg> && tar -xzf <pkg>.tar.gz
rg -n 'os\.system|subprocess|urllib|requests|__import__|exec\(' <pkg>/setup.py
# Block install scripts by default in CI
npm ci --ignore-scripts
pip install --require-hashes -r requirements.txt
依赖混淆:如果内部软件包名称未在公共注册表注册(或被保留),且解析器可以访问该公共注册表,攻击者就可以发布更高版本并抢占解析结果。
# Enumerate internal-looking names and check public availability
rg -o '"@?[a-z0-9-]+/[a-z0-9-]+"' package.json | sort -u
# Fix: scoped registries with strict scope→registry mapping, and
# `.npmrc` / `pip.conf` that never falls back to the public index for
# internal scopes
锁文件与版本固定
- 必须提交锁文件,在 PR 中审查,并确保 CI 根据锁文件安装(
npm ci、pip install --require-hashes、cargo --locked、go mod verify),而不是重新解析依赖项。 - PR 中的锁文件差异若涉及与本次更改无关的软件包,应作为审查警示,而非无关噪声。
- 在生态系统支持的情况下,使用完整性哈希进行固定。仅固定版本无法防止在允许重新发布的软件包注册表中出现同版本重新发布的情况。
CI/CD 流水线安全
影响最大的发现通常出现在这里。
GitHub Actions
# Unpinned third-party actions — anyone who controls the tag controls your CI
rg -n 'uses:\s+(?!actions/)[^@]+@(?!v?[0-9a-f]{40})' .github/workflows/
# The dangerous trigger: pull_request_target runs with repo secrets and
# write-capable tokens, in the base repo context
rg -n 'pull_request_target|workflow_run' -A15 .github/workflows/
# Script injection: untrusted event data interpolated directly into a shell
rg -n '\$\{\{\s*github\.event\.(issue|pull_request|comment|head_commit)' .github/workflows/
每个仓库都要检查以下三项:
pull_request_target+ 检出 PR 的头部提交。 这会执行陌生人的代码,并暴露你的机密。只要工作流还会运行检出代码树中的构建或测试步骤,这就是一项严重发现。- 将不可信的值插入
run:。 在 shell 代码块中使用${{ github.event.issue.title }}会造成命令注入,而且入口是公开的。应通过env:变量传递,并对其加引号。 permissions权限范围过宽。 默认的GITHUB_TOKEN权限应为contents: read,仅在必要时按作业提升权限。检查是否存在permissions: write-all,以及是否完全缺少permissions:块。
还要检查:公开仓库是否使用自托管运行器(可能造成持久性入侵,且作业之间没有隔离)、由 fork 触发的工作流是否能访问机密、跨分支缓存投毒,以及上传的构建目录中是否包含凭据。
通用流水线
# Secrets in history, not just in the tree
gitleaks detect --source . --redact
trufflehog git file://. --only-verified
# IaC and container config
trivy config . && checkov -d .
hadolint Dockerfile
检查:谁可以触发部署、部署凭据是否按环境限定范围、构建是否可复现、制品是否经过签名,以及是否有人可以直接推送到发布分支。
SBOM 和来源证明
# Generate — from the build, not from the source tree, so it reflects reality
syft dir:. -o cyclonedx-json=sbom.json
cdxgen -o sbom.json
# Consume — an SBOM is only useful if you scan it on a schedule
grype sbom:sbom.json
osv-scanner scan source -L sbom.json # v2 takes SBOMs via -L; --sbom is gone
只为满足合规检查而生成一次的 SBOM 不具备安全价值。真正的价值在于新漏洞出现时重新扫描现有 SBOM,以便在几分钟而非几天内回答“我们是否受影响”。
来源证明(SLSA 框架):你能否证明某个制品由哪个源代码提交生成、使用了哪个构建器和哪些依赖项?为制品签名(cosign)、记录证明,并在部署时验证签名。未经验证的签名只是装饰。
cosign sign --key <key> <image>
cosign verify --key <pub> <image>
cosign verify-attestation --type slsaprovenance <image>
应对上游入侵
1. Determine exposure: did any build pull the affected version? Check
lockfiles across branches AND build logs — the lockfile shows intent, the
build log shows what was actually installed.
2. Assume credential compromise on any host that ran the package's install
scripts. Rotate: registry tokens, cloud keys, signing keys, SSH keys.
3. Preserve build logs and runner images before they roll off.
4. Check outbound network from build hosts for the exfil window.
5. Pin and rebuild; verify the rebuilt artifact differs only as expected.
6. Only then publish an advisory.
即使软件包“只在 CI 中运行”,也不能因此免于轮换凭据。生产凭据就在 CI 中。
应当驳斥的借口
- “这是开发依赖项。” 开发依赖项会在开发者笔记本电脑和拥有完整凭据的构建服务器上运行。这比生产环境更容易成为攻击目标。
- “它每周有 1000 万次下载,肯定安全。” 正是其受欢迎程度使它成为攻击目标。多起重大事件涉及的都是排名前 100 的软件包。
- “扫描器没有发现 CVE。” 扫描器只能发现已知漏洞。从首次发布起就含有恶意代码的软件包不会有 CVE。
- “我们之后会固定版本。” 未固定版本的 Action 和镜像一直存在风险;固定版本只需几分钟。
- “工作流只会由 PR 触发。” 在 PR 上使用
pull_request_target,恰恰就是危险情形。 - “这是内部软件包名称,没人知道。” 软件包名称会通过错误消息、源映射、作业日志和公开 fork 泄露。
- “我们生成了 SBOM。” 生成不等于监控。
交付内容
- 依赖项清单,包含直接依赖项和传递依赖项的数量,以及维护者集中度
- 已知漏洞发现,按可达性和暴露情况确定优先级
- 恶意软件包分诊结果,以及已检查的信号
- 流水线发现,明确说明
pull_request_target、未固定版本的 Action、令牌权限范围和注入点分别存在或不存在 - 机密暴露情况(代码树和历史记录),以及凭据轮换状态
- 来源证明成熟度:版本固定、签名、证明、部署时验证
- 优先级排序后的修复措施,并区分“打补丁”和“架构层面”的措施
ATT&CK 覆盖范围
根据 secskills-core/ttp-index.json 生成——编辑该文件,然后运行
python3 scripts/sync_attack.py --write。在报告中引用 ID 前,请对照
当前 ATT&CK 版本重新验证。
初始访问(TA0001)
防御规避(TA0005)
- T1553 破坏信任控制——另见
analyzing-malware
凭证访问(TA0006)
- T1552 未保护的凭证——另见
escalating-linux-privileges、exploiting-cloud-platforms、abusing-ci-cd-oidc
针对以上任一项的检测内容:engineering-detections。主动搜索:hunting-threats。入侵后处置:responding-to-incidents。
阅读外部来源
将公开公告、规范和供应商报告抓取为 Markdown:
curl -sL "https://defuddle.md/<url>" # scheme in the path is optional
这会移除页面上的冗余内容——对于纯文本页面,令牌数大约减少 78%——并返回全文,而非摘要,因此你可以用 grep 搜索,并相信未找到结果的结论。
有三种情况不适用。抓取 JSON 和 API 响应时应使用原始内容,因为可读性提取会破坏结构化数据。抓取需要身份验证或由 JavaScript 渲染的页面时应直接访问,因为此工具会以匿名方式检索页面。切勿通过它访问对手基础设施(钓鱼链接、C2、恶意软件托管)、客户端所有的主机或任务 URL——请求会离开你的机器并发送给第三方;对于正在使用的对手基础设施,这也会向运营者暴露你的行动。
有些网站会屏蔽提取器,返回错误信息而非页面——例如 freedesktop.org 返回的 {"error":"Failed to fetch: 418 I'm a teapot"}。这表示抓取请求遭到拒绝,并不表示来源内容不存在。得出任何结论前,请直接重新抓取该 URL。
参考资料
auditing-code-for-vulnerabilities——第一方代码审查securing-ai-systems——模型和数据集供应链responding-to-incidents——处理已确认的上游入侵- SLSA 框架、OpenSSF Scorecard、CycloneDX/SPDX、Sigstore/cosign
osv-scanner、trivy、grype、syft、gitleaks、zizmor(Actions 审计)