Skip to content

ci(security): gate the secrets scan on a reviewed baseline - #2351

Merged
Gregory91G merged 4 commits into
mainfrom
ci/trufflehog-fp-baseline
Aug 10, 2026
Merged

ci(security): gate the secrets scan on a reviewed baseline#2351
Gregory91G merged 4 commits into
mainfrom
ci/trufflehog-fp-baseline

Conversation

@Gregory91G

@Gregory91G Gregory91G commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Refs #2301.

The secrets gate cannot tell a credential from ordinary code. A scan of every ref returns 272 findings across 9 detectors and not one is a credential — test function names, fixture values, UUIDs, constant names, CHANGE_ME placeholders, prose in a document. The 69 marked verified are all Lob, whose sandbox accepts any test_-prefixed string, so a Python test name comes back confirmed. Disabling those detectors is not available to us: Jira, GitLab, HubSpot and Atlassian cover services Insight integrates with, and URI and SQLServer cover the connection-string shapes that a real leak takes.

This adds a reviewed baseline instead — the mechanism detect-secrets and gitleaks both use, implemented here because TruffleHog has no equivalent (trufflesecurity/trufflehog#2687, open since 2024).

How it works

What the scanner emits. TruffleHog returns no verdict. It writes one JSON object per finding to stdout, which the workflow redirects to a file. Each record carries the detector that matched, the matched value in Raw, whether the vendor's API confirmed it, and the location under SourceMetadata.Data.Git — or under .Github, with a link instead of a file, when the source is the GitHub API. Deciding whether any of that should fail a build has always been our code's job; until now that code was three copies of a heredoc inlined in the YAML.

Fingerprint. Each finding is named by sha256(detector | path | raw), truncated to 16 hex characters.

  • Those three fields identify the finding: the same value in a different file is a different leak, a different value in the same file likewise.
  • The commit id is deliberately absent. It changes on rebase, amend and squash-merge while the content does not, so a fingerprint containing it would invalidate the whole baseline after any history rewrite. The commit still appears in the report, so a human can find the spot.
  • It is a hash rather than the plain tuple because the third field is the secret. sha256 keeps the value out of a file that lives in a public repository.

Allowlist. .github/trufflehog-allowlist.txt, one line per accepted finding:

96923d309cc0326b  Atlassian  docs/connectors/support/jsm.md  # example account id from vendor docs
└─ fingerprint    └─ detector └─ path                        └─ reason

The script reads only the first field. The detector, path and reason are for the reviewer, who can check the claim without running a scanner. A missing file is not an error — the baseline is then empty and the gate behaves as it did before. Seeded with the 120 fingerprints that the current 272 findings reduce to.

The run. Findings are split into those whose fingerprint is listed and those that are not. The step summary reports the known ones as a count and the new ones as a table — detector, verified, commit, path, fingerprint. Raw never reaches the summary, the log or an artifact. The blocking job exits non-zero when the table is non-empty; the two nightly jobs report and always exit zero.

Job Runs on Mode
secrets (diff) every pull request, its own commits only blocks
secrets (full history) nightly, every ref reports
secrets (GitHub API) nightly, comments and wikis reports

What this means for a pull request author

Nothing, in about thirteen of every fourteen pull requests. The 272 findings already in the repository are seeded into the baseline and never surface again, and the nightly jobs never fail anything, so the only findings an author ever sees are ones their own diff introduced.

When the check does go red, the run summary names the finding. From there:

  • A real credential — rotate it. The summary says so explicitly: removing the commit is not remediation, because the value was public the moment it was pushed.
  • A false positive — open the file at the path in the table, confirm the match is a test name, a fixture or an identifier, and add one line to .github/trufflehog-allowlist.txt in the same pull request, copying the fingerprint from the table and writing the reason. Push; the check clears.

The reviewer then sees that line in the diff, next to the code that produced it, and can judge the claim. That is the property --exclude-detectors cannot offer: there the same decision is made once, silently, and applies to the whole repository forever.

Expected frequency. 120 distinct false positives accumulated over the repository's 5.7 months, or roughly 21 a month, against 285 pull requests merged in the last 30 days — about one pull request in fourteen. They are not evenly spread: 55 are test function names and 32 are e2e metric fixtures, so the cost falls on people writing Python tests and metric expectations, not on everyone.

What does not change

No detector is disabled. A live Jira, GitLab or HubSpot token produces a fingerprint that is not in the file, which makes it new, which fails the build. The scan arguments, the result kinds and the redaction rules are untouched.

Test plan

  • The 272 real findings from a full scan, fed through the script against the seeded allowlist: exit 0, summary reads No new secrets. 272 known finding(s) matched the allowlist.
  • The same corpus plus one synthetic AWS key in src/backend/config.rs: exit 1, and the table lists that finding alone, with its fingerprint
  • report mode on the same corpus: exit 0
  • The same corpus with its last line truncated: block exits 1 with Scan output was incomplete: 1 unparsable line(s)…, report still exits 0, and the intact corpus still exits 0
  • python -m py_compile on the script; YAML parses; the three jobs keep their step order
  • actionlint .github/workflows/trufflehog.yml — exit 0
  • secrets (diff) green on this pull request
  • The next nightly secrets (full history) reports 272 known and 0 new

Follow-up

Once this lands, the # trufflehog:ignore added to .github/workflows/ghcr-cleanup.yml in #2308 can go: that finding belongs in the allowlist like the rest.

Summary by CodeRabbit

  • New Features

    • Added automated secret scanning for pull requests and repository history.
    • Scan results distinguish new findings from previously reviewed items and provide remediation guidance.
    • Pull-request scans can block changes when potential secrets are detected.
    • Reviewed, non-sensitive matches are documented and excluded from blocking results.
  • Bug Fixes

    • Improved handling of malformed or non-finding scan records.
    • Prevented raw secret values from appearing in scan summaries.
    • Ensured malformed scan output can correctly block pull-request validation.

Every finding is identified by sha256(detector|path|raw), truncated to 16 hex.
The hash carries no commit id, so an entry survives a rebase; it is a hash
rather than the tuple because the third field is the secret itself.

Findings whose fingerprint is listed in .github/trufflehog-allowlist.txt are
known false positives and do not fail the run; anything else does, in the
blocking job. Accepting a new one means adding a line with a reason in the
pull request that introduces it, so the claim is reviewed like any other diff.

The three jobs now share one script instead of three inline heredocs, and it
reads both the Git and the Github source metadata shapes.

Refs #2301

Signed-off-by: Gregory Gogin <grigoriy.gogin@constructor.tech>
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

The PR adds a shared TruffleHog JSONL gate, a reviewed fingerprint allowlist, and workflow integration. Pull-request scans block on new findings. Full-history and GitHub API scans report findings without blocking.

TruffleHog scanning and reporting

Layer / File(s) Summary
Shared gate processing
.github/scripts/trufflehog_gate.py
The gate parses findings, fingerprints records, redacts metadata, writes Markdown summaries, and applies block or report mode.
Reviewed finding allowlist
.github/trufflehog-allowlist.txt
The allowlist documents fingerprint handling and records reviewed non-credential findings.
Workflow scan integration
.github/workflows/trufflehog.yml
The pull-request scan calls the gate in block mode. History and GitHub API scans call it in report mode.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TruffleHog
  participant Workflow
  participant trufflehog_gate.py
  participant Allowlist
  participant GitHubSummary
  TruffleHog->>Workflow: Produce JSONL findings
  Workflow->>trufflehog_gate.py: Run scan gate
  trufflehog_gate.py->>Allowlist: Load fingerprints
  trufflehog_gate.py->>GitHubSummary: Append redacted summary
  trufflehog_gate.py->>Workflow: Return block or report status
Loading

Possibly related issues

  • constructorfabric/insight#2301: The issue proposes the reviewed fingerprint allowlist and shared TruffleHog gate implemented by this PR.

Possibly related PRs

Suggested reviewers: cyberantonz

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the reviewed baseline and blocking behavior added to the TruffleHog secrets scan.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/trufflehog-fp-baseline

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/scripts/trufflehog_gate.py:
- Around line 50-54: Update the final gate decision in the summary logic to
return nonzero when mode is block and unparsable is greater than zero, even when
no parsed finding is new. Preserve the existing success behavior for non-block
modes and block runs with zero unparsable output, using the existing unparsable
counter.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6ff9a766-1cd5-42bc-aa5f-f4daeb481a04

📥 Commits

Reviewing files that changed from the base of the PR and between f1b308e and d644142.

📒 Files selected for processing (3)
  • .github/scripts/trufflehog_gate.py
  • .github/trufflehog-allowlist.txt
  • .github/workflows/trufflehog.yml

Comment thread .github/scripts/trufflehog_gate.py
Fixes a problem found by CodeRabbit on this pull request: an empty result was
reported as a pass even when a line failed to parse. A truncated stream drops
every finding after the break, so in the blocking job that is not a pass.

Only unparsable lines count. Records that parse but carry no detector are
ordinary scanner output and stay silent.

Refs #2301

Signed-off-by: Gregory Gogin <grigoriy.gogin@constructor.tech>
@Gregory91G
Gregory91G requested a review from cyberantonz August 10, 2026 07:53
@Gregory91G
Gregory91G added this pull request to the merge queue Aug 10, 2026
Merged via the queue into main with commit 41af90f Aug 10, 2026
25 checks passed
@Gregory91G
Gregory91G deleted the ci/trufflehog-fp-baseline branch August 10, 2026 10:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants