Skip to content

Commit 91f90d3

Browse files
committed
chore: security workflow (gitleaks, pip-audit, npm audit, trivy) (#11)
1 parent 887c129 commit 91f90d3

4 files changed

Lines changed: 125 additions & 2 deletions

File tree

.github/branch-protection/develop.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010
"Pre-commit",
1111
"Branch-protection contexts sync",
1212
"Commit-type sync",
13-
"Lint PR title (conventional commits)"
13+
"Lint PR title (conventional commits)",
14+
"Secret scan (gitleaks)",
15+
"Python deps (pip-audit)",
16+
"Frontend deps (npm audit)",
17+
"Container image scan (trivy)"
1418
]
1519
},
1620
"enforce_admins": false,

.github/branch-protection/main.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010
"Pre-commit",
1111
"Branch-protection contexts sync",
1212
"Commit-type sync",
13-
"Lint PR title (conventional commits)"
13+
"Lint PR title (conventional commits)",
14+
"Secret scan (gitleaks)",
15+
"Python deps (pip-audit)",
16+
"Frontend deps (npm audit)",
17+
"Container image scan (trivy)"
1418
]
1519
},
1620
"enforce_admins": false,
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# pip-audit per-CVE ignore list. One CVE id per line. Lines starting with
2+
# `#` are comments and are skipped. Add a justification + tracking issue
3+
# (or a sunset date) above every entry so the next owner can audit it.
4+
#
5+
# Format:
6+
# # CVE-XXXX-NNNN — short reason; tracking issue / fix ETA.
7+
# CVE-XXXX-NNNN
8+
#
9+
# Currently empty for the harness scaffold — add entries as upstream
10+
# advisories require.

.github/workflows/security.yml

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
name: Security
2+
3+
# Action SHAs are pinned, not floating tags. To bump:
4+
# gh api repos/<owner>/<repo>/commits/<tag> --jq .sha
5+
# (use /commits/<tag>, NOT /git/refs/tags/<tag> — annotated tags would
6+
# return the tag-object SHA, which Actions can't resolve.)
7+
8+
on:
9+
push:
10+
branches: [develop, main]
11+
pull_request:
12+
branches: [develop, main]
13+
schedule:
14+
# Weekly Monday 06:00 UTC — re-runs the suite against the latest
15+
# vulnerability databases even when the repo is quiet.
16+
- cron: "0 6 * * 1"
17+
18+
permissions:
19+
contents: read
20+
21+
jobs:
22+
gitleaks:
23+
name: Secret scan (gitleaks)
24+
runs-on: ubuntu-latest
25+
steps:
26+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
27+
with:
28+
fetch-depth: 0 # full history so gitleaks can scan every commit
29+
# Install and run the binary directly — the v2 action attempts to upload
30+
# a SARIF artifact, which trips the account-wide artifact storage quota
31+
# on this repo. Running the CLI keeps us to a single blocking step with
32+
# no artifact output.
33+
- name: Install gitleaks
34+
run: |
35+
VERSION=8.24.3
36+
curl -sSL "https://github.com/zricethezav/gitleaks/releases/download/v${VERSION}/gitleaks_${VERSION}_linux_x64.tar.gz" \
37+
| tar -xz -C /usr/local/bin gitleaks
38+
gitleaks version
39+
- name: Run gitleaks
40+
run: gitleaks detect --source . --redact --verbose --no-banner --exit-code 1
41+
42+
pip-audit:
43+
name: Python deps (pip-audit)
44+
runs-on: ubuntu-latest
45+
steps:
46+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
47+
- uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8
48+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
49+
with:
50+
python-version: "3.14"
51+
- run: uv sync --frozen --extra dev
52+
- name: Audit production deps
53+
# --strict fails on ANY surfaced CVE, not just HIGH/CRITICAL. We don't
54+
# have a volume problem and any CVE in pinned deps is worth a
55+
# conscious accept-or-upgrade decision.
56+
#
57+
# Per-CVE ignores live in .github/security/pip-audit-ignore.txt with
58+
# a comment per ignore. xargs ferries them onto the command line so
59+
# the policy stays in version control rather than buried in this YAML.
60+
run: |
61+
uv pip install pip-audit
62+
IGNORES=$(grep -v '^\s*#' .github/security/pip-audit-ignore.txt 2>/dev/null \
63+
| grep -v '^\s*$' \
64+
| sed 's/^/--ignore-vuln /' \
65+
| tr '\n' ' ')
66+
uv run pip-audit --strict --progress-spinner off \
67+
--vulnerability-service osv \
68+
$IGNORES
69+
70+
npm-audit:
71+
name: Frontend deps (npm audit)
72+
runs-on: ubuntu-latest
73+
# Skips cleanly until #21 lands frontend/package.json.
74+
if: hashFiles('frontend/package-lock.json') != ''
75+
steps:
76+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
77+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
78+
with:
79+
node-version: "24"
80+
cache: npm
81+
cache-dependency-path: frontend/package-lock.json
82+
- run: cd frontend && npm ci
83+
# --audit-level=high — fail only on high/critical; moderate/low noted
84+
# but not blocking.
85+
- run: cd frontend && npm audit --audit-level=high
86+
87+
trivy-image:
88+
name: Container image scan (trivy)
89+
runs-on: ubuntu-latest
90+
# Blocking: any fixable HIGH/CRITICAL CVE in the built image fails the PR.
91+
steps:
92+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
93+
- name: Build image
94+
run: docker build -t harness-python-react:ci .
95+
- name: Run Trivy vulnerability scanner
96+
# Pinned to a SHA — never @master. A moving branch in a
97+
# supply-chain workflow defeats the point of the scan.
98+
uses: aquasecurity/trivy-action@a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8 # v0.36.0
99+
with:
100+
image-ref: harness-python-react:ci
101+
format: table
102+
severity: HIGH,CRITICAL
103+
exit-code: "1"
104+
ignore-unfixed: true
105+
vuln-type: os,library

0 commit comments

Comments
 (0)