-
Notifications
You must be signed in to change notification settings - Fork 7
feat: add supply chain security check workflow for PRs #227
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
2da6ec1
9976f97
df244b5
abf4b3c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| name: Supply Chain Security Check | ||
|
|
||
| on: | ||
| pull_request: | ||
| types: [opened, synchronize, reopened] | ||
|
|
||
| permissions: | ||
| contents: read | ||
| pull-requests: write | ||
| issues: read | ||
| id-token: write | ||
|
|
||
| jobs: | ||
| supply-chain-check: | ||
| if: ${{ !github.event.pull_request.head.repo.fork }} | ||
| runs-on: ubuntu-slim | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Use Node.js | ||
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | ||
| with: | ||
| node-version: '20.x' | ||
| cache: 'npm' | ||
|
|
||
| - name: Install dependencies | ||
| run: npm ci | ||
|
|
||
| - name: Get base branch package.json | ||
| id: base-pkg | ||
| env: | ||
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | ||
| run: | | ||
| git show "$BASE_SHA":package.json > /tmp/base-package.json | ||
|
|
||
| - name: Run supply chain security check | ||
| id: check | ||
| run: | | ||
| npx ts-node scripts/supply-chain-check-runner.ts /tmp/base-package.json > /tmp/supply-chain-report.md | ||
|
|
||
| - name: Claude supply chain analysis | ||
| uses: anthropics/claude-code-action@88c168b39e7e64da0286d812b6e9fbebb6708185 # v1.0.82 | ||
| with: | ||
| claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} | ||
| use_sticky_comment: true | ||
| direct_prompt: | | ||
| You are a supply chain security analyst. Your job is to analyze dependency changes | ||
| in this PR for supply chain attack risks and known vulnerabilities. | ||
|
|
||
| ## Step 1: Read the automated report | ||
| Read the supply chain security check report at /tmp/supply-chain-report.md. | ||
| This contains dependency changes, risk signals, and npm audit results. | ||
|
|
||
| ## Step 2: Web research for each changed/added package | ||
| For each added or updated dependency found in the report, use WebSearch to research: | ||
| - "<package-name> npm security vulnerability" (recent CVEs or advisories) | ||
| - "<package-name> npm supply chain attack" (known compromise incidents) | ||
| - "<package-name> npm malware typosquatting" (typosquatting or impersonation) | ||
| Search in English. Focus on results from the last 12 months. | ||
|
|
||
| ## Step 3: Post a PR comment in English | ||
| Write a concise PR comment that includes: | ||
| 1. A summary header with ✅ (all clear) or ⚠️ (issues found) | ||
| 2. A table of dependency changes (added/updated/removed) if any | ||
| 3. Automated risk signals from the report | ||
| 4. **Web research findings** — for each package, summarize what you found | ||
| (or note "No recent security issues found" if clean) | ||
| 5. npm audit vulnerability findings if any | ||
| 6. An actionable recommendation section | ||
|
|
||
| Keep the comment concise and focused on actionable insights. | ||
| Use markdown formatting for readability. | ||
| IMPORTANT: Your entire analysis and comment MUST be in English. | ||
| allowed_bots: 'dependabot[bot],claude[bot]' | ||
| claude_args: '--allowedTools Read,WebSearch,WebFetch,Bash(cat:*)' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| /** | ||
| * CI runner script for supply chain security checks. | ||
| * Invoked by the GitHub Actions workflow to analyze dependency changes, | ||
| * fetch package metadata, run npm audit, and output a markdown report. | ||
| * | ||
| * Usage: npx ts-node scripts/supply-chain-check-runner.ts <base-package-json-path> | ||
| * base-package-json-path: Path to the base branch's package.json file | ||
| * | ||
| * Outputs the markdown report to stdout. | ||
| */ | ||
|
|
||
| import * as fs from 'node:fs'; | ||
| import { | ||
| analyzePackageRisk, | ||
| type DependencyChange, | ||
| fetchPackageMetadata, | ||
| findDependencyChanges, | ||
| generateReport, | ||
| runNpmAudit, | ||
| } from './supply-chain-check'; | ||
|
|
||
| async function main() { | ||
| const basePackageJsonPath = process.argv[2]; | ||
| if (!basePackageJsonPath) { | ||
| console.error('Usage: supply-chain-check-runner.ts <base-package-json-path>'); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| const basePackage = JSON.parse(fs.readFileSync(basePackageJsonPath, 'utf-8')); | ||
| const headPackage = JSON.parse(fs.readFileSync('package.json', 'utf-8')); | ||
|
|
||
| // Find all dependency changes (production + dev) | ||
| const prodChanges = findDependencyChanges(basePackage.dependencies, headPackage.dependencies); | ||
| const devChanges = findDependencyChanges( | ||
| basePackage.devDependencies, | ||
| headPackage.devDependencies | ||
| ); | ||
| const allChanges = [...prodChanges, ...devChanges]; | ||
|
|
||
| // Analyze risk for added and updated packages | ||
| const packagesToCheck = allChanges.filter( | ||
| (c): c is DependencyChange & { newVersion: string } => | ||
| (c.type === 'added' || c.type === 'updated') && c.newVersion !== undefined | ||
| ); | ||
|
|
||
| const riskResults: { pkg: string; risks: ReturnType<typeof analyzePackageRisk> }[] = []; | ||
|
|
||
| for (const pkg of packagesToCheck) { | ||
| try { | ||
| const metadata = await fetchPackageMetadata(pkg.name, pkg.newVersion); | ||
| const risks = analyzePackageRisk(metadata); | ||
| riskResults.push({ pkg: pkg.name, risks }); | ||
| } catch (error) { | ||
| riskResults.push({ | ||
| pkg: pkg.name, | ||
| risks: [ | ||
| { | ||
| type: 'metadata-fetch-failed' as const, | ||
| severity: 'high' as const, | ||
| message: `Failed to fetch package metadata: ${error instanceof Error ? error.message : String(error)}`, | ||
| }, | ||
| ], | ||
| }); | ||
|
Comment on lines
+53
to
+63
|
||
| } | ||
| } | ||
|
|
||
| // Run npm audit | ||
| const auditResult = await runNpmAudit(); | ||
|
|
||
| // Generate report | ||
| const report = generateReport(allChanges, riskResults, auditResult); | ||
| console.log(report); | ||
| } | ||
|
|
||
| main().catch((error) => { | ||
| console.error('Supply chain check failed:', error); | ||
| process.exit(1); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Passing the entire base branch package.json as a single CLI argument is brittle (ARG_MAX limits, embedded newlines) and makes failures hard to diagnose. Consider changing the runner to accept a file path (e.g.,
/tmp/base-package.json) or read base JSON from stdin instead ofprocess.argv[2].