Skip to content

Commit 33a8c52

Browse files
committed
fix(ci): adopt canonical hypatia-scan.yml (env.HOME/scanner-layout + Comment-step gate)
1 parent ac295f4 commit 33a8c52

1 file changed

Lines changed: 46 additions & 111 deletions

File tree

.github/workflows/hypatia-scan.yml

Lines changed: 46 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,25 @@ on:
1010
schedule:
1111
- cron: '0 0 * * 0' # Weekly on Sunday
1212
workflow_dispatch:
13+
# Estate guardrail: cancel superseded runs so re-pushes don't pile up
14+
# queued runs across the estate. Safe here because this workflow only
15+
# performs read-only checks/lint/test/scan with no publish or mutation.
16+
concurrency:
17+
group: ${{ github.workflow }}-${{ github.ref }}
18+
cancel-in-progress: true
1319

1420
permissions:
1521
contents: read
16-
# `pull-requests: write` is needed for the "Comment on PR with findings"
17-
# step to POST a results summary. Note: on Dependabot PRs the token is
18-
# downgraded to read-only regardless, so that step is also marked
19-
# continue-on-error below.
22+
# security-events: read lets the built-in GITHUB_TOKEN query this
23+
# repo's own Dependabot alerts via the Hypatia DependabotAlerts rule
24+
# (DA001-DA004). Without this, `scan_from_path` gets HTTP 403 and
25+
# the rule silently returns no findings.
26+
# See 007-lang/audits/audit-dependabot-automation-gap-2026-04-17.md.
27+
security-events: read
28+
# pull-requests: write lets the advisory "Comment on PR with findings"
29+
# step post its summary. Without it the built-in GITHUB_TOKEN gets
30+
# "Resource not accessible by integration" and (absent continue-on-error)
31+
# hard-fails the scan — exactly what the gate-decoupling design forbids.
2032
pull-requests: write
2133

2234
jobs:
@@ -26,7 +38,7 @@ jobs:
2638

2739
steps:
2840
- name: Checkout repository
29-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
41+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
3042
with:
3143
fetch-depth: 0 # Full history for better pattern analysis
3244

@@ -36,17 +48,9 @@ jobs:
3648
elixir-version: '1.19.4'
3749
otp-version: '28.3'
3850

39-
- name: Clone Hypatia (or use checkout when scanning hypatia itself)
40-
env:
41-
REPO: ${{ github.repository }}
51+
- name: Clone Hypatia
4252
run: |
43-
# When scanning hypatia from inside hypatia, point $HOME/hypatia
44-
# at the PR/branch checkout instead of cloning main — otherwise
45-
# CLI changes can never pass their own gate (the scanner binary
46-
# would always come from main and ignore new flags).
47-
if [ "$REPO" = "hyperpolymath/hypatia" ]; then
48-
ln -sfn "${GITHUB_WORKSPACE}" "$HOME/hypatia"
49-
elif [ ! -d "$HOME/hypatia" ]; then
53+
if [ ! -d "$HOME/hypatia" ]; then
5054
git clone https://github.com/hyperpolymath/hypatia.git "$HOME/hypatia"
5155
fi
5256
@@ -62,39 +66,16 @@ jobs:
6266
- name: Run Hypatia scan
6367
id: scan
6468
env:
65-
# Suppress the "Warning: Dependabot alerts unavailable: GITHUB_TOKEN
66-
# not set" line so the run is silent-warning-free. The token is
67-
# read-only by default and only used to query Dependabot alerts.
69+
# Pass the built-in Actions token through to Hypatia so the
70+
# DependabotAlerts rule can query this repo's own alerts.
71+
# For cross-repo scanning (fleet-coordinator scan-supervised),
72+
# a PAT with `security_events` scope is required instead.
6873
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
69-
REPO: ${{ github.repository }}
7074
run: |
71-
echo "Scanning repository: $REPO"
72-
73-
# Run scanner with --exit-zero so a findings-found exit-1 does
74-
# NOT short-circuit the rest of this step under `set -e`. The
75-
# downstream "Check for critical or high-severity issues" step
76-
# is the explicit gate. See hyperpolymath/hypatia#213.
77-
#
78-
# Guard against the scanner producing no output (a crash, an
79-
# unknown flag, etc.): if hypatia-findings.json is empty or
80-
# missing after the run, fall back to "[]" so the jq calls
81-
# below don't 9 the whole gate. We surface stderr so the
82-
# underlying scanner failure is still visible in the log.
83-
set +e
84-
HYPATIA_FORMAT=json "$HOME/hypatia/hypatia-cli.sh" scan . --exit-zero \
85-
> hypatia-findings.json 2> hypatia-scan.stderr
86-
SCAN_EXIT=$?
87-
set -e
88-
echo "Scanner exit: $SCAN_EXIT"
89-
if [ -s hypatia-scan.stderr ]; then
90-
echo "--- scanner stderr ---"
91-
cat hypatia-scan.stderr
92-
echo "--- end stderr ---"
93-
fi
94-
if ! jq empty hypatia-findings.json 2>/dev/null; then
95-
echo "Scanner did not produce valid JSON; defaulting to empty findings."
96-
echo "[]" > hypatia-findings.json
97-
fi
75+
echo "Scanning repository: ${{ github.repository }}"
76+
77+
# Run scanner (exits non-zero when findings exist — suppress to continue)
78+
HYPATIA_FORMAT=json "$HOME/hypatia/hypatia-cli.sh" scan . --exit-zero > hypatia-findings.json || true
9879
9980
# Count findings
10081
FINDING_COUNT=$(jq '. | length' hypatia-findings.json 2>/dev/null || echo 0)
@@ -116,7 +97,7 @@ jobs:
11697
echo "- Medium: $MEDIUM" >> $GITHUB_STEP_SUMMARY
11798
11899
- name: Upload findings artifact
119-
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
100+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
120101
with:
121102
name: hypatia-findings
122103
path: hypatia-findings.json
@@ -191,76 +172,31 @@ jobs:
191172
# Cleanup
192173
rm -rf "$FLEET_DIR"
193174
194-
- name: Check for critical or high-severity issues
195-
if: steps.scan.outputs.critical > 0 || steps.scan.outputs.high > 0
196-
env:
197-
CRITICAL_COUNT: ${{ steps.scan.outputs.critical }}
198-
HIGH_COUNT: ${{ steps.scan.outputs.high }}
175+
- name: Check for critical issues
176+
if: steps.scan.outputs.critical > 0
199177
run: |
200-
echo "Total critical/high: $CRITICAL_COUNT critical, $HIGH_COUNT high"
201-
202-
# Baseline-aware gate: pre-existing accepted findings live in
203-
# .hypatia-baseline.json (committed). New critical/high findings
204-
# not in the baseline still fail the build. Findings are matched
205-
# on (severity, rule_module, type, file) tuple with absolute
206-
# build paths normalised to repo-relative.
207-
if [ -f .hypatia-baseline.json ]; then
208-
# Normalise + project just the comparable keys from the current scan.
209-
# Identity is the 4-tuple (severity, rule_module, type, file) per
210-
# the contract above. `action` is advisory metadata that varies
211-
# by run/strategy and MUST NOT be part of finding identity —
212-
# including it made every baseline entry fail to match.
213-
jq '[ .[] | select(.severity == "critical" or .severity == "high")
214-
| {severity, rule_module, type,
215-
file: (.file | sub("^/home/runner/work/[^/]+/[^/]+/"; "")
216-
| sub("^/github/workspace/"; "")) } ]' hypatia-findings.json > findings-current.json
217-
218-
# Subtract baseline. A current finding is "new" iff there's no
219-
# element in baseline equal to it (by-value).
220-
jq --slurpfile base .hypatia-baseline.json \
221-
'. as $cur | $cur | map(. as $f | select(($base[0] | any(. == $f)) | not))' \
222-
findings-current.json > findings-new.json
223-
new_count=$(jq 'length' findings-new.json)
224-
225-
if [ "$new_count" -gt 0 ]; then
226-
echo "::error::$new_count new critical/high finding(s) outside the baseline:"
227-
jq -r '.[] | " [\(.severity)] \(.rule_module)/\(.type) — \(.file)"' findings-new.json
228-
echo
229-
echo "If these are intentional, regenerate .hypatia-baseline.json:"
230-
echo " jq '[.[] | select(.severity == \"critical\" or .severity == \"high\") | {severity, rule_module, type, file}] | sort_by(.severity, .rule_module, .type, .file)' hypatia-findings.json > .hypatia-baseline.json"
231-
exit 1
232-
fi
233-
echo "All critical/high findings present in baseline — gate passes."
234-
else
235-
echo "No .hypatia-baseline.json — failing on any critical/high (legacy behaviour)."
236-
echo "Review hypatia-findings.json for details"
237-
exit 1
238-
fi
178+
echo "⚠️ Critical security issues found!"
179+
echo "Review hypatia-findings.json for details"
180+
# Don't fail the build yet - just warn
181+
# exit 1
239182
240183
- name: Generate scan report
241-
env:
242-
REPO: ${{ github.repository }}
243-
SHA: ${{ github.sha }}
244-
CRITICAL_COUNT: ${{ steps.scan.outputs.critical }}
245-
HIGH_COUNT: ${{ steps.scan.outputs.high }}
246-
MEDIUM_COUNT: ${{ steps.scan.outputs.medium }}
247-
TOTAL_COUNT: ${{ steps.scan.outputs.findings_count }}
248184
run: |
249185
cat << EOF > hypatia-report.md
250186
# Hypatia Security Scan Report
251187
252-
**Repository:** $REPO
188+
**Repository:** ${{ github.repository }}
253189
**Scan Date:** $(date -u +"%Y-%m-%d %H:%M:%S UTC")
254-
**Commit:** $SHA
190+
**Commit:** ${{ github.sha }}
255191
256192
## Summary
257193
258194
| Severity | Count |
259195
|----------|-------|
260-
| Critical | $CRITICAL_COUNT |
261-
| High | $HIGH_COUNT |
262-
| Medium | $MEDIUM_COUNT |
263-
| **Total**| $TOTAL_COUNT |
196+
| Critical | ${{ steps.scan.outputs.critical }} |
197+
| High | ${{ steps.scan.outputs.high }} |
198+
| Medium | ${{ steps.scan.outputs.medium }} |
199+
| **Total**| ${{ steps.scan.outputs.findings_count }} |
264200
265201
## Next Steps
266202
@@ -279,14 +215,13 @@ jobs:
279215
cat hypatia-report.md >> $GITHUB_STEP_SUMMARY
280216
281217
- name: Comment on PR with findings
282-
# Dependabot PRs always run with a read-only token regardless of the
283-
# workflow's declared permissions, so the createComment call below
284-
# would 403 on every dep-bump PR. The PR comment is informational
285-
# (the check result is already visible in the PR UI); we don't want
286-
# its absence to block merge.
287218
if: github.event_name == 'pull_request' && steps.scan.outputs.findings_count > 0
219+
# Advisory only — posting findings as a PR comment must never gate
220+
# the scan (hypatia#213 gate decoupling). Belt-and-braces alongside
221+
# the pull-requests: write permission above: a token/API hiccup or
222+
# a fork PR (read-only token) skips the comment, not the check.
288223
continue-on-error: true
289-
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v7
224+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v7
290225
with:
291226
script: |
292227
const fs = require('fs');
@@ -316,4 +251,4 @@ jobs:
316251
repo: context.repo.repo,
317252
issue_number: context.issue.number,
318253
body: comment
319-
});
254+
});

0 commit comments

Comments
 (0)