Skip to content
Merged
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
69 changes: 65 additions & 4 deletions .github/workflows/secret-scanner-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ jobs:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Full history. One of the two gating passes below scans git history,
# and the default depth-1 clone would silently reduce it to a single
# commit. That pass asserts non-shallowness for itself rather than
# trusting this line to have taken effect.
fetch-depth: 0

# Gitleaks runs as a pinned, checksum-verified binary rather than via
# gitleaks/gitleaks-action. The action injects gitleaks into the runner
Expand Down Expand Up @@ -210,10 +216,15 @@ jobs:
rm -rf .standards-gitleaks
test -s .gitleaks-estate.toml

# Scans the checked-out working tree (--no-git): deterministic and fast.
# A finding exits non-zero and fails the check. Full-history scanning is
# available by removing --no-git and adding `fetch-depth: 0` to the
# checkout above, at the cost of a slower scan on every PR.
# TWO gating passes run below and they are not redundant. Each sees a
# population the other cannot:
# * working tree (--no-git) -- sees untracked and generated files;
# cannot see a blob deleted in an earlier commit
# * git history -- sees committed-then-deleted secrets;
# cannot see a file that was never committed
# Both gate: a finding in either exits non-zero and fails the check.
# Full history costs a slower scan on every PR. That is the accepted
# price of a deleted secret no longer being permanently invisible.
# ── Estate baseline sharing ──────────────────────────────────────────
# A consumer repo can inherit the estate-wide allowlist instead of
# copying it, by writing in its own .gitleaks.toml:
Expand Down Expand Up @@ -310,6 +321,56 @@ jobs:
--config "$CONFIG" \
--exit-code 1

# Full-history pass — ADDITIVE, not a replacement. Measured on a fixture
# with this exact pinned binary, the same secret in two placements:
#
# committed then deleted working tree: MISSED history: FOUND
# untracked file working tree: FOUND history: MISSED
#
# git mode scans commit diffs, so it cannot see a file that was never
# committed; --no-git walks the checked-out tree, so it cannot see a blob
# that is no longer in it. Dropping either pass REMOVES coverage. Keeping
# both is why this was added as a new step rather than by editing the one
# above, whose name and log lines existing triage keys on.
#
# KNOWN GAP, stated rather than implied: the AsciiDoc mirror pass below
# scans a filesystem copy with --no-git, so fetch-depth: 0 cannot reach
# it. An AsciiDoc secret that was committed and later deleted stays
# invisible. "The gate is full-history" holds for every file class but
# that one.
- name: Gitleaks secret scan — git history (gating)
run: |
set -euo pipefail

# fetch-depth: 0 on the checkout is load-bearing HERE. If it ever
# regresses to the default depth-1 clone, detect would walk a single
# commit, find nothing and report a pass — a gate that cannot fail.
# Assert completeness from git itself: gitleaks' own "scanned N
# commits" line under-reports and is not proof of depth.
if [ "$(git rev-parse --is-shallow-repository)" != "false" ]; then
echo "::error::checkout is shallow -- a history scan here would be vacuous; refusing to report a pass"
exit 1
fi
echo "history available to this scan: $(git rev-list --count HEAD) commits"

# Same config resolution as the working-tree pass, duplicated rather
# than factored out so that step stays byte-identical.
CONFIG=".gitleaks-estate.toml"
if [ -f .gitleaks.toml ]; then
CONFIG=".gitleaks.toml"
fi
# A staging step that failed silently would leave gitleaks running a
# reduced config over almost nothing and calling the result green.
test -s "$CONFIG"

"$RUNNER_TEMP/gitleaks" detect \
--source . \
--redact \
--no-banner \
--verbose \
--config "$CONFIG" \
--exit-code 1

# AsciiDoc blind-spot pass — MEASURED, not theoretical.
#
# gitleaks' DEFAULT global allowlist excludes paths matching
Expand Down
Loading