From ad0c332facd8872120e80b4080db242387e46436 Mon Sep 17 00:00:00 2001 From: akula Date: Thu, 30 Jul 2026 13:26:12 -0400 Subject: [PATCH] Make the scanner say WHAT it found The first version reported a count and nothing else. It now prints rule, file, line, commit and entropy (values still redacted), uploads the JSON report, and says what to do in both directions -- rotate a real credential, or allowlist a false positive by the narrowest mechanism the tool actually honours. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/gitleaks.yml | 38 +++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gitleaks.yml b/.github/workflows/gitleaks.yml index 0aba39c..77e01a5 100644 --- a/.github/workflows/gitleaks.yml +++ b/.github/workflows/gitleaks.yml @@ -73,18 +73,46 @@ jobs: RANGE="" fi + # --redact: values never reach the log. --report-path: the JSON says + # WHICH file, line and commit, uploaded below. A scanner that only + # says "1 leak found" sends you hunting through the whole history. + COMMON="--source=. --redact --report-format json --report-path gitleaks-report.json" if [ -n "$RANGE" ]; then echo "scanning range $RANGE" - /tmp/gitleaks detect --source=. --redact --log-opts="$RANGE" + /tmp/gitleaks detect $COMMON --log-opts="$RANGE" else echo "scanning the ENTIRE history" - /tmp/gitleaks detect --source=. --redact + /tmp/gitleaks detect $COMMON fi - - name: What to do if that failed + - name: Where the findings are if: failure() run: | echo "::error::A secret was found, or the scan could not run. Either way this does not merge." - echo "If a credential was found: it is compromised. Remove it, ROTATE it (git history keeps the old value)," - echo "and confirm .gitignore covers it. Rewriting history alone does NOT make a leaked key safe." + # jq, not a heredoc: a heredoc body has to start at column 0, which + # terminates the YAML block scalar and makes this whole workflow + # unparseable. That shipped once and the run failed with no jobs at + # all — a broken scanner reads as a failing scan, which is at least + # loud, but do not do it again. + if [ -s gitleaks-report.json ]; then + echo "--- findings (file, line, commit — values stay redacted) ---" + jq -r '.[] | " \(.RuleID) \(.File):\(.StartLine) commit \(.Commit[0:10]) \(.Date) entropy \(.Entropy)"' \ + gitleaks-report.json || cat gitleaks-report.json + fi + echo "" + echo "If it is a real credential: it is compromised. Remove it, ROTATE it (git history keeps" + echo "the old value), and confirm .gitignore covers it. Rewriting history alone does NOT make" + echo "a leaked key safe — the object stays retrievable by hash." + echo "" + echo "If it is a false positive: allowlist THAT FINDING by fingerprint in .gitleaks.toml," + echo "with a line saying why. Never allowlist a path, an extension or a whole rule — that is" + echo "how a scanner goes blind without anyone noticing." exit 1 + + - name: Keep the report + if: failure() + uses: actions/upload-artifact@v4 + with: + name: gitleaks-report-${{ github.run_id }} + path: gitleaks-report.json + if-no-files-found: ignore