Skip to content

Commit c2f98cf

Browse files
committed
security: standardize secret scanning on TruffleHog
1 parent 9686751 commit c2f98cf

4 files changed

Lines changed: 14 additions & 101 deletions

File tree

.github/workflows/governance.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,20 @@
1111
# (rust-ci, codeql, dependabot, release, scan/mirror/pages plumbing).
1212

1313
name: Governance
14-
1514
on:
1615
push:
1716
branches: [main, master]
1817
pull_request:
1918
workflow_dispatch:
20-
2119
# Estate guardrail: cancel superseded runs so re-pushes / rebased PR
2220
# updates do not pile up queued runs against the shared account-wide
2321
# Actions concurrency pool. Applied only to read-only check workflows
2422
# (no publish/mutation), so cancelling a superseded run is always safe.
2523
concurrency:
2624
group: ${{ github.workflow }}-${{ github.ref }}
2725
cancel-in-progress: true
28-
2926
permissions:
3027
contents: read
31-
3228
jobs:
3329
governance:
3430
uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@861b5e911d9e5dcfb3c0ab3dd2a9a3c8fd0a1613

.github/workflows/hypatia-scan.yml

Lines changed: 8 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
11
# SPDX-License-Identifier: MPL-2.0
22
# Hypatia Neurosymbolic CI/CD Security Scan
33
name: Hypatia Security Scan
4-
54
on:
65
push:
7-
branches: [ main, master, develop ]
6+
branches: [main, master, develop]
87
pull_request:
9-
branches: [ main, master ]
8+
branches: [main, master]
109
schedule:
11-
- cron: '0 0 * * 0' # Weekly on Sunday
10+
- cron: '0 0 * * 0' # Weekly on Sunday
1211
workflow_dispatch:
1312
# Estate guardrail: cancel superseded runs so re-pushes don't pile up
1413
# queued runs across the estate. Safe here because this workflow only
1514
# performs read-only checks/lint/test/scan with no publish or mutation.
1615
concurrency:
1716
group: ${{ github.workflow }}-${{ github.ref }}
1817
cancel-in-progress: true
19-
2018
permissions:
2119
contents: read
2220
# security-events: write serves two purposes (write implies read):
@@ -38,31 +36,26 @@ permissions:
3836
# "Resource not accessible by integration" and (absent continue-on-error)
3937
# hard-fails the scan — exactly what the gate-decoupling design forbids.
4038
pull-requests: write
41-
4239
jobs:
4340
scan:
4441
name: Hypatia Neurosymbolic Analysis
4542
runs-on: ubuntu-latest
4643
timeout-minutes: 15
47-
4844
steps:
4945
- name: Checkout repository
5046
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
5147
with:
52-
fetch-depth: 0 # Full history for better pattern analysis
53-
48+
fetch-depth: 0 # Full history for better pattern analysis
5449
- name: Setup Elixir for Hypatia scanner
5550
uses: erlef/setup-beam@fc68ffb90438ef2936bbb3251622353b3dcb2f93 # v1.18.2
5651
with:
5752
elixir-version: '1.18'
5853
otp-version: '27'
59-
6054
- name: Clone Hypatia
6155
run: |
6256
if [ ! -d "$HOME/hypatia" ]; then
6357
git clone https://github.com/hyperpolymath/hypatia.git "$HOME/hypatia"
6458
fi
65-
6659
- name: Build Hypatia scanner (if needed)
6760
run: |
6861
cd "$HOME/hypatia"
@@ -71,7 +64,6 @@ jobs:
7164
mix deps.get
7265
mix escript.build
7366
fi
74-
7567
- name: Run Hypatia scan
7668
id: scan
7769
env:
@@ -104,14 +96,12 @@ jobs:
10496
echo "- Critical: $CRITICAL" >> $GITHUB_STEP_SUMMARY
10597
echo "- High: $HIGH" >> $GITHUB_STEP_SUMMARY
10698
echo "- Medium: $MEDIUM" >> $GITHUB_STEP_SUMMARY
107-
10899
- name: Upload findings artifact
109100
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
110101
with:
111102
name: hypatia-findings
112103
path: hypatia-findings.json
113104
retention-days: 90
114-
115105
- name: Convert Hypatia findings to SARIF
116106
# Always runs (no findings_count guard): an EMPTY SARIF run is
117107
# valid and intentional — uploading it clears stale Hypatia
@@ -227,7 +217,6 @@ jobs:
227217
console.log(`hypatia.sarif written: ${results.length} result(s).`);
228218
CJS
229219
node "$RUNNER_TEMP/hypatia-sarif.cjs"
230-
231220
- name: Upload SARIF to GitHub code scanning
232221
# Fork PRs get a read-only GITHUB_TOKEN, so security-events:write
233222
# is unavailable and upload-sarif cannot publish — skip there
@@ -239,16 +228,15 @@ jobs:
239228
# exists to end). The empty-SARIF "clear stale alerts" path is
240229
# handled in the converter above and does not error here.
241230
if: >-
242-
always() &&
243-
(github.event_name != 'pull_request' ||
231+
always() && (github.event_name != 'pull_request' ||
232+
244233
github.event.pull_request.head.repo.fork != true)
245234
uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v3.28.1
246235
with:
247236
sarif_file: hypatia.sarif
248237
# Distinct category so Hypatia results coexist with CodeQL's
249238
# (codeql.yml) instead of overwriting them on the same surface.
250239
category: hypatia
251-
252240
- name: Submit findings to gitbot-fleet (Phase 2)
253241
if: steps.scan.outputs.findings_count > 0
254242
# Phase 2 is the collaborative LEARNING side-channel ("bots share
@@ -272,52 +260,7 @@ jobs:
272260
GITHUB_REPOSITORY: ${{ github.repository }}
273261
GITHUB_SHA: ${{ github.sha }}
274262
FINDINGS_COUNT: ${{ steps.scan.outputs.findings_count }}
275-
run: |
276-
echo "📤 Submitting $FINDINGS_COUNT findings to gitbot-fleet..."
277-
278-
# Clone gitbot-fleet to temp directory. A clone failure (network,
279-
# repo gone) is non-fatal: learning submission is best-effort.
280-
FLEET_DIR="/tmp/gitbot-fleet-$$"
281-
if ! git clone --depth 1 https://github.com/hyperpolymath/gitbot-fleet.git "$FLEET_DIR"; then
282-
echo "::warning::Could not clone gitbot-fleet — skipping Phase 2 learning submission (non-fatal)."
283-
exit 0
284-
fi
285-
286-
# The submission script's location in gitbot-fleet has drifted
287-
# before (it was absent from the default branch, which exit-127'd
288-
# every consuming repo's scan). Probe known locations rather than
289-
# hard-coding one path, and skip gracefully if none is present.
290-
SUBMIT_SCRIPT=""
291-
for cand in \
292-
"$FLEET_DIR/scripts/submit-finding.sh" \
293-
"$FLEET_DIR/scripts/submit_finding.sh" \
294-
"$FLEET_DIR/bin/submit-finding.sh" \
295-
"$FLEET_DIR/submit-finding.sh"; do
296-
if [ -f "$cand" ]; then
297-
SUBMIT_SCRIPT="$cand"
298-
break
299-
fi
300-
done
301-
302-
if [ -z "$SUBMIT_SCRIPT" ]; then
303-
echo "::warning::gitbot-fleet submit-finding script not found at any known path — skipping Phase 2 learning submission (non-fatal). Findings are still uploaded as an artifact and gated below."
304-
rm -rf "$FLEET_DIR"
305-
exit 0
306-
fi
307-
308-
# Run submission script. Pass the findings path as ABSOLUTE —
309-
# the script cd's into its own working dir before reading the
310-
# file, so a relative path would resolve to the wrong place.
311-
# A submission-script failure is logged but non-fatal.
312-
if bash "$SUBMIT_SCRIPT" "$GITHUB_WORKSPACE/hypatia-findings.json"; then
313-
echo "✅ Finding submission complete"
314-
else
315-
echo "::warning::gitbot-fleet submission script exited non-zero — Phase 2 learning submission skipped (non-fatal)."
316-
fi
317-
318-
# Cleanup
319-
rm -rf "$FLEET_DIR"
320-
263+
run: "echo \"\U0001F4E4 Submitting $FINDINGS_COUNT findings to gitbot-fleet...\"\n\n# Clone gitbot-fleet to temp directory. A clone failure (network,\n# repo gone) is non-fatal: learning submission is best-effort.\nFLEET_DIR=\"/tmp/gitbot-fleet-$$\"\nif ! git clone --depth 1 https://github.com/hyperpolymath/gitbot-fleet.git \"$FLEET_DIR\"; then\n echo \"::warning::Could not clone gitbot-fleet — skipping Phase 2 learning submission (non-fatal).\"\n exit 0\nfi\n\n# The submission script's location in gitbot-fleet has drifted\n# before (it was absent from the default branch, which exit-127'd\n# every consuming repo's scan). Probe known locations rather than\n# hard-coding one path, and skip gracefully if none is present.\nSUBMIT_SCRIPT=\"\"\nfor cand in \\\n \"$FLEET_DIR/scripts/submit-finding.sh\" \\\n \"$FLEET_DIR/scripts/submit_finding.sh\" \\\n \"$FLEET_DIR/bin/submit-finding.sh\" \\\n \"$FLEET_DIR/submit-finding.sh\"; do\n if [ -f \"$cand\" ]; then\n SUBMIT_SCRIPT=\"$cand\"\n break\n fi\ndone\n\nif [ -z \"$SUBMIT_SCRIPT\" ]; then\n echo \"::warning::gitbot-fleet submit-finding script not found at any known path — skipping Phase 2 learning submission (non-fatal). Findings are still uploaded as an artifact and gated below.\"\n rm -rf \"$FLEET_DIR\"\n exit 0\nfi\n\n# Run submission script. Pass the findings path as ABSOLUTE —\n# the script cd's into its own working dir before reading the\n# file, so a relative path would resolve to the wrong place.\n# A submission-script failure is logged but non-fatal.\nif bash \"$SUBMIT_SCRIPT\" \"$GITHUB_WORKSPACE/hypatia-findings.json\"; then\n echo \"✅ Finding submission complete\"\nelse\n echo \"::warning::gitbot-fleet submission script exited non-zero — Phase 2 learning submission skipped (non-fatal).\"\nfi\n\n# Cleanup\nrm -rf \"$FLEET_DIR\"\n"
321264
- name: Check for critical issues
322265
if: steps.scan.outputs.critical > 0
323266
# GATING POLICY (explicit, by design — not an oversight):
@@ -335,7 +278,6 @@ jobs:
335278
echo "::warning::Hypatia found critical security issue(s) — advisory."
336279
echo "See the Security → Code scanning page (category: hypatia)"
337280
echo "and the hypatia-findings.json artifact for details."
338-
339281
- name: Generate scan report
340282
run: |
341283
cat << EOF > hypatia-report.md
@@ -374,7 +316,6 @@ jobs:
374316
EOF
375317
376318
cat hypatia-report.md >> $GITHUB_STEP_SUMMARY
377-
378319
- name: Comment on PR with findings
379320
if: github.event_name == 'pull_request' && steps.scan.outputs.findings_count > 0
380321
# Advisory only — posting findings as a PR comment must never gate
@@ -384,32 +325,4 @@ jobs:
384325
continue-on-error: true
385326
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v7
386327
with:
387-
script: |
388-
const fs = require('fs');
389-
const findings = JSON.parse(fs.readFileSync('hypatia-findings.json', 'utf8'));
390-
391-
const critical = findings.filter(f => f.severity === 'critical').length;
392-
const high = findings.filter(f => f.severity === 'high').length;
393-
394-
let comment = `## 🔍 Hypatia Security Scan\n\n`;
395-
comment += `**Findings:** ${findings.length} issues detected\n\n`;
396-
comment += `| Severity | Count |\n|----------|-------|\n`;
397-
comment += `| 🔴 Critical | ${critical} |\n`;
398-
comment += `| 🟠 High | ${high} |\n`;
399-
comment += `| 🟡 Medium | ${findings.length - critical - high} |\n\n`;
400-
401-
if (critical > 0) {
402-
comment += `⚠️ **Action Required:** Critical security issues found!\n\n`;
403-
}
404-
405-
comment += `<details><summary>View findings</summary>\n\n`;
406-
comment += `\`\`\`json\n${JSON.stringify(findings.slice(0, 10), null, 2)}\n\`\`\`\n`;
407-
comment += `</details>\n\n`;
408-
comment += `*Powered by Hypatia Neurosymbolic CI/CD Intelligence*`;
409-
410-
github.rest.issues.createComment({
411-
owner: context.repo.owner,
412-
repo: context.repo.repo,
413-
issue_number: context.issue.number,
414-
body: comment
415-
});
328+
script: "const fs = require('fs');\nconst findings = JSON.parse(fs.readFileSync('hypatia-findings.json', 'utf8'));\n\nconst critical = findings.filter(f => f.severity === 'critical').length;\nconst high = findings.filter(f => f.severity === 'high').length;\n\nlet comment = `## \U0001F50D Hypatia Security Scan\\n\\n`;\ncomment += `**Findings:** ${findings.length} issues detected\\n\\n`;\ncomment += `| Severity | Count |\\n|----------|-------|\\n`;\ncomment += `| \U0001F534 Critical | ${critical} |\\n`;\ncomment += `| \U0001F7E0 High | ${high} |\\n`;\ncomment += `| \U0001F7E1 Medium | ${findings.length - critical - high} |\\n\\n`;\n\nif (critical > 0) {\n comment += `⚠️ **Action Required:** Critical security issues found!\\n\\n`;\n}\n\ncomment += `<details><summary>View findings</summary>\\n\\n`;\ncomment += `\\`\\`\\`json\\n${JSON.stringify(findings.slice(0, 10), null, 2)}\\n\\`\\`\\`\\n`;\ncomment += `</details>\\n\\n`;\ncomment += `*Powered by Hypatia Neurosymbolic CI/CD Intelligence*`;\n\ngithub.rest.issues.createComment({\n owner: context.repo.owner,\n repo: context.repo.repo,\n issue_number: context.issue.number,\n body: comment\n});"

.machine_readable/contractiles/Justfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,6 @@ help:
6868
full-build:
6969
just clean
7070
just build
71-
just run
71+
just run
72+
secret-scan-trufflehog:
73+
@command -v trufflehog >/dev/null && trufflehog filesystem . --only-verified || true

Justfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,6 @@ help:
6868
full-build:
6969
just clean
7070
just build
71-
just run
71+
just run
72+
secret-scan-trufflehog:
73+
@command -v trufflehog >/dev/null && trufflehog filesystem . --only-verified || true

0 commit comments

Comments
 (0)