npm audit --json | python3 -c "
import json, sys
data = json.load(sys.stdin)
vulns = data.get('vulnerabilities', {})
for name, info in vulns.items():
severity = info.get('severity', 'unknown')
via = info.get('via', [])
fix_available = info.get('fixAvailable', False)
print(f'[{severity.upper()}] {name}')
for v in via:
if isinstance(v, dict):
print(f' Title: {v.get(\"title\", \"N/A\")}')
print(f' URL: {v.get(\"url\", \"N/A\")}')
print(f' Fix Available: {fix_available}')
print()
"
pip-audit --format json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for dep in data.get('dependencies', []):
name = dep.get('name', '')
version = dep.get('version', '')
vulns = dep.get('vulns', [])
if vulns:
print(f'[VULNERABLE] {name}=={version}')
for v in vulns:
print(f' ID: {v.get(\"id\", \"N/A\")}')
print(f' Description: {v.get(\"description\", \"N/A\")[:80]}')
"
for commit in $(git log --all --oneline -- '.github/workflows/' | head -10 | awk '{print $1}'); do echo "=== Commit: $commit ===" git show --stat $commit -- '.github/workflows/' git show $commit -- '.github/workflows/' | head -50
echo ""done
git log --all --oneline -- '.github/workflows/' | while read hash msg; do echo "--- $hash: $msg ---" git show $hash -- '.github/workflows/' 2>/dev/null | grep -E '^\+.*uses:|^\+.*run:|^\+.*secrets\.' | head -20
done
Jenkins/Travis CI/GitLab CI 构建环境入侵
CI/CD 平台
配置文件路径
高风险检查点
取证命令
GitHub Actions
.github/workflows/*.yml
uses: 引用、secrets.* 访问
git log -p -- .github/workflows/
Jenkins
Jenkinsfile / 构建目录
脚本步骤、凭据使用
查看 config.xml 和构建历史
Travis CI
.travis.yml
script 和 before_install 钩子
git log -p -- .travis.yml
GitLab CI
.gitlab-ci.yml
script 和 services 定义
git log -p -- .gitlab-ci.yml
CircleCI
.circleci/config.yml
commands 和 jobs 定义
git log -p -- .circleci/
构建日志与 artifact 分析
if command -v gh &> /dev/null; then gh run list --limit 10for run_id in $(gh run list --limit 5 --json databaseId -q '.[].databaseId'); do echo "=== Run ID: $run_id ===" gh run view $run_id --log 2>/dev/null | grep -iE 'upload|download|secret|token|curl|wget|eval|exec|base64|reverse' | head -20
donefi
title: GitHub Actions Workflow Modifiedid: 6b2c3d4e-5f6a-7b8c-9d0e-1f2a3b4c5d6estatus: experimentaldescription: Detects modifications to GitHub Actions workflow filesreferences:
- https://attack.mitre.org/techniques/T1195/002/author: x7peepsdate: 2026/07/05tags:
- attack.supply_chain - attack.t1195.002logsource:
category: file_eventproduct: linuxdetection:
selection_workflow_create:
TargetFilename|contains|all:
- '.github/workflows/' - '.yml'selection_workflow_modify:
TargetFilename|contains|all:
- '.github/workflows/'filter_legitimate:
TargetFilename|endswith:
- '.github/workflows/ci.yml'condition: selection_workflow_create or (selection_workflow_modify and not filter_legitimate)level: medium
Bash 脚本:自动化扫描
#!/bin/bash
SCAN_DIR="${1:-.}"REPORT_FILE="supply_chain_audit_$(date +%Y%m%d_%H%M%S).txt"echo "========================================" | tee "$REPORT_FILE"echo " Supply Chain Security Audit Report" | tee -a "$REPORT_FILE"echo " Scan Directory: $SCAN_DIR" | tee -a "$REPORT_FILE"echo " Scan Time: $(date)" | tee -a "$REPORT_FILE"echo "========================================" | tee -a "$REPORT_FILE"echo "" | tee -a "$REPORT_FILE"echo "[Phase 1] Checking package.json for suspicious scripts..." | tee -a "$REPORT_FILE"find "$SCAN_DIR" -name "package.json" -not -path "*/node_modules/*" | while read pkgfile; do echo "File: $pkgfile" | tee -a "$REPORT_FILE" python3 -c "
import json, sys
with open('$pkgfile') as f:
data = json.load(f)
scripts = data.get('scripts', {})
for hook, cmd in scripts.items():
if any(x in cmd for x in ['eval', 'exec', 'curl', 'wget', 'base64', 'child_process', 'http://', 'https://']):
print(f' [CRITICAL] {hook}: {cmd}')
elif any(x in cmd for x in ['require', 'import', 'spawn']):
print(f' [SUSPICIOUS] {hook}: {cmd}')
" 2>/dev/null | tee -a "$REPORT_FILE"doneecho "" | tee -a "$REPORT_FILE"echo "[Phase 2] Checking for URL-based dependencies..." | tee -a "$REPORT_FILE"find "$SCAN_DIR" -name "package.json" -not -path "*/node_modules/*" | while read pkgfile; do grep -E '"(http|git\+|git://|ssh://)'"$pkgfile" | while read line; do echo " [SUSPICIOUS] $pkgfile: $line" | tee -a "$REPORT_FILE"donedoneecho "" | tee -a "$REPORT_FILE"echo "[Phase 3] Checking for hidden GitHub workflows..." | tee -a "$REPORT_FILE"find "$SCAN_DIR" -path "*/.github/workflows/*.yml" -o -path "*/.github/workflows/*.yaml" | while read wf; do echo " Workflow: $wf" | tee -a "$REPORT_FILE" grep -n "uses:""$wf" | grep -vE "actions/|github/|peaceiris/|codecov/|coverallsapp/" | while read line; do echo " [SUSPICIOUS] Third-party action: $line" | tee -a "$REPORT_FILE"donedoneecho "" | tee -a "$REPORT_FILE"echo "[Phase 4] Checking requirements.txt for suspicious entries..." | tee -a "$REPORT_FILE"find "$SCAN_DIR" -name "requirements.txt" | while read reqfile; do grep -nE '(http://|https://|git\+|git://)'"$reqfile" | while read line; do echo " [SUSPICIOUS] $reqfile: $line" | tee -a "$REPORT_FILE"donedoneecho "" | tee -a "$REPORT_FILE"echo "[Phase 5] Git history analysis for workflow changes..." | tee -a "$REPORT_FILE"cd "$SCAN_DIR" 2>/dev/null && git log --all --oneline --diff-filter=ACMR -- '.github/workflows/' | head -20 | while read line; do echo " $line" | tee -a "$REPORT_FILE"doneecho "" | tee -a "$REPORT_FILE"echo "Audit Complete. Report saved to: $REPORT_FILE" | tee -a "$REPORT_FILE"
Python 脚本:SBOM 生成与漏洞匹配
import subprocess
import json
import sys
import os
defgenerate_sbom(target, output_format="cyclonedx-json"):
result = subprocess.run(
["syft", target, "-o", output_format],
capture_output=True, text=True, timeout=300 )
if result.returncode !=0:
print(f"[ERROR] SBOM generation failed: {result.stderr[:200]}")
returnNonereturn result.stdout
defscan_vulnerabilities(sbom_path):
result = subprocess.run(
["grype", sbom_path, "-o", "json"],
capture_output=True, text=True, timeout=300 )
if result.returncode !=0:
print(f"[ERROR] Vulnerability scan failed: {result.stderr[:200]}")
returnNonereturn json.loads(result.stdout)
defverify_signatures(image_ref):
result = subprocess.run(
["cosign", "verify", "--output", "json", image_ref],
capture_output=True, text=True, timeout=60 )
return {
"verified": result.returncode ==0,
"output": result.stdout[:500],
"error": result.stderr[:200] if result.returncode !=0elseNone }
defmain():
target = sys.argv[1] if len(sys.argv) >1else"." print(f"[*] Generating SBOM for: {target}")
sbom_json = generate_sbom(target)
ifnot sbom_json:
sys.exit(1)
sbom_file ="sbom_output.json"with open(sbom_file, "w") as f:
f.write(sbom_json)
print(f"[+] SBOM saved to {sbom_file}")
sbom_data = json.loads(sbom_json)
artifacts = sbom_data.get("artifacts", [])
print(f"[+] Total packages found: {len(artifacts)}")
print(f"[*] Scanning for vulnerabilities...")
vuln_data = scan_vulnerabilities(sbom_file)
if vuln_data:
matches = vuln_data.get("matches", [])
print(f"[+] Total vulnerabilities found: {len(matches)}")
severity_count = {"Critical": 0, "High": 0, "Medium": 0, "Low": 0, "Negligible": 0}
formatchin matches:
severity =match.get("vulnerability", {}).get("severity", "Unknown")
severity_count[severity] = severity_count.get(severity, 0) +1 print("\n[*] Vulnerability Summary:")
for sev, count in sorted(severity_count.items(), key=lambda x: x[1], reverse=True):
if count >0:
print(f" {sev}: {count}")
critical_high = [m for m in matches if m.get("vulnerability", {}).get("severity") in ("Critical", "High")]
if critical_high:
print(f"\n[!] Critical/High vulnerabilities requiring immediate attention:")
formatchin critical_high[:10]:
vuln =match.get("vulnerability", {})
artifact =match.get("artifact", {})
print(f" [{vuln.get('severity')}] {artifact.get('name')}=={artifact.get('version')}")
print(f" ID: {vuln.get('id')}")
print(f" Fix: {vuln.get('fix', {}).get('versions', ['N/A'])}")
if len(sys.argv) >2:
image = sys.argv[2]
print(f"\n[*] Verifying signatures for: {image}")
sig_result = verify_signatures(image)
print(f" Verified: {sig_result['verified']}")
if sig_result["error"]:
print(f" Error: {sig_result['error']}")
if __name__ =="__main__":
main()