supply-chain-attack-response
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSupply Chain Attack Response
软件供应链攻击响应
Software supply chain attacks target the dependencies, build systems, and distribution channels that developers trust implicitly. When a package on PyPI, npm, or crates.io is compromised, every downstream consumer inherits the malicious payload. This skill provides detection techniques, emergency response playbooks, and hardening strategies to protect your software supply chain end to end.
软件供应链攻击针对开发者默认信任的依赖项、构建系统和分发渠道。当PyPI、npm或crates.io上的某个包被攻陷时,所有下游使用者都会继承恶意负载。本技能提供检测技术、应急响应手册和加固策略,端到端保护你的软件供应链。
1. When to Use This Skill
1. 何时使用本技能
Invoke this skill when any of the following apply:
- A dependency you consume has been flagged as compromised (e.g., advisories on OSV.dev, GitHub Advisory Database, or vendor disclosure).
- You observe suspicious behavior from a dependency: unexpected network calls, file system writes outside its scope, or new post-install scripts.
- You are conducting a periodic supply chain security audit.
- A CI/CD pipeline is behaving unexpectedly after a dependency update.
- You are onboarding a new third-party dependency and want to verify its provenance.
- You need to respond to an incident such as a typosquatted package or registry account takeover.
- You are implementing SLSA compliance or need to generate build provenance.
出现以下任意情况时,调用本技能:
- 你使用的某个依赖项被标记为已攻陷(例如OSV.dev、GitHub Advisory Database上的公告,或供应商披露)。
- 你观察到依赖项的可疑行为:意外的网络调用、超出其范围的文件系统写入,或新增的安装后脚本。
- 你正在进行定期供应链安全审计。
- 更新依赖项后,CI/CD流水线出现异常行为。
- 你正在引入新的第三方依赖项,需要验证其来源。
- 你需要响应诸如仿冒包(typosquatted package)或注册表账户被接管等事件。
- 你正在实施SLSA合规要求,或需要生成构建来源信息(build provenance)。
2. Detection
2. 检测
2.1 npm Audit
2.1 npm Audit
bash
undefinedbash
undefinedFull audit of installed packages
对已安装包进行全面审计
npm audit
npm audit
JSON output for programmatic processing
输出JSON格式结果用于程序化处理
npm audit --json | jq '.vulnerabilities | to_entries[] | select(.value.severity == "critical")'
npm audit --json | jq '.vulnerabilities | to_entries[] | select(.value.severity == "critical")'
Fix automatically where possible
自动修复可修复的问题
npm audit fix
npm audit fix
Check for known malicious packages via Socket.dev CLI
通过Socket.dev CLI检查已知恶意包
npx socket scan --package-lock package-lock.json
undefinednpx socket scan --package-lock package-lock.json
undefined2.2 pip Audit
2.2 pip Audit
bash
undefinedbash
undefinedInstall pip-audit (maintained by Google/OSSF)
安装pip-audit(由Google/OSSF维护)
pip install pip-audit
pip install pip-audit
Audit current environment against OSV.dev
对照OSV.dev审计当前环境
pip-audit
pip-audit
Audit a requirements file directly
直接审计requirements文件
pip-audit -r requirements.txt --output json
pip-audit -r requirements.txt --output json
Check for typosquatting with bandersnatch or custom script
使用bandersnatch或自定义脚本检查仿冒包
pip-audit --strict --desc on
undefinedpip-audit --strict --desc on
undefined2.3 Cargo Audit
2.3 Cargo Audit
bash
undefinedbash
undefinedInstall cargo-audit
安装cargo-audit
cargo install cargo-audit
cargo install cargo-audit
Run audit against RustSec Advisory Database
对照RustSec Advisory Database运行审计
cargo audit
cargo audit
JSON output for CI integration
输出JSON格式结果用于CI集成
cargo audit --json
cargo audit --json
Check for yanked crates
检查已撤回的crates
cargo audit --deny yanked
undefinedcargo audit --deny yanked
undefined2.4 Sigstore / Cosign Verification
2.4 Sigstore / Cosign 验证
bash
undefinedbash
undefinedVerify a container image signature with cosign
使用cosign验证容器镜像签名
cosign verify
--certificate-identity "https://github.com/myorg/myrepo/.github/workflows/build.yml@refs/heads/main"
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
ghcr.io/myorg/myimage:latest
--certificate-identity "https://github.com/myorg/myrepo/.github/workflows/build.yml@refs/heads/main"
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
ghcr.io/myorg/myimage:latest
cosign verify
--certificate-identity "https://github.com/myorg/myrepo/.github/workflows/build.yml@refs/heads/main"
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
ghcr.io/myorg/myimage:latest
--certificate-identity "https://github.com/myorg/myrepo/.github/workflows/build.yml@refs/heads/main"
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
ghcr.io/myorg/myimage:latest
Verify an artifact with sigstore-python
使用sigstore-python验证工件
pip install sigstore
python -m sigstore verify identity
--cert-identity "release@example.com"
--cert-oidc-issuer "https://accounts.google.com"
artifact.tar.gz
--cert-identity "release@example.com"
--cert-oidc-issuer "https://accounts.google.com"
artifact.tar.gz
undefinedpip install sigstore
python -m sigstore verify identity
--cert-identity "release@example.com"
--cert-oidc-issuer "https://accounts.google.com"
artifact.tar.gz
--cert-identity "release@example.com"
--cert-oidc-issuer "https://accounts.google.com"
artifact.tar.gz
undefined2.5 SLSA Provenance Checks
2.5 SLSA 来源信息检查
bash
undefinedbash
undefinedInstall slsa-verifier
安装slsa-verifier
go install github.com/slsa-framework/slsa-verifier/v2/cli/slsa-verifier@latest
go install github.com/slsa-framework/slsa-verifier/v2/cli/slsa-verifier@latest
Verify provenance of a binary
验证二进制文件的来源信息
slsa-verifier verify-artifact my-binary
--provenance-path my-binary.intoto.jsonl
--source-uri github.com/myorg/myrepo
--source-tag v1.2.3
--provenance-path my-binary.intoto.jsonl
--source-uri github.com/myorg/myrepo
--source-tag v1.2.3
---slsa-verifier verify-artifact my-binary
--provenance-path my-binary.intoto.jsonl
--source-uri github.com/myorg/myrepo
--source-tag v1.2.3
--provenance-path my-binary.intoto.jsonl
--source-uri github.com/myorg/myrepo
--source-tag v1.2.3
---3. Emergency Response Playbook
3. 应急响应手册
When a dependency is confirmed compromised, execute these steps in order.
确认依赖项被攻陷后,按以下顺序执行步骤。
Step 1: Contain -- Pin and Freeze
步骤1:遏制 -- 固定版本并冻结
bash
undefinedbash
undefinedPin the last known-good version immediately in package.json
立即在package.json中固定最后一个已知安全的版本
npm install <package>@<safe-version> --save-exact
npm install <package>@<safe-version> --save-exact
For pip, pin with hash verification
对于pip,使用哈希验证固定版本
pip download <package>==<safe-version> --require-hashes -d ./vendor/
pip download <package>==<safe-version> --require-hashes -d ./vendor/
For cargo, pin in Cargo.toml
对于cargo,在Cargo.toml中固定版本
Replace: some_crate = "^1.2" with:
将: some_crate = "^1.2" 替换为:
some_crate = "=1.2.3"
some_crate = "=1.2.3"
cargo update -p some_crate --precise 1.2.3
undefinedcargo update -p some_crate --precise 1.2.3
undefinedStep 2: Audit Exposure
步骤2:审计暴露范围
bash
undefinedbash
undefinedDetermine which versions you pulled and when
确定你拉取的版本和时间
npm
npm
npm ls <compromised-package>
cat package-lock.json | jq '.packages | to_entries[] | select(.key | contains("<compromised-package>"))'
npm ls <compromised-package>
cat package-lock.json | jq '.packages | to_entries[] | select(.key | contains("<compromised-package>"))'
pip
pip
pip show <compromised-package>
pip cache list <compromised-package>
pip show <compromised-package>
pip cache list <compromised-package>
Check git history for when the dependency version changed
检查git历史记录,查看依赖项版本何时变更
git log --all -p -- package-lock.json | grep -A2 -B2 "<compromised-package>"
undefinedgit log --all -p -- package-lock.json | grep -A2 -B2 "<compromised-package>"
undefinedStep 3: Scan for Indicators of Compromise
步骤3:扫描入侵指标(IOC)
bash
undefinedbash
undefinedSearch for known IOCs from the advisory
搜索公告中提到的已知入侵指标
grep -r "suspicious-domain.com" ./node_modules/<compromised-package>/
grep -r "eval(atob" ./node_modules/<compromised-package>/
grep -r "suspicious-domain.com" ./node_modules/<compromised-package>/
grep -r "eval(atob" ./node_modules/<compromised-package>/
Check for unexpected post-install scripts
检查意外的安装后脚本
cat node_modules/<compromised-package>/package.json | jq '.scripts'
cat node_modules/<compromised-package>/package.json | jq '.scripts'
For Python packages, inspect setup.py and init.py
对于Python包,检查setup.py和__init__.py
find ~/.local/lib/python*/site-packages/<compromised-package>/ -name "*.py"
| xargs grep -l "subprocess|os.system|exec(|eval("
| xargs grep -l "subprocess|os.system|exec(|eval("
undefinedfind ~/.local/lib/python*/site-packages/<compromised-package>/ -name "*.py"
| xargs grep -l "subprocess|os.system|exec(|eval("
| xargs grep -l "subprocess|os.system|exec(|eval("
undefinedStep 4: Notify Stakeholders
步骤4:通知相关人员
text
SUBJECT: [SECURITY INCIDENT] Compromised dependency: <package-name>
SEVERITY: Critical
IMPACT: <package-name> versions <affected-range> contain malicious code.
AFFECTED SYSTEMS: <list of repos/services consuming this dependency>
STATUS: Contained -- pinned to safe version <safe-version>
ACTIONS TAKEN:
1. Pinned all repositories to last known-good version
2. Initiated audit of all systems that pulled affected versions
3. Scanning for indicators of compromise
RECOMMENDED ACTIONS:
- Do NOT deploy any build that consumed affected versions
- Review CI/CD logs for the timeframe <start> to <end>
- Rotate any secrets that were accessible to the build environmenttext
主题:[安全事件] 已攻陷依赖项: <package-name>
严重程度:Critical
影响范围:<package-name>版本<affected-range>包含恶意代码。
受影响系统:<使用该依赖项的仓库/服务列表>
状态:已遏制 -- 固定到安全版本<safe-version>
已采取行动:
1. 将所有仓库固定到最后一个已知安全的版本
2. 启动对所有拉取过受影响版本的系统的审计
3. 扫描入侵指标
建议行动:
- 不要部署任何使用过受影响版本的构建产物
- 审查<start>至<end>时间段内的CI/CD日志
- 轮换构建环境可访问的所有密钥Step 5: Replace or Fork
步骤5:替换或复刻
bash
undefinedbash
undefinedIf the package maintainer account was compromised, fork the last safe version
如果包维护者账户被攻陷,复刻最后一个安全版本
git clone https://github.com/original-author/<package>.git
cd <package>
git checkout v<safe-version>
git clone https://github.com/original-author/<package>.git
cd <package>
git checkout v<safe-version>
Publish to your private registry or vendor directly
发布到你的私有注册表或直接作为供应商包使用
For npm, point to your fork via package.json
对于npm,在package.json中指向你的复刻版本
"dependencies": { "<package>": "git+https://github.com/yourorg/<package>.git#v1.2.3" }
"dependencies": { "<package>": "git+https://github.com/yourorg/<package>.git#v1.2.3" }
---
---4. Lockfile Auditing
4. 锁文件审计
Lockfiles are your first line of defense. Tampered or inconsistent lockfiles indicate something is wrong.
锁文件是你的第一道防线。被篡改或不一致的锁文件表明存在问题。
4.1 Verify Lockfile Integrity
4.1 验证锁文件完整性
bash
undefinedbash
undefinednpm: ensure lockfile matches package.json (fails CI if out of sync)
npm: 确保锁文件与package.json匹配(如果不同步,CI会失败)
npm ci
npm ci
Yarn: check lockfile integrity
Yarn: 检查锁文件完整性
yarn install --frozen-lockfile
yarn install --frozen-lockfile
pip: generate a hash-locked requirements file
pip: 生成带哈希锁定的requirements文件
pip-compile --generate-hashes requirements.in -o requirements.txt
pip-compile --generate-hashes requirements.in -o requirements.txt
Verify no unexpected changes in lockfile during PR
在PR中检查锁文件是否有意外变更
git diff --name-only origin/main...HEAD | grep -E "(package-lock|yarn.lock|Cargo.lock|requirements.txt)"
undefinedgit diff --name-only origin/main...HEAD | grep -E "(package-lock|yarn.lock|Cargo.lock|requirements.txt)"
undefined4.2 Detect Typosquatting
4.2 检测仿冒包(Typosquatting)
bash
undefinedbash
undefinedUse the socket CLI to check for typosquatting risk
使用socket CLI检查仿冒风险
npx socket scan --package-lock package-lock.json
npx socket scan --package-lock package-lock.json
Python: check package names against popular packages
Python: 对照热门包检查包名
pip-audit -r requirements.txt 2>&1 | grep -i "typosquat"
pip-audit -r requirements.txt 2>&1 | grep -i "typosquat"
Custom check: compare package names to known popular packages
自定义检查:将包名与已知热门包对比
Flag anything with edit distance <= 2 from a top-1000 package
标记与前1000个热门包编辑距离<=2的包
python3 -c "
import json, sys
from difflib import SequenceMatcher
with open('package-lock.json') as f:
lock = json.load(f)
popular = ['express','lodash','react','axios','chalk','debug','commander','inquirer']
for pkg in lock.get('packages', {}):
name = pkg.split('node_modules/')[-1] if 'node_modules/' in pkg else pkg
for p in popular:
ratio = SequenceMatcher(None, name, p).ratio()
if 0.75 < ratio < 1.0 and name != p:
print(f'WARNING: {name} is suspiciously similar to {p} (similarity: {ratio:.2f})')
"
undefinedpython3 -c "
import json, sys
from difflib import SequenceMatcher
with open('package-lock.json') as f:
lock = json.load(f)
popular = ['express','lodash','react','axios','chalk','debug','commander','inquirer']
for pkg in lock.get('packages', {}):
name = pkg.split('node_modules/')[-1] if 'node_modules/' in pkg else pkg
for p in popular:
ratio = SequenceMatcher(None, name, p).ratio()
if 0.75 < ratio < 1.0 and name != p:
print(f'WARNING: {name} is suspiciously similar to {p} (similarity: {ratio:.2f})')
"
undefined4.3 Lockfile Diff in CI
4.3 CI中的锁文件差异检查
yaml
undefinedyaml
undefined.github/workflows/lockfile-check.yml
.github/workflows/lockfile-check.yml
name: Lockfile Audit
on: pull_request
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check for lockfile changes
run: |
LOCKFILES="package-lock.json yarn.lock pnpm-lock.yaml Cargo.lock requirements.txt poetry.lock"
for f in $LOCKFILES; do
if git diff --name-only origin/main...HEAD | grep -q "$f"; then
echo "::warning::Lockfile $f was modified -- review dependency changes carefully"
git diff origin/main...HEAD -- "$f" | head -100
fi
done
- name: Run npm audit
if: hashFiles('package-lock.json') != ''
run: npm audit --audit-level=high
---name: Lockfile Audit
on: pull_request
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check for lockfile changes
run: |
LOCKFILES="package-lock.json yarn.lock pnpm-lock.yaml Cargo.lock requirements.txt poetry.lock"
for f in $LOCKFILES; do
if git diff --name-only origin/main...HEAD | grep -q "$f"; then
echo "::warning::Lockfile $f was modified -- review dependency changes carefully"
git diff origin/main...HEAD -- "$f" | head -100
fi
done
- name: Run npm audit
if: hashFiles('package-lock.json') != ''
run: npm audit --audit-level=high
---5. Package Pinning and Verification
5. 包固定与验证
5.1 pip Hash Checking
5.1 pip哈希检查
text
undefinedtext
undefinedrequirements.txt with hashes (generated by pip-compile --generate-hashes)
带哈希的requirements.txt(由pip-compile --generate-hashes生成)
requests==2.31.0
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003eb
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7f0edf3fcb0fce8afe0f44546e1
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003eb
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7f0edf3fcb0fce8afe0f44546e1
```bashrequests==2.31.0
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003eb
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7f0edf3fcb0fce8afe0f44546e1
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003eb
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7f0edf3fcb0fce8afe0f44546e1
```bashInstall with mandatory hash verification
强制使用哈希验证进行安装
pip install --require-hashes -r requirements.txt
pip install --require-hashes -r requirements.txt
Generate hashes for existing requirements
为现有依赖项生成哈希
pip-compile --generate-hashes requirements.in
undefinedpip-compile --generate-hashes requirements.in
undefined5.2 npm Package Integrity
5.2 npm包完整性
bash
undefinedbash
undefinednpm automatically verifies integrity hashes in package-lock.json
npm会自动验证package-lock.json中的完整性哈希
Ensure your lockfile contains integrity fields:
确保你的锁文件包含integrity字段:
cat package-lock.json | jq '.packages | to_entries[] | select(.value.integrity == null) | .key'
cat package-lock.json | jq '.packages | to_entries[] | select(.value.integrity == null) | .key'
Enable strict engine and audit checks in .npmrc
在.npmrc中启用严格引擎和审计检查
cat >> .npmrc << 'EOF'
engine-strict=true
audit=true
audit-level=high
EOF
undefinedcat >> .npmrc << 'EOF'
engine-strict=true
audit=true
audit-level=high
EOF
undefined5.3 cargo-vet for Rust
5.3 Rust的cargo-vet
bash
undefinedbash
undefinedInstall cargo-vet
安装cargo-vet
cargo install cargo-vet
cargo install cargo-vet
Initialize in your project
在项目中初始化
cargo vet init
cargo vet init
Certify a crate after review
审核后认证一个crate
cargo vet certify serde 1.0.193
cargo vet certify serde 1.0.193
Import audit results from trusted organizations
导入可信组织的审计结果
cargo vet trust --all mozilla
cargo vet trust --all google
cargo vet trust --all mozilla
cargo vet trust --all google
Run verification in CI
在CI中运行验证
cargo vet check
---cargo vet check
---6. Container Image Verification
6. 容器镜像验证
6.1 Cosign Sign and Verify
6.1 Cosign签名与验证
bash
undefinedbash
undefinedSign an image (keyless via Sigstore/Fulcio in CI)
签名镜像(在CI中通过Sigstore/Fulcio实现无密钥签名)
cosign sign ghcr.io/myorg/myimage@sha256:abc123...
cosign sign ghcr.io/myorg/myimage@sha256:abc123...
Verify with expected identity
使用预期身份验证
cosign verify
--certificate-identity-regexp "https://github.com/myorg/.*"
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
ghcr.io/myorg/myimage:latest
--certificate-identity-regexp "https://github.com/myorg/.*"
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
ghcr.io/myorg/myimage:latest
cosign verify
--certificate-identity-regexp "https://github.com/myorg/.*"
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
ghcr.io/myorg/myimage:latest
--certificate-identity-regexp "https://github.com/myorg/.*"
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
ghcr.io/myorg/myimage:latest
Verify and extract attestations
验证并提取声明
cosign verify-attestation
--type slsaprovenance
--certificate-identity-regexp "https://github.com/myorg/.*"
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
ghcr.io/myorg/myimage:latest | jq '.payload' | base64 -d | jq .
--type slsaprovenance
--certificate-identity-regexp "https://github.com/myorg/.*"
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
ghcr.io/myorg/myimage:latest | jq '.payload' | base64 -d | jq .
undefinedcosign verify-attestation
--type slsaprovenance
--certificate-identity-regexp "https://github.com/myorg/.*"
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
ghcr.io/myorg/myimage:latest | jq '.payload' | base64 -d | jq .
--type slsaprovenance
--certificate-identity-regexp "https://github.com/myorg/.*"
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
ghcr.io/myorg/myimage:latest | jq '.payload' | base64 -d | jq .
undefined6.2 Kyverno Policy -- Require Signed Images
6.2 Kyverno策略 -- 要求签名镜像
yaml
undefinedyaml
undefinedkyverno-require-signed-images.yaml
kyverno-require-signed-images.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-signed-images
spec:
validationFailureAction: Enforce
background: false
rules:
- name: verify-image-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "ghcr.io/myorg/"
attestors:
- entries:
- keyless:
subject: "https://github.com/myorg/"
issuer: "https://token.actions.githubusercontent.com"
rekor:
url: https://rekor.sigstore.dev
```bashapiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-signed-images
spec:
validationFailureAction: Enforce
background: false
rules:
- name: verify-image-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "ghcr.io/myorg/"
attestors:
- entries:
- keyless:
subject: "https://github.com/myorg/"
issuer: "https://token.actions.githubusercontent.com"
rekor:
url: https://rekor.sigstore.dev
```bashApply the policy
应用策略
kubectl apply -f kyverno-require-signed-images.yaml
kubectl apply -f kyverno-require-signed-images.yaml
Test: this unsigned image should be rejected
测试:这个未签名的镜像应该被拒绝
kubectl run test --image=ghcr.io/myorg/unsigned-image:latest
kubectl run test --image=ghcr.io/myorg/unsigned-image:latest
Expected: admission webhook denies the request
预期结果:准入webhook拒绝请求
---
---7. CI/CD Pipeline Hardening
7. CI/CD流水线加固
7.1 Pin GitHub Actions by SHA
7.1 通过SHA固定GitHub Actions
yaml
undefinedyaml
undefinedBAD: mutable tag, can be hijacked
不推荐:可变标签,可能被劫持
- uses: actions/checkout@v4
- uses: actions/checkout@v4
GOOD: pinned to exact commit SHA
推荐:固定到确切的提交SHA
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
```bash- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
```bashUse pin-github-action to automate pinning
使用pin-github-action自动完成固定
npm install -g pin-github-action
pin-github-action .github/workflows/*.yml
undefinednpm install -g pin-github-action
pin-github-action .github/workflows/*.yml
undefined7.2 Isolated Runners
7.2 隔离运行器
yaml
undefinedyaml
undefinedUse ephemeral self-hosted runners that are destroyed after each job
使用临时自托管运行器,作业完成后销毁
jobs:
build:
runs-on: self-hosted
container:
image: ghcr.io/myorg/build-env:latest@sha256:abc123...
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- name: Build in isolated container
run: |
# No access to host filesystem or network beyond what's needed
make build
undefinedjobs:
build:
runs-on: self-hosted
container:
image: ghcr.io/myorg/build-env:latest@sha256:abc123...
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- name: Build in isolated container
run: |
# 仅访问所需的主机文件系统或网络
make build
undefined7.3 OIDC for Cloud Authentication (No Long-Lived Secrets)
7.3 用于云认证的OIDC(无长期密钥)
yaml
undefinedyaml
undefinedGitHub Actions OIDC with AWS -- no static credentials stored
GitHub Actions与AWS的OIDC集成 -- 不存储静态凭证
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy
aws-region: us-east-1
- run: aws s3 cp build/ s3://my-bucket/ --recursive
undefinedjobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy
aws-region: us-east-1
- run: aws s3 cp build/ s3://my-bucket/ --recursive
undefined7.4 Restrict Workflow Permissions
7.4 限制工作流权限
yaml
undefinedyaml
undefinedAt the top of every workflow, use least-privilege permissions
在每个工作流顶部,使用最小权限原则
permissions:
contents: read
packages: read
permissions:
contents: read
packages: read
Never grant write permissions globally; scope them per job
不要全局授予写入权限;按作业分配权限
jobs:
publish:
permissions:
contents: read
packages: write
---jobs:
publish:
permissions:
contents: read
packages: write
---8. SLSA Framework Implementation
8. SLSA框架实施
8.1 SLSA Levels Overview
8.1 SLSA级别概述
| Level | Requirement |
|---|---|
| SLSA 1 | Build process is documented and generates provenance |
| SLSA 2 | Provenance is generated by a hosted build service and is authenticated |
| SLSA 3 | Build platform is hardened, provenance is non-falsifiable |
| 级别 | 要求 |
|---|---|
| SLSA 1 | 记录构建流程并生成来源信息 |
| SLSA 2 | 由托管构建服务生成来源信息并进行身份验证 |
| SLSA 3 | 构建平台已加固,来源信息不可篡改 |
8.2 SLSA Level 1 -- Generate Provenance
8.2 SLSA级别1 -- 生成来源信息
yaml
undefinedyaml
undefined.github/workflows/slsa-build.yml
.github/workflows/slsa-build.yml
name: SLSA Build
on:
push:
tags: ["v*"]
jobs:
build:
runs-on: ubuntu-latest
outputs:
digest: ${{ steps.hash.outputs.digest }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- name: Build artifact
run: |
make build
cp dist/my-binary ./my-binary
- name: Generate digest
id: hash
run: |
DIGEST=$(sha256sum my-binary | cut -d ' ' -f1)
echo "digest=$DIGEST" >> "$GITHUB_OUTPUT"
- uses: actions/upload-artifact@v4
with:
name: my-binary
path: my-binary
undefinedname: SLSA Build
on:
push:
tags: ["v*"]
jobs:
build:
runs-on: ubuntu-latest
outputs:
digest: ${{ steps.hash.outputs.digest }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- name: Build artifact
run: |
make build
cp dist/my-binary ./my-binary
- name: Generate digest
id: hash
run: |
DIGEST=$(sha256sum my-binary | cut -d ' ' -f1)
echo "digest=$DIGEST" >> "$GITHUB_OUTPUT"
- uses: actions/upload-artifact@v4
with:
name: my-binary
path: my-binary
undefined8.3 SLSA Level 2-3 -- Use the SLSA GitHub Generator
8.3 SLSA级别2-3 -- 使用SLSA GitHub生成器
yaml
provenance:
needs: build
permissions:
actions: read
id-token: write
contents: write
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
with:
base64-subjects: |
${{ needs.build.outputs.digest }} my-binary
upload-assets: trueyaml
provenance:
needs: build
permissions:
actions: read
id-token: write
contents: write
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
with:
base64-subjects: |
${{ needs.build.outputs.digest }} my-binary
upload-assets: true8.4 Verify SLSA Provenance
8.4 验证SLSA来源信息
bash
undefinedbash
undefinedDownload the provenance and binary from the release
从版本发布中下载来源信息和二进制文件
gh release download v1.2.3 -p "my-binary" -p "my-binary.intoto.jsonl"
gh release download v1.2.3 -p "my-binary" -p "my-binary.intoto.jsonl"
Verify
验证
slsa-verifier verify-artifact my-binary
--provenance-path my-binary.intoto.jsonl
--source-uri github.com/myorg/myrepo
--source-tag v1.2.3
--provenance-path my-binary.intoto.jsonl
--source-uri github.com/myorg/myrepo
--source-tag v1.2.3
echo $? # 0 = verified successfully
---slsa-verifier verify-artifact my-binary
--provenance-path my-binary.intoto.jsonl
--source-uri github.com/myorg/myrepo
--source-tag v1.2.3
--provenance-path my-binary.intoto.jsonl
--source-uri github.com/myorg/myrepo
--source-tag v1.2.3
echo $? # 0 = 验证成功
---9. Dependency Firewall
9. 依赖防火墙
9.1 Artifactory Remote Repository with Allow List
9.1 带允许列表的Artifactory远程仓库
yaml
undefinedyaml
undefinedartifactory-remote-npm.yaml
artifactory-remote-npm.yaml
apiVersion: v1
kind: RemoteRepository
metadata:
name: npm-remote
spec:
packageType: npm
url: https://registry.npmjs.org
includesPattern: |
express/**
lodash/**
react/**
@types/**
excludesPattern: |
malicious
typosquat
xrayIndex: true
blockMismatchingMimeTypes: true
enableTokenAuthentication: true
undefinedapiVersion: v1
kind: RemoteRepository
metadata:
name: npm-remote
spec:
packageType: npm
url: https://registry.npmjs.org
includesPattern: |
express/**
lodash/**
react/**
@types/**
excludesPattern: |
malicious
typosquat
xrayIndex: true
blockMismatchingMimeTypes: true
enableTokenAuthentication: true
undefined9.2 Nexus Repository Firewall Rules
9.2 Nexus仓库防火墙规则
bash
undefinedbash
undefinedEnable Nexus Firewall audit on a proxy repository
在代理仓库上启用Nexus Firewall审计
curl -u admin:$NEXUS_PASSWORD -X PUT
"https://nexus.internal/service/rest/v1/security/content-selectors"
-H "Content-Type: application/json"
-d '{ "name": "block-suspicious-pypi", "description": "Block packages with no maintainer history", "expression": "format == "pypi" and coordinate.age < 7" }'
"https://nexus.internal/service/rest/v1/security/content-selectors"
-H "Content-Type: application/json"
-d '{ "name": "block-suspicious-pypi", "description": "Block packages with no maintainer history", "expression": "format == "pypi" and coordinate.age < 7" }'
undefinedcurl -u admin:$NEXUS_PASSWORD -X PUT
"https://nexus.internal/service/rest/v1/security/content-selectors"
-H "Content-Type: application/json"
-d '{ "name": "block-suspicious-pypi", "description": "Block packages with no maintainer history", "expression": "format == "pypi" and coordinate.age < 7" }'
"https://nexus.internal/service/rest/v1/security/content-selectors"
-H "Content-Type: application/json"
-d '{ "name": "block-suspicious-pypi", "description": "Block packages with no maintainer history", "expression": "format == "pypi" and coordinate.age < 7" }'
undefined9.3 Verdaccio Private npm Registry
9.3 Verdaccio私有npm注册表
yaml
undefinedyaml
undefinedverdaccio config.yaml
verdaccio config.yaml
storage: /verdaccio/storage
uplinks:
npmjs:
url: https://registry.npmjs.org/
cache: true
maxage: 30m
packages:
'@myorg/*':
access: $authenticated
publish: $authenticated
proxy: [] # never proxy internal packages
'**':
access: $authenticated
publish: $deny # block publishing public package names
proxy: npmjs
Block known malicious packages
'event-stream':
access: $deny
publish: $deny
---storage: /verdaccio/storage
uplinks:
npmjs:
url: https://registry.npmjs.org/
cache: true
maxage: 30m
packages:
'@myorg/*':
access: $authenticated
publish: $authenticated
proxy: [] # 绝不代理内部包
'**':
access: $authenticated
publish: $deny # 阻止发布公共包名
proxy: npmjs
阻止已知恶意包
'event-stream':
access: $deny
publish: $deny
---10. Monitoring and Alerting
10. 监控与告警
10.1 Detect New Dependencies in Pull Requests
10.1 检测Pull Request中的新依赖项
yaml
undefinedyaml
undefined.github/workflows/dependency-review.yml
.github/workflows/dependency-review.yml
name: Dependency Review
on: pull_request
permissions:
contents: read
pull-requests: write
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: actions/dependency-review-action@4901385134134e04cec5fbe5ddfe3b2c5bd5d976 # v4
with:
fail-on-severity: high
deny-licenses: GPL-3.0, AGPL-3.0
comment-summary-in-pr: always
warn-only: false
undefinedname: Dependency Review
on: pull_request
permissions:
contents: read
pull-requests: write
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: actions/dependency-review-action@4901385134134e04cec5fbe5ddfe3b2c5bd5d976 # v4
with:
fail-on-severity: high
deny-licenses: GPL-3.0, AGPL-3.0
comment-summary-in-pr: always
warn-only: false
undefined10.2 OSV.dev Integration
10.2 OSV.dev集成
bash
undefinedbash
undefinedInstall osv-scanner
安装osv-scanner
go install github.com/google/osv-scanner/cmd/osv-scanner@latest
go install github.com/google/osv-scanner/cmd/osv-scanner@latest
Scan a project directory (auto-detects lockfiles)
扫描项目目录(自动检测锁文件)
osv-scanner -r /path/to/project
osv-scanner -r /path/to/project
Scan a specific lockfile
扫描特定锁文件
osv-scanner --lockfile=package-lock.json
osv-scanner --lockfile=package-lock.json
Scan a Docker image
扫描Docker镜像
osv-scanner --docker myimage:latest
osv-scanner --docker myimage:latest
Output as JSON for CI processing
输出JSON格式结果用于CI处理
osv-scanner -r /path/to/project --format json | jq '.results[].packages[].vulnerabilities[] | .id'
undefinedosv-scanner -r /path/to/project --format json | jq '.results[].packages[].vulnerabilities[] | .id'
undefined10.3 Dependabot Configuration
10.3 Dependabot配置
yaml
undefinedyaml
undefined.github/dependabot.yml
.github/dependabot.yml
version: 2
updates:
-
package-ecosystem: "npm" directory: "/" schedule: interval: "daily" open-pull-requests-limit: 10 reviewers:
- "security-team" labels:
- "dependencies"
- "security"
Group minor/patch updates but keep major separate for review
groups: production-dependencies: dependency-type: "production" update-types: ["minor", "patch"] dev-dependencies: dependency-type: "development" update-types: ["minor", "patch"] -
package-ecosystem: "pip" directory: "/" schedule: interval: "daily"
-
package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly"
undefinedversion: 2
updates:
-
package-ecosystem: "npm" directory: "/" schedule: interval: "daily" open-pull-requests-limit: 10 reviewers:
- "security-team" labels:
- "dependencies"
- "security"
合并次要/补丁更新,但将主要更新单独保留以便审查
groups: production-dependencies: dependency-type: "production" update-types: ["minor", "patch"] dev-dependencies: dependency-type: "development" update-types: ["minor", "patch"] -
package-ecosystem: "pip" directory: "/" schedule: interval: "daily"
-
package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly"
undefined10.4 Custom Webhook Alert for New Dependencies
10.4 新依赖项的自定义Webhook告警
bash
#!/usr/bin/env bashbash
#!/usr/bin/env bashalert-new-deps.sh -- run in CI on PRs to detect newly added dependencies
alert-new-deps.sh -- 在CI的PR中运行,检测新增的依赖项
set -euo pipefail
BASE_BRANCH="${1:-origin/main}"
LOCKFILE="package-lock.json"
NEW_DEPS=$(diff <(git show "$BASE_BRANCH:$LOCKFILE" 2>/dev/null | jq -r '.packages | keys[]' | sort)
<(jq -r '.packages | keys[]' "$LOCKFILE" | sort)
| grep "^>" | sed 's/^> //' || true)
<(jq -r '.packages | keys[]' "$LOCKFILE" | sort)
| grep "^>" | sed 's/^> //' || true)
if [ -n "$NEW_DEPS" ]; then
echo "New dependencies detected:"
echo "$NEW_DEPS"
Send to Slack
curl -s -X POST "$SLACK_WEBHOOK_URL"
-H "Content-Type: application/json"
-d "{ "text": "New dependencies added in PR #${PR_NUMBER}:\n```${NEW_DEPS}```", "channel": "#security-alerts" }" fi
-H "Content-Type: application/json"
-d "{ "text": "New dependencies added in PR #${PR_NUMBER}:\n```${NEW_DEPS}```", "channel": "#security-alerts" }" fi
---set -euo pipefail
BASE_BRANCH="${1:-origin/main}"
LOCKFILE="package-lock.json"
NEW_DEPS=$(diff <(git show "$BASE_BRANCH:$LOCKFILE" 2>/dev/null | jq -r '.packages | keys[]' | sort)
<(jq -r '.packages | keys[]' "$LOCKFILE" | sort)
| grep "^>" | sed 's/^> //' || true)
<(jq -r '.packages | keys[]' "$LOCKFILE" | sort)
| grep "^>" | sed 's/^> //' || true)
if [ -n "$NEW_DEPS" ]; then
echo "New dependencies detected:"
echo "$NEW_DEPS"
发送到Slack
curl -s -X POST "$SLACK_WEBHOOK_URL"
-H "Content-Type: application/json"
-d "{ "text": "New dependencies added in PR #${PR_NUMBER}:\n```${NEW_DEPS}```", "channel": "#security-alerts" }" fi
-H "Content-Type: application/json"
-d "{ "text": "New dependencies added in PR #${PR_NUMBER}:\n```${NEW_DEPS}```", "channel": "#security-alerts" }" fi
---11. Post-Incident Response
11. 事件后响应
11.1 Forensics Checklist
11.1 取证清单
text
[ ] Identify the exact compromised package version(s)
[ ] Determine the time window of exposure (first install to detection)
[ ] List all repositories and services that consumed the package
[ ] Check CI/CD build logs for the exposure window
[ ] Inspect runtime logs for outbound connections to unknown hosts
[ ] Review process execution logs for unexpected child processes
[ ] Check for modifications to other files in node_modules/site-packages
[ ] Verify no additional packages were installed as transitive deps
[ ] Dump and analyze DNS query logs for the exposure period
[ ] Check for new cron jobs, systemd services, or scheduled tasks
[ ] Audit all secrets/tokens that were accessible to the build environmenttext
[ ] 确定确切的已攻陷包版本
[ ] 确定暴露时间窗口(从首次安装到检测到攻击)
[ ] 列出所有使用该包的仓库和服务
[ ] 检查暴露时间窗口内的CI/CD构建日志
[ ] 检查运行时日志中是否有指向未知主机的出站连接
[ ] 审查进程执行日志中是否有意外的子进程
[ ] 检查node_modules/site-packages中其他文件是否被修改
[ ] 验证是否没有额外的包作为传递依赖被安装
[ ] 导出并分析暴露期间的DNS查询日志
[ ] 检查是否有新的cron任务、systemd服务或计划任务
[ ] 审计构建环境可访问的所有密钥/令牌11.2 Blast Radius Assessment
11.2 影响范围评估
bash
#!/usr/bin/env bashbash
#!/usr/bin/env bashblast-radius.sh -- assess how widely a compromised package spread
blast-radius.sh -- 评估已攻陷包的传播范围
set -euo pipefail
COMPROMISED_PKG="$1"
COMPROMISED_VERSIONS="$2" # comma-separated, e.g., "1.2.3,1.2.4"
echo "=== Blast Radius Assessment for $COMPROMISED_PKG ==="
set -euo pipefail
COMPROMISED_PKG="$1"
COMPROMISED_VERSIONS="$2" # 逗号分隔,例如 "1.2.3,1.2.4"
echo "=== Blast Radius Assessment for $COMPROMISED_PKG ==="
Check all repos in the org
检查组织内的所有仓库
for repo in $(gh repo list myorg --json name -q '.[].name'); do
echo "--- Checking $repo ---"
Check package-lock.json
LOCK=$(gh api "repos/myorg/$repo/contents/package-lock.json"
--jq '.content' 2>/dev/null | base64 -d 2>/dev/null || true)
--jq '.content' 2>/dev/null | base64 -d 2>/dev/null || true)
if echo "$LOCK" | grep -q ""$COMPROMISED_PKG""; then
VERSION=$(echo "$LOCK" | jq -r ".packages["node_modules/$COMPROMISED_PKG"].version // empty")
if echo "$COMPROMISED_VERSIONS" | grep -q "$VERSION"; then
echo "AFFECTED: $repo uses $COMPROMISED_PKG@$VERSION"
fi
fi
done
undefinedfor repo in $(gh repo list myorg --json name -q '.[].name'); do
echo "--- Checking $repo ---"
检查package-lock.json
LOCK=$(gh api "repos/myorg/$repo/contents/package-lock.json"
--jq '.content' 2>/dev/null | base64 -d 2>/dev/null || true)
--jq '.content' 2>/dev/null | base64 -d 2>/dev/null || true)
if echo "$LOCK" | grep -q ""$COMPROMISED_PKG""; then
VERSION=$(echo "$LOCK" | jq -r ".packages["node_modules/$COMPROMISED_PKG"].version // empty")
if echo "$COMPROMISED_VERSIONS" | grep -q "$VERSION"; then
echo "AFFECTED: $repo uses $COMPROMISED_PKG@$VERSION"
fi
fi
done
undefined11.3 Secret Rotation After Compromise
11.3 攻陷后的密钥轮换
bash
undefinedbash
undefinedRotate all secrets that were accessible during the exposure window
轮换暴露时间窗口内可访问的所有密钥
1. Rotate cloud provider credentials
1. 轮换云提供商凭证
aws iam create-access-key --user-name ci-deploy
aws iam delete-access-key --user-name ci-deploy --access-key-id OLD_KEY_ID
aws iam create-access-key --user-name ci-deploy
aws iam delete-access-key --user-name ci-deploy --access-key-id OLD_KEY_ID
2. Rotate GitHub tokens
2. 轮换GitHub令牌
gh auth refresh
gh auth refresh
3. Rotate database credentials
3. 轮换数据库凭证
kubectl create secret generic db-credentials
--from-literal=password="$(openssl rand -base64 32)"
--dry-run=client -o yaml | kubectl apply -f -
--from-literal=password="$(openssl rand -base64 32)"
--dry-run=client -o yaml | kubectl apply -f -
kubectl create secret generic db-credentials
--from-literal=password="$(openssl rand -base64 32)"
--dry-run=client -o yaml | kubectl apply -f -
--from-literal=password="$(openssl rand -base64 32)"
--dry-run=client -o yaml | kubectl apply -f -
4. Rotate npm/PyPI publish tokens
4. 轮换npm/PyPI发布令牌
npm token revoke <old-token>
npm token create --read-only
npm token revoke <old-token>
npm token create --read-only
5. Invalidate all active sessions/JWTs
5. 使所有活动会话/JWT失效
Application-specific -- trigger a key rotation in your auth service
特定于应用程序 -- 在你的认证服务中触发密钥轮换
undefinedundefined11.4 Communication Templates
11.4 沟通模板
text
--- INTERNAL INCIDENT REPORT ---
Incident ID: SC-YYYY-NNN
Date Detected: YYYY-MM-DD HH:MM UTC
Package: <name>@<version>
Registry: npm / PyPI / crates.io
Advisory: <link to CVE or advisory>
Timeline:
- YYYY-MM-DD HH:MM: Compromised version published to registry
- YYYY-MM-DD HH:MM: First installation in our environment (from CI logs)
- YYYY-MM-DD HH:MM: Compromise detected via <audit tool / advisory / manual review>
- YYYY-MM-DD HH:MM: Pinned to safe version across all repos
- YYYY-MM-DD HH:MM: Completed IOC scan -- no evidence of exploitation
- YYYY-MM-DD HH:MM: All exposed secrets rotated
Blast Radius:
- Repositories affected: N
- Production deployments with compromised version: N
- Secrets potentially exposed: <list>
Root Cause:
<Maintainer account takeover / malicious maintainer / build system compromise>
Remediation:
1. Pinned to safe version
2. Rotated all potentially exposed secrets
3. Deployed clean builds to production
4. Added package to monitoring watch list
Preventive Measures:
1. Enabled hash-pinning for all dependencies
2. Added dependency-review-action to all repos
3. Configured Artifactory proxy with allowlist
4. Scheduled quarterly supply chain auditstext
--- 内部事件报告 ---
事件ID: SC-YYYY-NNN
检测日期: YYYY-MM-DD HH:MM UTC
包: <name>@<version>
注册表: npm / PyPI / crates.io
公告链接: <CVE或公告链接>
时间线:
- YYYY-MM-DD HH:MM: 已攻陷版本发布到注册表
- YYYY-MM-DD HH:MM: 我们环境中首次安装(来自CI日志)
- YYYY-MM-DD HH:MM: 通过<审计工具/公告/人工审查>检测到攻陷
- YYYY-MM-DD HH:MM: 在所有仓库中固定到安全版本
- YYYY-MM-DD HH:MM: 完成IOC扫描 -- 无被利用迹象
- YYYY-MM-DD HH:MM: 所有暴露的密钥已轮换
影响范围:
- 受影响仓库数量: N
- 使用已攻陷版本的生产部署数量: N
- 可能暴露的密钥: <列表>
根本原因:
<维护者账户被接管/恶意维护者/构建系统被攻陷>
修复措施:
1. 固定到安全版本
2. 轮换所有可能暴露的密钥
3. 向生产环境部署干净的构建产物
4. 将该包添加到监控观察列表
预防措施:
1. 为所有依赖项启用哈希固定
2. 为所有仓库添加dependency-review-action
3. 配置带允许列表的Artifactory代理
4. 安排季度供应链审计Quick Reference
快速参考
| Task | Command |
|---|---|
| Audit npm | |
| Audit pip | |
| Audit cargo | |
| Scan with OSV | |
| Verify cosign signature | |
| Verify SLSA provenance | |
| Pin GitHub Actions | |
| Check lockfile drift | |
| Generate pip hashes | |
| Cargo vet check | |
| 任务 | 命令 |
|---|---|
| 审计npm | |
| 审计pip | |
| 审计cargo | |
| 使用OSV扫描 | |
| 验证cosign签名 | |
| 验证SLSA来源信息 | |
| 固定GitHub Actions | |
| 检查锁文件漂移 | |
| 生成pip哈希 | |
| Cargo vet检查 | |