Skip to content

Commit 609e27b

Browse files
HavenXiaclaude
andcommitted
ci(security): add dependency review and bun audit checks
Two dependency vulnerability checks per the approved plan: - dependency-review: fails PRs introducing dependencies with high+ advisories. Covers what GitHub's dependency graph sees (direct deps and Actions; bun.lock transitives are out of its reach). - bun audit: audits the full lockfile. Report-only and green on pull_request/push/workflow_dispatch (severity counts in the job summary, raw JSON as artifact); scheduled runs fail on high/critical findings to notify maintainers. The workflow is check/report only: it never modifies package.json or bun.lock and opens no upgrade PRs. Actions are pinned to release commit SHAs; Bun version matches ci.yml to avoid result drift. Closes #850 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PVZLWprb3HLfiDvc6YuHNw
1 parent fbe7556 commit 609e27b

1 file changed

Lines changed: 112 additions & 0 deletions

File tree

.github/workflows/security.yml

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
name: Security
2+
3+
# Dependency vulnerability checks (#850).
4+
#
5+
# Exit behavior by event:
6+
# pull_request -> report-only, always green (baseline not yet clean)
7+
# push (main) -> report-only, always green
8+
# workflow_dispatch -> report-only, always green (manual verification)
9+
# schedule -> fails when high/critical advisories exist, to notify maintainers
10+
#
11+
# This workflow only checks and reports. It never modifies package.json or
12+
# bun.lock and never opens upgrade PRs.
13+
14+
on:
15+
pull_request:
16+
branches: [main]
17+
paths:
18+
- 'package.json'
19+
- 'bun.lock'
20+
- '.github/workflows/**'
21+
push:
22+
branches: [main]
23+
paths:
24+
- 'package.json'
25+
- 'bun.lock'
26+
- '.github/workflows/**'
27+
schedule:
28+
# Weekly sweep so new advisories against existing deps surface without a push
29+
- cron: '17 3 * * 1'
30+
workflow_dispatch:
31+
32+
permissions:
33+
contents: read
34+
35+
jobs:
36+
# ── Dependency Review ──────────────────────────────────────────────────
37+
# Fails a PR that introduces a dependency with a high+ advisory.
38+
# Coverage: GitHub's dependency graph does not parse bun.lock, so this only
39+
# sees direct dependencies from package.json and GitHub Actions. The full
40+
# transitive tree is covered by the audit job below.
41+
dependency-review:
42+
name: Dependency Review
43+
if: github.event_name == 'pull_request'
44+
runs-on: ubuntu-latest
45+
steps:
46+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
47+
with:
48+
persist-credentials: false
49+
- uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4
50+
with:
51+
fail-on-severity: high
52+
53+
# ── Bun Audit ──────────────────────────────────────────────────────────
54+
# Audits the full bun.lock tree against known advisories. Writes severity
55+
# counts to the job summary and uploads the raw JSON as an artifact.
56+
audit:
57+
name: Bun Audit
58+
runs-on: ubuntu-latest
59+
steps:
60+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
61+
with:
62+
persist-credentials: false
63+
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
64+
with:
65+
# Keep aligned with ci.yml so audit results do not drift between workflows
66+
bun-version: '1.3.12'
67+
- run: bun install --frozen-lockfile
68+
- name: Run bun audit
69+
run: |
70+
bun audit --json > audit.json || true
71+
# An invalid file means the audit itself broke (network, registry) —
72+
# that must fail on every event, unlike advisory findings below.
73+
jq empty audit.json
74+
75+
TOTAL=$(jq '[.[][]] | length' audit.json)
76+
CRITICAL=$(jq '[.[][] | select(.severity == "critical")] | length' audit.json)
77+
HIGH=$(jq '[.[][] | select(.severity == "high")] | length' audit.json)
78+
MODERATE=$(jq '[.[][] | select(.severity == "moderate")] | length' audit.json)
79+
LOW=$(jq '[.[][] | select(.severity == "low")] | length' audit.json)
80+
81+
if [ "${{ github.event_name }}" = "schedule" ]; then
82+
MODE="enforcing: fails on high/critical"
83+
else
84+
MODE="report-only"
85+
fi
86+
87+
{
88+
echo "## bun audit report"
89+
echo ""
90+
echo "- Bun version: $(bun --version)"
91+
echo "- Event: \`${{ github.event_name }}\` ($MODE)"
92+
echo "- Total advisories: **$TOTAL**"
93+
echo ""
94+
echo "| Severity | Count |"
95+
echo "|---|---|"
96+
echo "| critical | $CRITICAL |"
97+
echo "| high | $HIGH |"
98+
echo "| moderate | $MODERATE |"
99+
echo "| low | $LOW |"
100+
echo ""
101+
echo "Raw output is attached as the \`bun-audit-report\` artifact."
102+
} >> "$GITHUB_STEP_SUMMARY"
103+
104+
if [ "${{ github.event_name }}" = "schedule" ] && [ "$((CRITICAL + HIGH))" -gt 0 ]; then
105+
echo "::error::bun audit found $CRITICAL critical and $HIGH high advisories"
106+
exit 1
107+
fi
108+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
109+
if: always()
110+
with:
111+
name: bun-audit-report
112+
path: audit.json

0 commit comments

Comments
 (0)