Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,8 @@ jobs:
# A multi-arch manifest resolves to the runner's platform (linux/amd64); the arm64
# leg shares the same base image and package set, so one pass is representative.
#
# CRITICAL/HIGH only. Widening to MEDIUM/LOW was tried and reverted on review
# (constructorfabric/insight#2016): every finding here comes from the base layer and the
# remedy is the same either way — refresh the base image — while the wider filter triples
# the alert count and buries the CRITICALs that drive action.
# CRITICAL/HIGH only: everything here comes from the base layer, and the remedy is the
# same at any severity — refresh the base image.
env:
TRIVY_IMAGE: aquasec/trivy:0.72.0@sha256:cffe3f5161a47a6823fbd23d985795b3ed72a4c806da4c4df16266c02accdd6f
IMAGE_REF: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.merge.outputs.build_tag }}
Expand Down
13 changes: 5 additions & 8 deletions .github/workflows/trivy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,9 @@ jobs:
# date-scoped — they matter again the moment this pass becomes blocking:
# CVE-2026-12345 exp:2026-12-31 # tracked in <issue>, fix queued
#
# `-w /src` is load-bearing for that: Trivy resolves the default ignore file relative to
# the working directory, so without it a repo-root `.trivyignore` is read from the
# image's own cwd and silently ignored. `--ignorefile` is not used instead because Trivy
# exits FATAL when the named file does not exist, which would break every run until
# someone adds a waiver.
# `-w /src` is required for that: Trivy resolves the default ignore file relative to the
# working directory. Do not switch to `--ignorefile` — it exits FATAL when the file is
# absent, breaking every run until someone adds a waiver.
run: |
docker run --rm \
-v "${{ github.workspace }}:/src:ro" \
Expand Down Expand Up @@ -100,9 +98,8 @@ jobs:
# CRITICAL is in the severity list even though the `critical` pass covers it: that pass
# only prints a table, so without this a CRITICAL would never appear as an alert.
#
# `--include-dev-deps` because a compromised build-time dependency executes on the
# runner and can alter the bundle. Trivy's pnpm default hides them, which understates
# the supply-chain surface — on this repository it hides 8 of 37 findings.
# `--include-dev-deps` because a compromised build-time dependency executes on the runner
# and can alter the bundle; Trivy's pnpm default hides them.
run: |
set -euo pipefail
docker run --rm \
Expand Down
44 changes: 34 additions & 10 deletions .github/workflows/trufflehog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,24 @@ jobs:
[ -f trufflehog-findings.jsonl ] || exit 0
python3 - trufflehog-findings.jsonl "${GITHUB_STEP_SUMMARY:-/dev/null}" <<'PY'
import json, sys, collections
rows = []
rows, unparsable, skipped = [], 0, 0
with open(sys.argv[1], encoding="utf-8") as fh:
for line in fh:
line = line.strip()
if not line:
continue
d = json.loads(line)
try:
d = json.loads(line)
except json.JSONDecodeError:
# A truncated or non-JSON line must not take the whole summary down with
# it; it is counted and reported instead.
unparsable += 1
continue
if "DetectorName" not in d:
# Only findings carry a detector. Anything else on stdout is not a finding
# and must not inflate the count.
skipped += 1
continue
git = (d.get("SourceMetadata") or {}).get("Data", {}).get("Git", {})
rows.append({
"detector": d.get("DetectorName", "?"),
Expand All @@ -110,6 +121,8 @@ jobs:
out = open(sys.argv[2], "a", encoding="utf-8")
w = out.write
w("## TruffleHog secret scan\n\n")
if unparsable or skipped:
w(f"_{unparsable} unparsable line(s), {skipped} non-finding record(s) skipped._\n\n")
if not rows:
w("No secrets detected in the scanned history (all result kinds enabled).\n")
sys.exit(0)
Expand All @@ -129,8 +142,7 @@ jobs:
# The git scan above only reaches commits that some ref still points at. Two classes of leak
# sit outside it, and only the GitHub API can see them: objects orphaned by a force push or a
# deleted branch, which remain fetchable by SHA, and secrets pasted into pull request or issue
# comments, where they arrive more often than in code. On this repository the API source reads
# ~8600 chunks against the git scan's ~6900, and takes about ten seconds.
# comments, where they arrive more often than in code.
#
# Off pull requests because it depends on API availability and rate limits, and a transient
# outage should not colour a pull request at all.
Expand All @@ -141,13 +153,12 @@ jobs:
timeout-minutes: 20
steps:
- name: TruffleHog scan (GitHub API — comments, wikis, unreachable objects)
env:
# The auto-injected token is enough for a public repository's commits and comments.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Deliberately unauthenticated, and it must stay that way: TruffleHog's GitHub source
# calls `GET /user`, which an installation token cannot read — passing `GITHUB_TOKEN`
# fails the job with 403. The repository is public, so no token is needed.
run: |
set -euo pipefail
docker run --rm \
-e GITHUB_TOKEN \
trufflesecurity/trufflehog:3.96.0@sha256:aa821cf4ace8861c7d096d83818cdf7bb9719028a52d37a52eaad44086a52577 \
github \
--repo "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" \
Expand All @@ -165,13 +176,24 @@ jobs:
[ -f trufflehog-api-findings.jsonl ] || exit 0
python3 - trufflehog-api-findings.jsonl "${GITHUB_STEP_SUMMARY:-/dev/null}" <<'PY'
import json, sys, collections
rows = []
rows, unparsable, skipped = [], 0, 0
with open(sys.argv[1], encoding="utf-8") as fh:
for line in fh:
line = line.strip()
if not line:
continue
d = json.loads(line)
try:
d = json.loads(line)
except json.JSONDecodeError:
# A truncated or non-JSON line must not take the whole summary down with
# it; it is counted and reported instead.
unparsable += 1
continue
if "DetectorName" not in d:
# Only findings carry a detector. Anything else on stdout is not a finding
# and must not inflate the count.
skipped += 1
continue
meta = (d.get("SourceMetadata") or {}).get("Data", {})
# The GitHub source reports under Github for commits and comments alike.
loc = meta.get("Github") or meta.get("Git") or {}
Expand All @@ -184,6 +206,8 @@ jobs:
out = open(sys.argv[2], "a", encoding="utf-8")
w = out.write
w("## TruffleHog — GitHub API source (report-only)\n\n")
if unparsable or skipped:
w(f"_{unparsable} unparsable line(s), {skipped} non-finding record(s) skipped._\n\n")
if not rows:
w("No secrets detected in commits, pull request and issue comments, or wikis "
"(all result kinds enabled).\n")
Expand Down