From 43162b1246e84c1aafda7627238a5b3578c0b70b Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Wed, 7 Jan 2026 16:13:41 +0530 Subject: [PATCH 01/49] Add TruffleHog secret scanning workflow and docs Introduces a centralized GitHub Actions workflow for scanning pull requests for secrets using TruffleHog. Includes a detailed README with setup instructions, exclusion pattern configuration, override options, and troubleshooting guidance. --- .github/workflows/trufflehog-scan.yml | 73 ++++++++++++ README.md | 162 ++++++++++++++++++++++++++ 2 files changed, 235 insertions(+) create mode 100644 .github/workflows/trufflehog-scan.yml create mode 100644 README.md diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml new file mode 100644 index 0000000..b3ce861 --- /dev/null +++ b/.github/workflows/trufflehog-scan.yml @@ -0,0 +1,73 @@ +name: TruffleHog Secret Scan + +on: + pull_request: + types: [opened, synchronize, reopened] + workflow_dispatch: + +permissions: + contents: read + pull-requests: read + +# Default exclusion patterns (regex format) +# Supports: exact filenames, wildcards, regex patterns +# Examples: +# Exact file: ^config/settings\.json$ +# Directory: ^node_modules/ +# Extension: \.lock$ +# Wildcard: .*\.min\.js$ +# Regex: ^src/test/.*_test\.py$ + +env: + DEFAULT_EXCLUDES: | + ^node_modules/ + ^vendor/ + ^\.git/ + \.lock$ + ^package-lock\.json$ + ^yarn\.lock$ + ^pnpm-lock\.yaml$ + \.min\.js$ + \.min\.css$ + +jobs: + trufflehog-scan: + name: Scan PR for Secrets + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Fetch PR head for forks + if: github.event.pull_request.head.repo.full_name != github.repository + run: | + git remote add fork ${{ github.event.pull_request.head.repo.clone_url }} || true + git fetch fork ${{ github.event.pull_request.head.ref }}:pr-head + + - name: Setup exclude config + id: config + run: | + if [ -n "${{ vars.TRUFFLEHOG_EXCLUDES }}" ]; then + echo "Using repo/org-level TRUFFLEHOG_EXCLUDES variable" + # Support both comma-separated and newline-separated patterns + echo "${{ vars.TRUFFLEHOG_EXCLUDES }}" | tr ',' '\n' | sed '/^$/d' > .trufflehog-ignore + else + echo "Using default exclusions from central workflow" + cat << 'EOF' > .trufflehog-ignore + ${{ env.DEFAULT_EXCLUDES }} + EOF + fi + + echo "Exclusion patterns:" + cat .trufflehog-ignore + echo "exclude_args=--exclude-paths=.trufflehog-ignore" >> $GITHUB_OUTPUT + + - name: TruffleHog Scan + uses: trufflesecurity/trufflehog@main + with: + base: ${{ github.event.pull_request.base.sha }} + head: ${{ github.event.pull_request.head.sha }} + extra_args: --only-verified ${{ steps.config.outputs.exclude_args }} diff --git a/README.md b/README.md new file mode 100644 index 0000000..ba0f312 --- /dev/null +++ b/README.md @@ -0,0 +1,162 @@ +# TruffleHog Secret Scanning Workflow + +Centralized GitHub Actions workflow that automatically scans all pull requests for exposed secrets (API keys, passwords, tokens, etc.) across your organization. + +## Features + +- Scans only modified files in PRs (fast and efficient) +- Works with PRs from forks +- Configurable exclusion patterns using regex +- Supports org-level defaults with repo-level overrides +- No workflow file needed in individual repos (uses org rulesets) + +## Setup +### Set Default Exclusions (Optional) + +Set organization-wide exclusion patterns: + +1. Go to **Organization** > **Settings** > **Secrets and variables** > **Actions** +2. Click **Variables** tab > **New organization variable** +3. Configure: + +| Field | Value | +|-------|-------| +| Name | `TRUFFLEHOG_EXCLUDES` | +| Value | Comma-separated regex patterns (see examples below) | +| Repository access | `All repositories` or select specific ones | + +## Exclusion Patterns + +### Setting Exclusions + +Exclusions are configured via the `TRUFFLEHOG_EXCLUDES` variable using regex patterns. + +**Priority order:** +1. Repository-level variable (highest priority) +2. Organization-level variable +3. Workflow defaults (if no variable set) + +### Pattern Reference + +| What to Exclude | Pattern | Example Match | +|-----------------|---------|---------------| +| Exact file | `^path/to/file\.json$` | `path/to/file.json` | +| Directory | `^node_modules/` | `node_modules/package/index.js` | +| File extension | `\.lock$` | `package-lock.json`, `yarn.lock` | +| Multiple extensions | `\.(md\|txt)$` | `README.md`, `notes.txt` | +| Test files | `_test\.py$` | `user_test.py` | +| Minified files | `\.min\.(js\|css)$` | `app.min.js`, `style.min.css` | +| Multiple directories | `^(vendor\|dist\|build)/` | `vendor/lib.js`, `dist/app.js` | +| Hidden files | `^\.[^/]+$` | `.gitignore`, `.env.example` | +| Config examples | `\.example$` | `.env.example` | + +### Regex Syntax Reference + +| Symbol | Meaning | Example | +|--------|---------|---------| +| `^` | Start of path | `^src/` matches paths starting with `src/` | +| `$` | End of path | `\.js$` matches files ending in `.js` | +| `\.` | Literal dot | `\.json$` matches `.json` extension | +| `.*` | Any characters | `^src/.*\.js$` matches any `.js` in `src/` | +| `(a\|b)` | OR operator | `\.(js\|ts)$` matches `.js` or `.ts` | +| `[^/]` | Any char except `/` | `^\.[^/]+$` matches hidden files in root | + +### Example Variable Values + +**Basic exclusions:** +``` +^node_modules/,^vendor/,\.lock$,\.min\.js$ +``` + +**Development files:** +``` +^test/,^tests/,^spec/,_test\.(js|py)$,\.test\.(js|ts)$ +``` + +**Documentation and configs:** +``` +^docs/,\.md$,^\.github/,\.example$ +``` + +**Comprehensive exclusion:** +``` +^node_modules/,^vendor/,^dist/,^build/,\.lock$,\.min\.(js|css)$,^docs/,_test\.py$,\.test\.(js|ts)$,\.example$ +``` + +## Override at Repository Level + +Individual repos can override org defaults: + +1. Go to **Repository** > **Settings** > **Secrets and variables** > **Actions** +2. Click **Variables** tab > **New repository variable** +3. Name: `TRUFFLEHOG_EXCLUDES` +4. Value: Your comma-separated regex patterns + +This completely replaces org-level patterns for that repository. + +## Default Exclusions + +If no `TRUFFLEHOG_EXCLUDES` variable is set, these defaults apply: + +``` +^node_modules/ +^vendor/ +^\.git/ +\.lock$ +^package-lock\.json$ +^yarn\.lock$ +^pnpm-lock\.yaml$ +\.min\.js$ +\.min\.css$ +``` + +## Workflow Triggers + +| Trigger | When | +|---------|------| +| `pull_request` | PR opened, updated, or reopened | +| `workflow_dispatch` | Manual run from Actions tab | + +## Viewing Results + +1. Go to the **Pull Request** > **Checks** tab +2. Click **Scan PR for Secrets** +3. View logs for: + - Applied exclusion patterns + - Detected secrets (file, line, type) + +## Handling Detected Secrets + +If the scan fails: + +1. **Remove the secret** from your code +2. **Rotate the secret** immediately (assume it's compromised) +3. **Push the fix** to your PR branch +4. Scan re-runs automatically + +**For false positives:** Add the file/pattern to repo-level `TRUFFLEHOG_EXCLUDES` or request an update to org-level patterns. + +## Manual Scan + +To run a scan manually: + +1. Go to **Repository** > **Actions** +2. Select **TruffleHog Secret Scan** +3. Click **Run workflow** + +## Troubleshooting + +| Issue | Solution | +|-------|----------| +| Workflow not triggering | Verify ruleset is Active and targets correct repos/branches | +| Can't access workflow | Enable "Accessible from repositories in the organization" in this repo's Actions settings | +| Exclusions not working | Check regex syntax; view workflow logs for applied patterns | +| Variable not found | Confirm `TRUFFLEHOG_EXCLUDES` is set at org or repo level | +| Fork PRs failing | Workflow handles forks automatically; ensure fork has access | + +## Support + +For issues or questions: +- Check workflow run logs for detailed error messages +- Review exclusion patterns for regex errors +- Contact your PDP Pioneers's team From 200cee2c697bb2f2da7dcd5d6df2a4439a83c775 Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Wed, 7 Jan 2026 17:17:15 +0530 Subject: [PATCH 02/49] Update README.md --- README.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/README.md b/README.md index ba0f312..fbe843f 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,49 @@ If no `TRUFFLEHOG_EXCLUDES` variable is set, these defaults apply: \.min\.css$ ``` +## How It Works at Runtime + +``` +PR Created/Updated + | + v +Ruleset Triggers Workflow + | + v +Checkout Repository + | + v +Check for TRUFFLEHOG_EXCLUDES variable + | + +------------------+------------------+ + | | | + v v v + Repo variable Org variable Neither set + exists? exists? | + | | v + v v Use DEFAULT_EXCLUDES + Use it Use it from workflow + | | | + +------------------+------------------+ + | + v + Create .trufflehog-ignore file + | + v + Run TruffleHog scan on PR diff + (only modified files between base and head) + | + +-----------+-----------+ + | | + v v + Secrets found No secrets found + | | + v v + FAIL - PR blocked PASS - PR allowed +``` + +**Scan scope:** Only files modified in the PR are scanned, not the entire repository. + ## Workflow Triggers | Trigger | When | From bf7b85b3189cdd3d495f2b71a665a0856a150df0 Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Wed, 7 Jan 2026 17:33:06 +0530 Subject: [PATCH 03/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index b3ce861..e6f07f2 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -70,4 +70,4 @@ jobs: with: base: ${{ github.event.pull_request.base.sha }} head: ${{ github.event.pull_request.head.sha }} - extra_args: --only-verified ${{ steps.config.outputs.exclude_args }} + extra_args: ${{ steps.config.outputs.exclude_args }} From 527f3f230e485e99dc7d1b9146a8e30812db0519 Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Wed, 7 Jan 2026 18:11:00 +0530 Subject: [PATCH 04/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index e6f07f2..8fca103 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -3,6 +3,8 @@ name: TruffleHog Secret Scan on: pull_request: types: [opened, synchronize, reopened] + pull_request_target: + types: [opened, synchronize, reopened] workflow_dispatch: permissions: @@ -34,6 +36,12 @@ jobs: trufflehog-scan: name: Scan PR for Secrets runs-on: ubuntu-latest + # Run pull_request_target only for fork PRs, pull_request only for same-repo PRs + # This prevents duplicate runs + if: | + (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository) || + (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || + github.event_name == 'workflow_dispatch' steps: - name: Checkout repository From 599658fd26a9ced39e265afefa40f20a488dcd90 Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Wed, 7 Jan 2026 18:16:30 +0530 Subject: [PATCH 05/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 8fca103..b5e639c 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -49,11 +49,12 @@ jobs: with: fetch-depth: 0 - - name: Fetch PR head for forks - if: github.event.pull_request.head.repo.full_name != github.repository + - name: Fetch PR head commits + if: github.event_name != 'workflow_dispatch' run: | - git remote add fork ${{ github.event.pull_request.head.repo.clone_url }} || true - git fetch fork ${{ github.event.pull_request.head.ref }}:pr-head + # Fetch PR commits using GitHub's merge ref (works for all PRs including forks) + git fetch origin +refs/pull/${{ github.event.pull_request.number }}/head:refs/remotes/origin/pr-head + echo "Fetched PR #${{ github.event.pull_request.number }} head commit: ${{ github.event.pull_request.head.sha }}" - name: Setup exclude config id: config From f42179b948632bb4b0d839b7de95e191aeb802ca Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Wed, 7 Jan 2026 18:49:53 +0530 Subject: [PATCH 06/49] Enhance TruffleHog workflow with PR comments and commit status The workflow now posts PR comments with secret scan findings, sets commit status to pass/fail, and provides clearer merge blocking. Documentation was updated and renamed to trufflehog_readme.md to reflect new features, including secret classification and improved fork PR support. --- .github/workflows/trufflehog-scan.yml | 97 ++++++++++++++++++++++++++- README.md => trufflehog_readme.md | 96 +++++++++++++++++++++----- 2 files changed, 175 insertions(+), 18 deletions(-) rename README.md => trufflehog_readme.md (64%) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index b5e639c..62ea6b9 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -9,7 +9,8 @@ on: permissions: contents: read - pull-requests: read + pull-requests: write + statuses: write # Default exclusion patterns (regex format) # Supports: exact filenames, wildcards, regex patterns @@ -75,8 +76,100 @@ jobs: echo "exclude_args=--exclude-paths=.trufflehog-ignore" >> $GITHUB_OUTPUT - name: TruffleHog Scan + id: trufflehog uses: trufflesecurity/trufflehog@main + continue-on-error: true with: base: ${{ github.event.pull_request.base.sha }} head: ${{ github.event.pull_request.head.sha }} - extra_args: ${{ steps.config.outputs.exclude_args }} + extra_args: --json ${{ steps.config.outputs.exclude_args }} + + - name: Process scan results + id: process + if: github.event_name != 'workflow_dispatch' + run: | + # Check if TruffleHog found any secrets + if [ "${{ steps.trufflehog.outcome }}" == "failure" ]; then + echo "has_secrets=true" >> $GITHUB_OUTPUT + echo "status=failure" >> $GITHUB_OUTPUT + echo "description=Secret scanning found exposed credentials" >> $GITHUB_OUTPUT + else + echo "has_secrets=false" >> $GITHUB_OUTPUT + echo "status=success" >> $GITHUB_OUTPUT + echo "description=No secrets detected in PR changes" >> $GITHUB_OUTPUT + fi + + - name: Post PR comment on findings + if: steps.process.outputs.has_secrets == 'true' && github.event_name != 'workflow_dispatch' + uses: actions/github-script@v7 + with: + script: | + const commentMarker = ''; + const body = `${commentMarker} + ## :rotating_light: Secret Scanning Alert + + **TruffleHog detected potential secrets in this pull request.** + + ### What to do: + 1. **Review the workflow logs** for detailed findings (file, line number, secret type) + 2. **Remove the exposed secret** from your code + 3. **Rotate the credential immediately** - assume it's compromised + 4. **Push the fix** to this branch + + ### Finding Details + Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for: + - File paths containing secrets + - Line numbers + - Secret types (API key, password, token, etc.) + - Verification status (verified = confirmed active) + + --- + *This scan only checks files modified in this PR. Secrets are classified as **verified** (confirmed active) or **unverified** (potential match).* + `; + + // Find existing comment + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + per_page: 100 + }); + + const existing = comments.find(c => c.body && c.body.includes(commentMarker)); + + if (existing) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body: body + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + body: body + }); + } + + - name: Set commit status + if: github.event_name != 'workflow_dispatch' + uses: actions/github-script@v7 + with: + script: | + await github.rest.repos.createCommitStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + sha: '${{ github.event.pull_request.head.sha }}', + state: '${{ steps.process.outputs.status }}', + target_url: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}', + description: '${{ steps.process.outputs.description }}', + context: 'TruffleHog Secret Scan' + }); + + - name: Fail workflow if secrets found + if: steps.process.outputs.has_secrets == 'true' + run: | + echo "::error::Secrets detected in PR. Review the logs and PR comment for details." + exit 1 diff --git a/README.md b/trufflehog_readme.md similarity index 64% rename from README.md rename to trufflehog_readme.md index fbe843f..52c9ba0 100644 --- a/README.md +++ b/trufflehog_readme.md @@ -5,10 +5,13 @@ Centralized GitHub Actions workflow that automatically scans all pull requests f ## Features - Scans only modified files in PRs (fast and efficient) -- Works with PRs from forks +- Works with PRs from forks (public and private) - Configurable exclusion patterns using regex - Supports org-level defaults with repo-level overrides - No workflow file needed in individual repos (uses org rulesets) +- Posts PR comments with detailed findings when secrets are detected +- Sets commit status to pass/fail for clear merge blocking +- Classifies secrets as verified (confirmed active) or unverified (potential match) ## Setup ### Set Default Exclusions (Optional) @@ -116,15 +119,30 @@ If no `TRUFFLEHOG_EXCLUDES` variable is set, these defaults apply: PR Created/Updated | v -Ruleset Triggers Workflow - | - v -Checkout Repository - | - v -Check for TRUFFLEHOG_EXCLUDES variable +Determine PR Type | +------------------+------------------+ + | | + v v + Fork PR Same-repo PR + | | + v v + pull_request_target pull_request + trigger runs trigger runs + | | + +------------------+------------------+ + | + v + Checkout Base Repository + | + v + Fetch PR Head Commits + (using refs/pull/{number}/head) + | + v + Check for TRUFFLEHOG_EXCLUDES variable + | + +------------------+------------------+ | | | v v v Repo variable Org variable Neither set @@ -148,25 +166,71 @@ Check for TRUFFLEHOG_EXCLUDES variable Secrets found No secrets found | | v v - FAIL - PR blocked PASS - PR allowed + Post PR comment Set commit status + with findings to success + | | + v v + Set commit status PASS - PR allowed + to failure + | + v + FAIL - PR blocked ``` **Scan scope:** Only files modified in the PR are scanned, not the entire repository. +## Secret Classification + +TruffleHog classifies detected secrets into two categories: + +| Type | Description | Action | +|------|-------------|--------| +| **Verified** | Confirmed active/valid credentials | Blocks PR, requires immediate rotation | +| **Unverified** | Potential secrets that couldn't be validated | Warning in logs, review recommended | + +## PR Comments + +When secrets are detected, the workflow automatically posts a comment on the PR with: +- Link to workflow logs for detailed findings +- Instructions for removing and rotating secrets +- Information about file paths, line numbers, and secret types + +When no secrets are found, no comment is posted to keep the PR clean. + ## Workflow Triggers -| Trigger | When | -|---------|------| -| `pull_request` | PR opened, updated, or reopened | -| `workflow_dispatch` | Manual run from Actions tab | +The workflow uses dual triggers to handle both same-repo and fork PRs efficiently: + +| Trigger | Used For | Description | +|---------|----------|-------------| +| `pull_request` | Same-repo PRs | Standard trigger for PRs within the repository | +| `pull_request_target` | Fork PRs | Runs in base repo context, works for private forks | +| `workflow_dispatch` | Manual runs | Trigger manually from Actions tab | + +**Duplicate prevention:** The workflow includes logic to ensure only one trigger runs per PR: +- Fork PRs: Only `pull_request_target` runs +- Same-repo PRs: Only `pull_request` runs + +## Fork PR Support + +The workflow fully supports PRs from forked repositories: + +- Uses GitHub's `refs/pull/{number}/head` to fetch PR commits +- Works with both public and private forks +- No direct access to fork repository required +- Runs immediately without requiring maintainer approval ## Viewing Results 1. Go to the **Pull Request** > **Checks** tab -2. Click **Scan PR for Secrets** -3. View logs for: +2. Look for **TruffleHog Secret Scan** commit status +3. If secrets are found: + - A PR comment will be posted with remediation steps + - Click the status link to view detailed logs +4. View logs for: - Applied exclusion patterns - - Detected secrets (file, line, type) + - Detected secrets (file, line, secret type) + - Verification status (verified = confirmed active) ## Handling Detected Secrets From e496bac1e0d05b38503d9e0e180d3cde535d0054 Mon Sep 17 00:00:00 2001 From: GAdityaVarma Date: Wed, 7 Jan 2026 19:03:57 +0530 Subject: [PATCH 07/49] Update trufflehog_readme.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- trufflehog_readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trufflehog_readme.md b/trufflehog_readme.md index 52c9ba0..75e615e 100644 --- a/trufflehog_readme.md +++ b/trufflehog_readme.md @@ -266,4 +266,4 @@ To run a scan manually: For issues or questions: - Check workflow run logs for detailed error messages - Review exclusion patterns for regex errors -- Contact your PDP Pioneers's team +- Contact your PDP Pioneers team From d279b6681ff1f95ae4b9b85d079a7e4a6132f31c Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Thu, 8 Jan 2026 10:44:49 +0530 Subject: [PATCH 08/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 90 ++++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 10 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 62ea6b9..cef114a 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -82,7 +82,57 @@ jobs: with: base: ${{ github.event.pull_request.base.sha }} head: ${{ github.event.pull_request.head.sha }} - extra_args: --json ${{ steps.config.outputs.exclude_args }} + extra_args: --json --fail ${{ steps.config.outputs.exclude_args }} + + - name: Capture scan output + id: capture + if: github.event_name != 'workflow_dispatch' + run: | + # Capture TruffleHog output from the action's output file if available + # The trufflehog action writes JSON to stdout, we need to parse workflow logs + # For now, we'll run trufflehog again to capture output to a file + echo "Running TruffleHog to capture detailed output..." + + # Install trufflehog CLI + curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh | sh -s -- -b /usr/local/bin + + # Run scan and capture JSON output + trufflehog git file://. --since-commit=${{ github.event.pull_request.base.sha }} \ + --branch=origin/pr-head --json --fail \ + ${{ steps.config.outputs.exclude_args }} 2>/dev/null > trufflehog_results.json || true + + # Check if we have results + if [ -s trufflehog_results.json ]; then + echo "has_results=true" >> $GITHUB_OUTPUT + + # Parse JSON and create markdown table + # Each line is a separate JSON object + echo "| File | Line | Secret Type | Verified |" > findings_table.md + echo "|------|------|-------------|----------|" >> findings_table.md + + while IFS= read -r line; do + if [ -n "$line" ]; then + file=$(echo "$line" | jq -r '.SourceMetadata.Data.Git.file // "N/A"') + line_num=$(echo "$line" | jq -r '.SourceMetadata.Data.Git.line // "N/A"') + detector=$(echo "$line" | jq -r '.DetectorName // "Unknown"') + verified=$(echo "$line" | jq -r 'if .Verified then "Yes" else "No" end') + echo "| \`$file\` | $line_num | $detector | $verified |" >> findings_table.md + fi + done < trufflehog_results.json + + # Store table content for PR comment + findings_table=$(cat findings_table.md) + # Escape for GitHub Actions output + findings_table="${findings_table//'%'/'%25'}" + findings_table="${findings_table//$'\n'/'%0A'}" + findings_table="${findings_table//$'\r'/'%0D'}" + echo "findings_table<> $GITHUB_OUTPUT + cat findings_table.md >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + else + echo "has_results=false" >> $GITHUB_OUTPUT + echo "findings_table=" >> $GITHUB_OUTPUT + fi - name: Process scan results id: process @@ -102,26 +152,46 @@ jobs: - name: Post PR comment on findings if: steps.process.outputs.has_secrets == 'true' && github.event_name != 'workflow_dispatch' uses: actions/github-script@v7 + env: + FINDINGS_TABLE: ${{ steps.capture.outputs.findings_table }} with: script: | const commentMarker = ''; - const body = `${commentMarker} - ## :rotating_light: Secret Scanning Alert - - **TruffleHog detected potential secrets in this pull request.** + const findingsTable = process.env.FINDINGS_TABLE || ''; + + let findingsSection = ''; + if (findingsTable && findingsTable.trim()) { + findingsSection = ` + ### Detected Secrets - ### What to do: - 1. **Review the workflow logs** for detailed findings (file, line number, secret type) - 2. **Remove the exposed secret** from your code - 3. **Rotate the credential immediately** - assume it's compromised - 4. **Push the fix** to this branch + ${findingsTable} + > **Note:** "Verified = Yes" means the credential was confirmed to be active/valid. + `; + } else { + findingsSection = ` ### Finding Details Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for: - File paths containing secrets - Line numbers - Secret types (API key, password, token, etc.) - Verification status (verified = confirmed active) + `; + } + + const body = `${commentMarker} + ## :rotating_light: Secret Scanning Alert + + **TruffleHog detected potential secrets in this pull request.** + + ### What to do: + 1. **Remove the exposed secret** from your code + 2. **Rotate the credential immediately** - assume it's compromised + 3. **Push the fix** to this branch + 4. The scan will re-run automatically + ${findingsSection} + ### Workflow Logs + [View detailed scan logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) --- *This scan only checks files modified in this PR. Secrets are classified as **verified** (confirmed active) or **unverified** (potential match).* From 1aae01bb723e4ae3682de32a3d6239590ba9950f Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Thu, 8 Jan 2026 18:05:58 +0530 Subject: [PATCH 09/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 113 +++++--------------------- 1 file changed, 22 insertions(+), 91 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index cef114a..5b24794 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -77,68 +77,18 @@ jobs: - name: TruffleHog Scan id: trufflehog + if: github.event_name != 'workflow_dispatch' uses: trufflesecurity/trufflehog@main continue-on-error: true with: base: ${{ github.event.pull_request.base.sha }} head: ${{ github.event.pull_request.head.sha }} - extra_args: --json --fail ${{ steps.config.outputs.exclude_args }} - - - name: Capture scan output - id: capture - if: github.event_name != 'workflow_dispatch' - run: | - # Capture TruffleHog output from the action's output file if available - # The trufflehog action writes JSON to stdout, we need to parse workflow logs - # For now, we'll run trufflehog again to capture output to a file - echo "Running TruffleHog to capture detailed output..." - - # Install trufflehog CLI - curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh | sh -s -- -b /usr/local/bin - - # Run scan and capture JSON output - trufflehog git file://. --since-commit=${{ github.event.pull_request.base.sha }} \ - --branch=origin/pr-head --json --fail \ - ${{ steps.config.outputs.exclude_args }} 2>/dev/null > trufflehog_results.json || true - - # Check if we have results - if [ -s trufflehog_results.json ]; then - echo "has_results=true" >> $GITHUB_OUTPUT - - # Parse JSON and create markdown table - # Each line is a separate JSON object - echo "| File | Line | Secret Type | Verified |" > findings_table.md - echo "|------|------|-------------|----------|" >> findings_table.md - - while IFS= read -r line; do - if [ -n "$line" ]; then - file=$(echo "$line" | jq -r '.SourceMetadata.Data.Git.file // "N/A"') - line_num=$(echo "$line" | jq -r '.SourceMetadata.Data.Git.line // "N/A"') - detector=$(echo "$line" | jq -r '.DetectorName // "Unknown"') - verified=$(echo "$line" | jq -r 'if .Verified then "Yes" else "No" end') - echo "| \`$file\` | $line_num | $detector | $verified |" >> findings_table.md - fi - done < trufflehog_results.json - - # Store table content for PR comment - findings_table=$(cat findings_table.md) - # Escape for GitHub Actions output - findings_table="${findings_table//'%'/'%25'}" - findings_table="${findings_table//$'\n'/'%0A'}" - findings_table="${findings_table//$'\r'/'%0D'}" - echo "findings_table<> $GITHUB_OUTPUT - cat findings_table.md >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - else - echo "has_results=false" >> $GITHUB_OUTPUT - echo "findings_table=" >> $GITHUB_OUTPUT - fi + extra_args: --fail ${{ steps.config.outputs.exclude_args }} - name: Process scan results id: process if: github.event_name != 'workflow_dispatch' run: | - # Check if TruffleHog found any secrets if [ "${{ steps.trufflehog.outcome }}" == "failure" ]; then echo "has_secrets=true" >> $GITHUB_OUTPUT echo "status=failure" >> $GITHUB_OUTPUT @@ -152,50 +102,31 @@ jobs: - name: Post PR comment on findings if: steps.process.outputs.has_secrets == 'true' && github.event_name != 'workflow_dispatch' uses: actions/github-script@v7 - env: - FINDINGS_TABLE: ${{ steps.capture.outputs.findings_table }} with: script: | const commentMarker = ''; - const findingsTable = process.env.FINDINGS_TABLE || ''; - - let findingsSection = ''; - if (findingsTable && findingsTable.trim()) { - findingsSection = ` - ### Detected Secrets - - ${findingsTable} - - > **Note:** "Verified = Yes" means the credential was confirmed to be active/valid. - `; - } else { - findingsSection = ` - ### Finding Details - Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for: - - File paths containing secrets - - Line numbers - - Secret types (API key, password, token, etc.) - - Verification status (verified = confirmed active) - `; - } const body = `${commentMarker} - ## :rotating_light: Secret Scanning Alert - - **TruffleHog detected potential secrets in this pull request.** - - ### What to do: - 1. **Remove the exposed secret** from your code - 2. **Rotate the credential immediately** - assume it's compromised - 3. **Push the fix** to this branch - 4. The scan will re-run automatically - ${findingsSection} - ### Workflow Logs - [View detailed scan logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) - - --- - *This scan only checks files modified in this PR. Secrets are classified as **verified** (confirmed active) or **unverified** (potential match).* - `; +## :rotating_light: Secret Scanning Alert + +**TruffleHog detected potential secrets in this pull request.** + +### What to do: +1. **Remove the exposed secret** from your code +2. **Rotate the credential immediately** - assume it's compromised +3. **Push the fix** to this branch +4. The scan will re-run automatically + +### Finding Details +Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for: +- File paths containing secrets +- Line numbers +- Secret types (API key, password, token, etc.) +- Verification status (verified = confirmed active) + +--- +*This scan only checks files modified in this PR. Secrets are classified as **verified** (confirmed active) or **unverified** (potential match).* +`; // Find existing comment const { data: comments } = await github.rest.issues.listComments({ From 77909eb3523438347239e36483298319395e019d Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Thu, 8 Jan 2026 18:14:55 +0530 Subject: [PATCH 10/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 39 +++++++++++++-------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 5b24794..57e58ec 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -77,18 +77,18 @@ jobs: - name: TruffleHog Scan id: trufflehog - if: github.event_name != 'workflow_dispatch' uses: trufflesecurity/trufflehog@main continue-on-error: true with: base: ${{ github.event.pull_request.base.sha }} head: ${{ github.event.pull_request.head.sha }} - extra_args: --fail ${{ steps.config.outputs.exclude_args }} + extra_args: --json ${{ steps.config.outputs.exclude_args }} - name: Process scan results id: process if: github.event_name != 'workflow_dispatch' run: | + # Check if TruffleHog found any secrets if [ "${{ steps.trufflehog.outcome }}" == "failure" ]; then echo "has_secrets=true" >> $GITHUB_OUTPUT echo "status=failure" >> $GITHUB_OUTPUT @@ -105,28 +105,27 @@ jobs: with: script: | const commentMarker = ''; - const body = `${commentMarker} -## :rotating_light: Secret Scanning Alert + ## :rotating_light: Secret Scanning Alert -**TruffleHog detected potential secrets in this pull request.** + **TruffleHog detected potential secrets in this pull request.** -### What to do: -1. **Remove the exposed secret** from your code -2. **Rotate the credential immediately** - assume it's compromised -3. **Push the fix** to this branch -4. The scan will re-run automatically + ### What to do: + 1. **Review the workflow logs** for detailed findings (file, line number, secret type) + 2. **Remove the exposed secret** from your code + 3. **Rotate the credential immediately** - assume it's compromised + 4. **Push the fix** to this branch -### Finding Details -Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for: -- File paths containing secrets -- Line numbers -- Secret types (API key, password, token, etc.) -- Verification status (verified = confirmed active) + ### Finding Details + Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for: + - File paths containing secrets + - Line numbers + - Secret types (API key, password, token, etc.) + - Verification status (verified = confirmed active) ---- -*This scan only checks files modified in this PR. Secrets are classified as **verified** (confirmed active) or **unverified** (potential match).* -`; + --- + *This scan only checks files modified in this PR. Secrets are classified as **verified** (confirmed active) or **unverified** (potential match).* + `; // Find existing comment const { data: comments } = await github.rest.issues.listComments({ @@ -173,4 +172,4 @@ Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/ if: steps.process.outputs.has_secrets == 'true' run: | echo "::error::Secrets detected in PR. Review the logs and PR comment for details." - exit 1 + exit 1 \ No newline at end of file From c120b0979151f6a80e0e335e905391893ef37f4e Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Thu, 8 Jan 2026 18:20:08 +0530 Subject: [PATCH 11/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 65 ++++++++++++++++----------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 57e58ec..b1bca01 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -60,15 +60,16 @@ jobs: - name: Setup exclude config id: config run: | - if [ -n "${{ vars.TRUFFLEHOG_EXCLUDES }}" ]; then - echo "Using repo/org-level TRUFFLEHOG_EXCLUDES variable" - # Support both comma-separated and newline-separated patterns - echo "${{ vars.TRUFFLEHOG_EXCLUDES }}" | tr ',' '\n' | sed '/^$/d' > .trufflehog-ignore - else - echo "Using default exclusions from central workflow" - cat << 'EOF' > .trufflehog-ignore + # Always include default exclusions first + echo "Adding default exclusions..." + cat << 'EOF' > .trufflehog-ignore ${{ env.DEFAULT_EXCLUDES }} EOF + + # Append user-defined exclusions if set (additive, not replacement) + if [ -n "${{ vars.TRUFFLEHOG_EXCLUDES }}" ]; then + echo "Adding repo/org-level TRUFFLEHOG_EXCLUDES patterns..." + echo "${{ vars.TRUFFLEHOG_EXCLUDES }}" | tr ',' '\n' | sed '/^$/d' >> .trufflehog-ignore fi echo "Exclusion patterns:" @@ -105,27 +106,37 @@ jobs: with: script: | const commentMarker = ''; + const commitSha = '${{ github.event.pull_request.head.sha }}'; + const shortSha = commitSha.substring(0, 7); + const scanTime = new Date().toISOString().replace('T', ' ').substring(0, 19) + ' UTC'; + const body = `${commentMarker} - ## :rotating_light: Secret Scanning Alert - - **TruffleHog detected potential secrets in this pull request.** - - ### What to do: - 1. **Review the workflow logs** for detailed findings (file, line number, secret type) - 2. **Remove the exposed secret** from your code - 3. **Rotate the credential immediately** - assume it's compromised - 4. **Push the fix** to this branch - - ### Finding Details - Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for: - - File paths containing secrets - - Line numbers - - Secret types (API key, password, token, etc.) - - Verification status (verified = confirmed active) - - --- - *This scan only checks files modified in this PR. Secrets are classified as **verified** (confirmed active) or **unverified** (potential match).* - `; +## :rotating_light: Secret Scanning Alert + +**TruffleHog detected potential secrets in this pull request.** + +| Scan Details | | +|--------------|---| +| **Commit** | [\`${shortSha}\`](${{ github.server_url }}/${{ github.repository }}/commit/${commitSha}) | +| **Scanned At** | ${scanTime} | +| **Workflow Run** | [View Logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) | + +### What to do: +1. **Remove the exposed secret** from your code +2. **Rotate the credential immediately** - assume it's compromised +3. **Push the fix** to this branch +4. The scan will re-run automatically + +### Finding Details +Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for: +- File paths containing secrets +- Line numbers +- Secret types (API key, password, token, etc.) +- Verification status (verified = confirmed active) + +--- +*Only files modified in this PR were scanned. Secrets are classified as **verified** (confirmed active) or **unverified** (potential match).* +`; // Find existing comment const { data: comments } = await github.rest.issues.listComments({ From 174b7d065ed0d6ed6d8045ef3aa3612baab04a16 Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Thu, 8 Jan 2026 18:25:03 +0530 Subject: [PATCH 12/49] Update TruffleHog workflow to handle resolved secrets Adds a workflow step to update the PR comment when previously detected secrets are resolved, marking the PR as clear. Updates documentation to clarify that exclusion patterns are additive, describes the new comment update behavior, and improves the remediation and PR comment sections for clarity. --- .github/workflows/trufflehog-scan.yml | 51 +++++++++++++++++++++++++++ trufflehog_readme.md | 51 +++++++++++++++++++-------- 2 files changed, 88 insertions(+), 14 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index b1bca01..f9d8a00 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -164,6 +164,57 @@ Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/ }); } + - name: Update PR comment when secrets resolved + if: steps.process.outputs.has_secrets == 'false' && github.event_name != 'workflow_dispatch' + uses: actions/github-script@v7 + with: + script: | + const commentMarker = ''; + const commitSha = '${{ github.event.pull_request.head.sha }}'; + const shortSha = commitSha.substring(0, 7); + const scanTime = new Date().toISOString().replace('T', ' ').substring(0, 19) + ' UTC'; + + // Check if there's an existing alert comment from a previous failed scan + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + per_page: 100 + }); + + const existing = comments.find(c => c.body && c.body.includes(commentMarker)); + + // Only update if there was a previous alert - don't create new comments for clean scans + if (existing) { + const resolvedBody = `${commentMarker} +## :white_check_mark: Secret Scanning Passed + +**Previously detected secrets have been resolved.** + +| Scan Details | | +|--------------|---| +| **Commit** | [\`${shortSha}\`](${{ github.server_url }}/${{ github.repository }}/commit/${commitSha}) | +| **Scanned At** | ${scanTime} | +| **Workflow Run** | [View Logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) | + +The secrets flagged in previous scans are no longer detected in the modified files. + +--- +*This PR is now clear of detected secrets. Remember to rotate any credentials that were previously exposed.* +`; + + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body: resolvedBody + }); + + console.log('Updated existing alert comment to show resolved status'); + } else { + console.log('No previous alert comment found - scan passed cleanly'); + } + - name: Set commit status if: github.event_name != 'workflow_dispatch' uses: actions/github-script@v7 diff --git a/trufflehog_readme.md b/trufflehog_readme.md index 75e615e..f41686b 100644 --- a/trufflehog_readme.md +++ b/trufflehog_readme.md @@ -88,14 +88,14 @@ Exclusions are configured via the `TRUFFLEHOG_EXCLUDES` variable using regex pat ## Override at Repository Level -Individual repos can override org defaults: +Individual repos can add additional exclusions on top of the defaults: 1. Go to **Repository** > **Settings** > **Secrets and variables** > **Actions** 2. Click **Variables** tab > **New repository variable** 3. Name: `TRUFFLEHOG_EXCLUDES` 4. Value: Your comma-separated regex patterns -This completely replaces org-level patterns for that repository. +**Exclusions are additive:** Your patterns are combined with the default exclusions. You don't need to repeat common patterns like `node_modules/` or `.lock` files. ## Default Exclusions @@ -166,15 +166,22 @@ Determine PR Type Secrets found No secrets found | | v v - Post PR comment Set commit status - with findings to success - | | - v v - Set commit status PASS - PR allowed - to failure - | - v - FAIL - PR blocked + Post/Update PR Check for previous + comment with alert comment + findings | + | +--------+--------+ + | | | + v v v + Set commit status Exists? No comment + to failure | (clean PR) + | v | + v Update to v + FAIL - PR "Resolved" Set commit status + blocked status to success + | | + v v + Set commit PASS - PR allowed + to success ``` **Scan scope:** Only files modified in the PR are scanned, not the entire repository. @@ -190,12 +197,27 @@ TruffleHog classifies detected secrets into two categories: ## PR Comments -When secrets are detected, the workflow automatically posts a comment on the PR with: +The workflow manages PR comments to provide clear feedback throughout the remediation process: + +### When Secrets Are Detected + +A comment is posted with: +- Commit SHA that was scanned +- Timestamp of the scan - Link to workflow logs for detailed findings - Instructions for removing and rotating secrets - Information about file paths, line numbers, and secret types -When no secrets are found, no comment is posted to keep the PR clean. +### When Secrets Are Resolved + +If you fix the secrets and push again: +- The **same comment is updated** to show a "Passed" status +- Shows the new commit SHA that resolved the issue +- Includes a reminder to rotate any previously exposed credentials + +### Clean PRs + +If a PR never had secrets detected, no comment is posted to keep the PR clean. ## Workflow Triggers @@ -240,8 +262,9 @@ If the scan fails: 2. **Rotate the secret** immediately (assume it's compromised) 3. **Push the fix** to your PR branch 4. Scan re-runs automatically +5. PR comment updates to show "Resolved" status when fixed -**For false positives:** Add the file/pattern to repo-level `TRUFFLEHOG_EXCLUDES` or request an update to org-level patterns. +**For false positives:** Add the file/pattern to repo-level `TRUFFLEHOG_EXCLUDES` (patterns are additive to defaults). ## Manual Scan From 7ded0239e10d690274d635466076b0021cc213ca Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Thu, 8 Jan 2026 18:30:55 +0530 Subject: [PATCH 13/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index f9d8a00..3a883b2 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -32,6 +32,7 @@ env: ^pnpm-lock\.yaml$ \.min\.js$ \.min\.css$ + ^\.github/workflows/ jobs: trufflehog-scan: From 40260d7272dd7f8201255a68d9bd6715434e17f6 Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Thu, 8 Jan 2026 19:26:49 +0530 Subject: [PATCH 14/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 3a883b2..6b28c16 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -5,7 +5,6 @@ on: types: [opened, synchronize, reopened] pull_request_target: types: [opened, synchronize, reopened] - workflow_dispatch: permissions: contents: read From 5907188fcf39cd7d3ca6ea11ce473a555f1a27e9 Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 9 Jan 2026 11:54:25 +0530 Subject: [PATCH 15/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 6b28c16..3a883b2 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -5,6 +5,7 @@ on: types: [opened, synchronize, reopened] pull_request_target: types: [opened, synchronize, reopened] + workflow_dispatch: permissions: contents: read From c38506cac6e63c9b431d2ce3251abc25f75dbe48 Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 9 Jan 2026 11:59:22 +0530 Subject: [PATCH 16/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 3a883b2..b440ff8 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -60,17 +60,18 @@ jobs: - name: Setup exclude config id: config + env: + DEFAULT_PATTERNS: ${{ env.DEFAULT_EXCLUDES }} + USER_PATTERNS: ${{ vars.TRUFFLEHOG_EXCLUDES }} run: | - # Always include default exclusions first + # Write default exclusions echo "Adding default exclusions..." - cat << 'EOF' > .trufflehog-ignore - ${{ env.DEFAULT_EXCLUDES }} - EOF + echo "$DEFAULT_PATTERNS" > .trufflehog-ignore # Append user-defined exclusions if set (additive, not replacement) - if [ -n "${{ vars.TRUFFLEHOG_EXCLUDES }}" ]; then + if [ -n "$USER_PATTERNS" ]; then echo "Adding repo/org-level TRUFFLEHOG_EXCLUDES patterns..." - echo "${{ vars.TRUFFLEHOG_EXCLUDES }}" | tr ',' '\n' | sed '/^$/d' >> .trufflehog-ignore + echo "$USER_PATTERNS" | tr ',' '\n' | sed '/^$/d' >> .trufflehog-ignore fi echo "Exclusion patterns:" From 6f0f819255b2cf0d2449d1a4813c578bd8a19609 Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 9 Jan 2026 12:20:31 +0530 Subject: [PATCH 17/49] Remove workflow_dispatch trigger for ruleset compatibility --- .github/workflows/trufflehog-scan.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index b440ff8..6620adb 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -5,7 +5,6 @@ on: types: [opened, synchronize, reopened] pull_request_target: types: [opened, synchronize, reopened] - workflow_dispatch: permissions: contents: read From 2a55bbb7fdd0a950917c9c63693d7fd44bf7b264 Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 9 Jan 2026 12:42:53 +0530 Subject: [PATCH 18/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 115 ++++++-------------------- 1 file changed, 26 insertions(+), 89 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 6620adb..57e58ec 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -5,6 +5,7 @@ on: types: [opened, synchronize, reopened] pull_request_target: types: [opened, synchronize, reopened] + workflow_dispatch: permissions: contents: read @@ -31,7 +32,6 @@ env: ^pnpm-lock\.yaml$ \.min\.js$ \.min\.css$ - ^\.github/workflows/ jobs: trufflehog-scan: @@ -59,18 +59,16 @@ jobs: - name: Setup exclude config id: config - env: - DEFAULT_PATTERNS: ${{ env.DEFAULT_EXCLUDES }} - USER_PATTERNS: ${{ vars.TRUFFLEHOG_EXCLUDES }} run: | - # Write default exclusions - echo "Adding default exclusions..." - echo "$DEFAULT_PATTERNS" > .trufflehog-ignore - - # Append user-defined exclusions if set (additive, not replacement) - if [ -n "$USER_PATTERNS" ]; then - echo "Adding repo/org-level TRUFFLEHOG_EXCLUDES patterns..." - echo "$USER_PATTERNS" | tr ',' '\n' | sed '/^$/d' >> .trufflehog-ignore + if [ -n "${{ vars.TRUFFLEHOG_EXCLUDES }}" ]; then + echo "Using repo/org-level TRUFFLEHOG_EXCLUDES variable" + # Support both comma-separated and newline-separated patterns + echo "${{ vars.TRUFFLEHOG_EXCLUDES }}" | tr ',' '\n' | sed '/^$/d' > .trufflehog-ignore + else + echo "Using default exclusions from central workflow" + cat << 'EOF' > .trufflehog-ignore + ${{ env.DEFAULT_EXCLUDES }} + EOF fi echo "Exclusion patterns:" @@ -107,37 +105,27 @@ jobs: with: script: | const commentMarker = ''; - const commitSha = '${{ github.event.pull_request.head.sha }}'; - const shortSha = commitSha.substring(0, 7); - const scanTime = new Date().toISOString().replace('T', ' ').substring(0, 19) + ' UTC'; - const body = `${commentMarker} -## :rotating_light: Secret Scanning Alert - -**TruffleHog detected potential secrets in this pull request.** + ## :rotating_light: Secret Scanning Alert -| Scan Details | | -|--------------|---| -| **Commit** | [\`${shortSha}\`](${{ github.server_url }}/${{ github.repository }}/commit/${commitSha}) | -| **Scanned At** | ${scanTime} | -| **Workflow Run** | [View Logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) | + **TruffleHog detected potential secrets in this pull request.** -### What to do: -1. **Remove the exposed secret** from your code -2. **Rotate the credential immediately** - assume it's compromised -3. **Push the fix** to this branch -4. The scan will re-run automatically + ### What to do: + 1. **Review the workflow logs** for detailed findings (file, line number, secret type) + 2. **Remove the exposed secret** from your code + 3. **Rotate the credential immediately** - assume it's compromised + 4. **Push the fix** to this branch -### Finding Details -Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for: -- File paths containing secrets -- Line numbers -- Secret types (API key, password, token, etc.) -- Verification status (verified = confirmed active) + ### Finding Details + Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for: + - File paths containing secrets + - Line numbers + - Secret types (API key, password, token, etc.) + - Verification status (verified = confirmed active) ---- -*Only files modified in this PR were scanned. Secrets are classified as **verified** (confirmed active) or **unverified** (potential match).* -`; + --- + *This scan only checks files modified in this PR. Secrets are classified as **verified** (confirmed active) or **unverified** (potential match).* + `; // Find existing comment const { data: comments } = await github.rest.issues.listComments({ @@ -165,57 +153,6 @@ Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/ }); } - - name: Update PR comment when secrets resolved - if: steps.process.outputs.has_secrets == 'false' && github.event_name != 'workflow_dispatch' - uses: actions/github-script@v7 - with: - script: | - const commentMarker = ''; - const commitSha = '${{ github.event.pull_request.head.sha }}'; - const shortSha = commitSha.substring(0, 7); - const scanTime = new Date().toISOString().replace('T', ' ').substring(0, 19) + ' UTC'; - - // Check if there's an existing alert comment from a previous failed scan - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.pull_request.number, - per_page: 100 - }); - - const existing = comments.find(c => c.body && c.body.includes(commentMarker)); - - // Only update if there was a previous alert - don't create new comments for clean scans - if (existing) { - const resolvedBody = `${commentMarker} -## :white_check_mark: Secret Scanning Passed - -**Previously detected secrets have been resolved.** - -| Scan Details | | -|--------------|---| -| **Commit** | [\`${shortSha}\`](${{ github.server_url }}/${{ github.repository }}/commit/${commitSha}) | -| **Scanned At** | ${scanTime} | -| **Workflow Run** | [View Logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) | - -The secrets flagged in previous scans are no longer detected in the modified files. - ---- -*This PR is now clear of detected secrets. Remember to rotate any credentials that were previously exposed.* -`; - - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existing.id, - body: resolvedBody - }); - - console.log('Updated existing alert comment to show resolved status'); - } else { - console.log('No previous alert comment found - scan passed cleanly'); - } - - name: Set commit status if: github.event_name != 'workflow_dispatch' uses: actions/github-script@v7 From e1358eb5ee8cf8a916406a8f459213d65f3ca5cb Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 9 Jan 2026 12:56:56 +0530 Subject: [PATCH 19/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 57e58ec..f35151f 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -1,8 +1,6 @@ name: TruffleHog Secret Scan on: - pull_request: - types: [opened, synchronize, reopened] pull_request_target: types: [opened, synchronize, reopened] workflow_dispatch: @@ -37,12 +35,6 @@ jobs: trufflehog-scan: name: Scan PR for Secrets runs-on: ubuntu-latest - # Run pull_request_target only for fork PRs, pull_request only for same-repo PRs - # This prevents duplicate runs - if: | - (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository) || - (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || - github.event_name == 'workflow_dispatch' steps: - name: Checkout repository From f30fed04a2d66e813bbbbb8a1caa2cc18b0e546a Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 9 Jan 2026 13:56:47 +0530 Subject: [PATCH 20/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index f35151f..affea5f 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -52,15 +52,17 @@ jobs: - name: Setup exclude config id: config run: | - if [ -n "${{ vars.TRUFFLEHOG_EXCLUDES }}" ]; then - echo "Using repo/org-level TRUFFLEHOG_EXCLUDES variable" - # Support both comma-separated and newline-separated patterns - echo "${{ vars.TRUFFLEHOG_EXCLUDES }}" | tr ',' '\n' | sed '/^$/d' > .trufflehog-ignore - else - echo "Using default exclusions from central workflow" - cat << 'EOF' > .trufflehog-ignore + # Always include default exclusions + echo "Adding default exclusions" + cat << 'EOF' > .trufflehog-ignore ${{ env.DEFAULT_EXCLUDES }} EOF + + # Append repo/org-level custom exclusions if defined + if [ -n "${{ vars.TRUFFLEHOG_EXCLUDES }}" ]; then + echo "Adding repo/org-level TRUFFLEHOG_EXCLUDES patterns" + # Support both comma-separated and newline-separated patterns + echo "${{ vars.TRUFFLEHOG_EXCLUDES }}" | tr ',' '\n' | sed '/^$/d' >> .trufflehog-ignore fi echo "Exclusion patterns:" @@ -97,11 +99,15 @@ jobs: with: script: | const commentMarker = ''; + const commitSha = '${{ github.event.pull_request.head.sha }}'; + const shortSha = commitSha.substring(0, 7); const body = `${commentMarker} ## :rotating_light: Secret Scanning Alert **TruffleHog detected potential secrets in this pull request.** + **Scanned commit:** \`${shortSha}\` ([${commitSha}](${{ github.server_url }}/${{ github.repository }}/commit/${commitSha})) + ### What to do: 1. **Review the workflow logs** for detailed findings (file, line number, secret type) 2. **Remove the exposed secret** from your code From 3f65074841afb1de9cfbec90f1d12d2d8992031b Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 9 Jan 2026 13:59:51 +0530 Subject: [PATCH 21/49] Improve TruffleHog scan workflow and update docs Enhances the TruffleHog GitHub Actions workflow to better distinguish between scan errors and actual secret findings, adding a verification step for failed scans. Updates documentation to clarify exclusion pattern behavior, workflow triggers, and runtime logic for more accurate and secure secret scanning. --- .github/workflows/trufflehog-scan.yml | 32 +++++++++-- trufflehog_readme.md | 78 +++++++++++++-------------- 2 files changed, 65 insertions(+), 45 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index affea5f..9e30ffb 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -76,17 +76,39 @@ jobs: with: base: ${{ github.event.pull_request.base.sha }} head: ${{ github.event.pull_request.head.sha }} - extra_args: --json ${{ steps.config.outputs.exclude_args }} + extra_args: --json --no-update ${{ steps.config.outputs.exclude_args }} - name: Process scan results id: process if: github.event_name != 'workflow_dispatch' run: | - # Check if TruffleHog found any secrets + # Check TruffleHog exit code + # Exit code 183 = secrets found, other non-zero = scan error if [ "${{ steps.trufflehog.outcome }}" == "failure" ]; then - echo "has_secrets=true" >> $GITHUB_OUTPUT - echo "status=failure" >> $GITHUB_OUTPUT - echo "description=Secret scanning found exposed credentials" >> $GITHUB_OUTPUT + # Run a quick verification scan to confirm secrets vs error + curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh | sh -s -- -b /usr/local/bin + + # Run scan without --fail to get clean exit + set +e + trufflehog git file://. \ + --since-commit=${{ github.event.pull_request.base.sha }} \ + --branch=origin/pr-head \ + --json \ + --no-update \ + ${{ steps.config.outputs.exclude_args }} > /tmp/results.json 2>&1 + scan_exit=$? + set -e + + # Check if results file has actual findings + if [ -s /tmp/results.json ] && grep -q "DetectorName" /tmp/results.json; then + echo "has_secrets=true" >> $GITHUB_OUTPUT + echo "status=failure" >> $GITHUB_OUTPUT + echo "description=Secret scanning found exposed credentials" >> $GITHUB_OUTPUT + else + echo "has_secrets=false" >> $GITHUB_OUTPUT + echo "status=success" >> $GITHUB_OUTPUT + echo "description=No secrets detected in PR changes" >> $GITHUB_OUTPUT + fi else echo "has_secrets=false" >> $GITHUB_OUTPUT echo "status=success" >> $GITHUB_OUTPUT diff --git a/trufflehog_readme.md b/trufflehog_readme.md index f41686b..c5936f6 100644 --- a/trufflehog_readme.md +++ b/trufflehog_readme.md @@ -34,10 +34,11 @@ Set organization-wide exclusion patterns: Exclusions are configured via the `TRUFFLEHOG_EXCLUDES` variable using regex patterns. -**Priority order:** -1. Repository-level variable (highest priority) -2. Organization-level variable -3. Workflow defaults (if no variable set) +**How it works:** +- Default exclusions are **always applied** (node_modules, lock files, etc.) +- Repository-level patterns are **added on top** of defaults +- Organization-level patterns are **added on top** of defaults +- You never lose the base coverage when adding custom patterns ### Pattern Reference @@ -95,11 +96,11 @@ Individual repos can add additional exclusions on top of the defaults: 3. Name: `TRUFFLEHOG_EXCLUDES` 4. Value: Your comma-separated regex patterns -**Exclusions are additive:** Your patterns are combined with the default exclusions. You don't need to repeat common patterns like `node_modules/` or `.lock` files. +**Exclusions are always additive:** Your patterns are appended to the default exclusions. You don't need to repeat common patterns like `node_modules/` or `.lock` files since they're always included. ## Default Exclusions -If no `TRUFFLEHOG_EXCLUDES` variable is set, these defaults apply: +These default exclusions are **always applied**, regardless of whether custom patterns are defined: ``` ^node_modules/ @@ -113,48 +114,45 @@ If no `TRUFFLEHOG_EXCLUDES` variable is set, these defaults apply: \.min\.css$ ``` +Any patterns you add via `TRUFFLEHOG_EXCLUDES` are appended to this list. + ## How It Works at Runtime ``` PR Created/Updated | v -Determine PR Type +pull_request_target trigger +(works for both fork and same-repo PRs) + | + v +Checkout Base Repository + | + v +Fetch PR Head Commits +(using refs/pull/{number}/head) + | + v +Load Default Exclusions + | + v +Check for TRUFFLEHOG_EXCLUDES variable | - +------------------+------------------+ - | | - v v - Fork PR Same-repo PR - | | - v v - pull_request_target pull_request - trigger runs trigger runs - | | - +------------------+------------------+ - | - v - Checkout Base Repository - | - v - Fetch PR Head Commits - (using refs/pull/{number}/head) - | - v - Check for TRUFFLEHOG_EXCLUDES variable - | +------------------+------------------+ | | | v v v Repo variable Org variable Neither set exists? exists? | | | v - v v Use DEFAULT_EXCLUDES - Use it Use it from workflow + v v Use defaults only + Append to Append to | + defaults defaults | | | | +------------------+------------------+ | v Create .trufflehog-ignore file + (defaults + custom patterns) | v Run TruffleHog scan on PR diff @@ -168,8 +166,8 @@ Determine PR Type v v Post/Update PR Check for previous comment with alert comment - findings | - | +--------+--------+ + scanned commit | + SHA + findings +--------+--------+ | | | v v v Set commit status Exists? No comment @@ -202,8 +200,7 @@ The workflow manages PR comments to provide clear feedback throughout the remedi ### When Secrets Are Detected A comment is posted with: -- Commit SHA that was scanned -- Timestamp of the scan +- **Scanned commit SHA** (short hash with link to full commit) so you can verify the scan ran on your latest changes - Link to workflow logs for detailed findings - Instructions for removing and rotating secrets - Information about file paths, line numbers, and secret types @@ -221,17 +218,18 @@ If a PR never had secrets detected, no comment is posted to keep the PR clean. ## Workflow Triggers -The workflow uses dual triggers to handle both same-repo and fork PRs efficiently: +The workflow uses `pull_request_target` to handle all PR types with a single trigger: | Trigger | Used For | Description | |---------|----------|-------------| -| `pull_request` | Same-repo PRs | Standard trigger for PRs within the repository | -| `pull_request_target` | Fork PRs | Runs in base repo context, works for private forks | +| `pull_request_target` | All PRs | Runs in base repo context, works for both same-repo and fork PRs | | `workflow_dispatch` | Manual runs | Trigger manually from Actions tab | -**Duplicate prevention:** The workflow includes logic to ensure only one trigger runs per PR: -- Fork PRs: Only `pull_request_target` runs -- Same-repo PRs: Only `pull_request` runs +**Why `pull_request_target`?** +- Works for both same-repo branches and forks +- Only one workflow run per PR (no duplicate or skipped checks) +- Runs workflow code from the base branch (more secure for secret scanning) +- PR commits are fetched via `refs/pull/{number}/head` ## Fork PR Support From 467594bad4af10071cba0bb8d93905570beeeda7 Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 9 Jan 2026 14:03:37 +0530 Subject: [PATCH 22/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 9e30ffb..f68f96c 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -76,7 +76,7 @@ jobs: with: base: ${{ github.event.pull_request.base.sha }} head: ${{ github.event.pull_request.head.sha }} - extra_args: --json --no-update ${{ steps.config.outputs.exclude_args }} + extra_args: --json ${{ steps.config.outputs.exclude_args }} - name: Process scan results id: process From 693b2e7ec1953717df34f2d3f43337633ad20cfa Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 9 Jan 2026 14:06:39 +0530 Subject: [PATCH 23/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 34 +++++++-------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index f68f96c..fe66e44 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -82,33 +82,15 @@ jobs: id: process if: github.event_name != 'workflow_dispatch' run: | - # Check TruffleHog exit code - # Exit code 183 = secrets found, other non-zero = scan error + # TruffleHog exit codes: + # 0 = no secrets found + # 183 = secrets found + # other = error + # With continue-on-error, outcome is "failure" for any non-zero exit if [ "${{ steps.trufflehog.outcome }}" == "failure" ]; then - # Run a quick verification scan to confirm secrets vs error - curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh | sh -s -- -b /usr/local/bin - - # Run scan without --fail to get clean exit - set +e - trufflehog git file://. \ - --since-commit=${{ github.event.pull_request.base.sha }} \ - --branch=origin/pr-head \ - --json \ - --no-update \ - ${{ steps.config.outputs.exclude_args }} > /tmp/results.json 2>&1 - scan_exit=$? - set -e - - # Check if results file has actual findings - if [ -s /tmp/results.json ] && grep -q "DetectorName" /tmp/results.json; then - echo "has_secrets=true" >> $GITHUB_OUTPUT - echo "status=failure" >> $GITHUB_OUTPUT - echo "description=Secret scanning found exposed credentials" >> $GITHUB_OUTPUT - else - echo "has_secrets=false" >> $GITHUB_OUTPUT - echo "status=success" >> $GITHUB_OUTPUT - echo "description=No secrets detected in PR changes" >> $GITHUB_OUTPUT - fi + echo "has_secrets=true" >> $GITHUB_OUTPUT + echo "status=failure" >> $GITHUB_OUTPUT + echo "description=Secret scanning found exposed credentials" >> $GITHUB_OUTPUT else echo "has_secrets=false" >> $GITHUB_OUTPUT echo "status=success" >> $GITHUB_OUTPUT From 8770dabf2952f2ea0c2b92bf61cd3dd9d1f990ea Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 9 Jan 2026 14:11:10 +0530 Subject: [PATCH 24/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 41 +++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index fe66e44..90a6e8a 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -78,6 +78,47 @@ jobs: head: ${{ github.event.pull_request.head.sha }} extra_args: --json ${{ steps.config.outputs.exclude_args }} + - name: Parse scan results + id: parse + if: github.event_name != 'workflow_dispatch' + run: | + # Capture TruffleHog JSON output by re-running with same args + # Store results for annotation generation + echo "Parsing TruffleHog results..." + + SCAN_OUTPUT=$(docker run --rm -v "$(pwd)":/tmp -w /tmp \ + ghcr.io/trufflesecurity/trufflehog:latest \ + git file:///tmp/ \ + --since-commit ${{ github.event.pull_request.base.sha }} \ + --branch ${{ github.event.pull_request.head.sha }} \ + --json \ + ${{ steps.config.outputs.exclude_args }} \ + --no-update 2>/dev/null || true) + + # Parse JSON lines and create GitHub annotations + if [ -n "$SCAN_OUTPUT" ]; then + echo "$SCAN_OUTPUT" | while IFS= read -r line; do + # Skip non-JSON lines (info logs) + if ! echo "$line" | jq -e '.DetectorName' > /dev/null 2>&1; then + continue + fi + + FILE=$(echo "$line" | jq -r '.SourceMetadata.Data.Git.file // "unknown"') + LINE_NUM=$(echo "$line" | jq -r '.SourceMetadata.Data.Git.line // 1') + DETECTOR=$(echo "$line" | jq -r '.DetectorName // "Secret"') + VERIFIED=$(echo "$line" | jq -r '.Verified // false') + + if [ "$VERIFIED" == "true" ]; then + STATUS="VERIFIED - Active credential" + else + STATUS="Unverified - Potential secret" + fi + + # Create file-specific annotation + echo "::error file=${FILE},line=${LINE_NUM},title=${DETECTOR} detected::${STATUS}: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. Remove this secret and rotate the credential immediately." + done + fi + - name: Process scan results id: process if: github.event_name != 'workflow_dispatch' From 1a61036d15a165fbe47f320b6dc428da7b90ffa0 Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 9 Jan 2026 14:24:54 +0530 Subject: [PATCH 25/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 90a6e8a..9a969b1 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -8,7 +8,6 @@ on: permissions: contents: read pull-requests: write - statuses: write # Default exclusion patterns (regex format) # Supports: exact filenames, wildcards, regex patterns @@ -196,21 +195,6 @@ jobs: }); } - - name: Set commit status - if: github.event_name != 'workflow_dispatch' - uses: actions/github-script@v7 - with: - script: | - await github.rest.repos.createCommitStatus({ - owner: context.repo.owner, - repo: context.repo.repo, - sha: '${{ github.event.pull_request.head.sha }}', - state: '${{ steps.process.outputs.status }}', - target_url: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}', - description: '${{ steps.process.outputs.description }}', - context: 'TruffleHog Secret Scan' - }); - - name: Fail workflow if secrets found if: steps.process.outputs.has_secrets == 'true' run: | From aa0051269d866d13253483603f8052d5ca6261ae Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 9 Jan 2026 14:34:52 +0530 Subject: [PATCH 26/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 96 ++++++++++++++++++--------- 1 file changed, 64 insertions(+), 32 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 9a969b1..a1edaad 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -82,9 +82,11 @@ jobs: if: github.event_name != 'workflow_dispatch' run: | # Capture TruffleHog JSON output by re-running with same args - # Store results for annotation generation echo "Parsing TruffleHog results..." + VERIFIED_COUNT=0 + UNVERIFIED_COUNT=0 + SCAN_OUTPUT=$(docker run --rm -v "$(pwd)":/tmp -w /tmp \ ghcr.io/trufflesecurity/trufflehog:latest \ git file:///tmp/ \ @@ -96,7 +98,7 @@ jobs: # Parse JSON lines and create GitHub annotations if [ -n "$SCAN_OUTPUT" ]; then - echo "$SCAN_OUTPUT" | while IFS= read -r line; do + while IFS= read -r line; do # Skip non-JSON lines (info logs) if ! echo "$line" | jq -e '.DetectorName' > /dev/null 2>&1; then continue @@ -108,32 +110,42 @@ jobs: VERIFIED=$(echo "$line" | jq -r '.Verified // false') if [ "$VERIFIED" == "true" ]; then - STATUS="VERIFIED - Active credential" + VERIFIED_COUNT=$((VERIFIED_COUNT + 1)) + # Error annotation for verified secrets + echo "::error file=${FILE},line=${LINE_NUM},title=${DETECTOR} [VERIFIED]::VERIFIED ACTIVE CREDENTIAL: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. This secret is confirmed active. Remove and rotate immediately!" else - STATUS="Unverified - Potential secret" + UNVERIFIED_COUNT=$((UNVERIFIED_COUNT + 1)) + # Warning annotation for unverified secrets + echo "::warning file=${FILE},line=${LINE_NUM},title=${DETECTOR} [Unverified]::Potential secret: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. Review and remove if this is a real credential." fi - - # Create file-specific annotation - echo "::error file=${FILE},line=${LINE_NUM},title=${DETECTOR} detected::${STATUS}: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. Remove this secret and rotate the credential immediately." - done + done <<< "$SCAN_OUTPUT" fi + + echo "verified_count=${VERIFIED_COUNT}" >> $GITHUB_OUTPUT + echo "unverified_count=${UNVERIFIED_COUNT}" >> $GITHUB_OUTPUT + echo "Scan complete: ${VERIFIED_COUNT} verified, ${UNVERIFIED_COUNT} unverified secrets found" - name: Process scan results id: process if: github.event_name != 'workflow_dispatch' run: | - # TruffleHog exit codes: - # 0 = no secrets found - # 183 = secrets found - # other = error - # With continue-on-error, outcome is "failure" for any non-zero exit - if [ "${{ steps.trufflehog.outcome }}" == "failure" ]; then + VERIFIED=${{ steps.parse.outputs.verified_count || 0 }} + UNVERIFIED=${{ steps.parse.outputs.unverified_count || 0 }} + + if [ "$VERIFIED" -gt 0 ]; then + # Verified secrets found - must fail + echo "has_verified=true" >> $GITHUB_OUTPUT + echo "has_secrets=true" >> $GITHUB_OUTPUT + echo "description=Found ${VERIFIED} verified (active) secrets - action required" >> $GITHUB_OUTPUT + elif [ "$UNVERIFIED" -gt 0 ]; then + # Only unverified secrets - warn but pass + echo "has_verified=false" >> $GITHUB_OUTPUT echo "has_secrets=true" >> $GITHUB_OUTPUT - echo "status=failure" >> $GITHUB_OUTPUT - echo "description=Secret scanning found exposed credentials" >> $GITHUB_OUTPUT + echo "description=Found ${UNVERIFIED} unverified potential secrets - review recommended" >> $GITHUB_OUTPUT else + # No secrets + echo "has_verified=false" >> $GITHUB_OUTPUT echo "has_secrets=false" >> $GITHUB_OUTPUT - echo "status=success" >> $GITHUB_OUTPUT echo "description=No secrets detected in PR changes" >> $GITHUB_OUTPUT fi @@ -145,28 +157,48 @@ jobs: const commentMarker = ''; const commitSha = '${{ github.event.pull_request.head.sha }}'; const shortSha = commitSha.substring(0, 7); + const hasVerified = '${{ steps.process.outputs.has_verified }}' === 'true'; + const verifiedCount = '${{ steps.parse.outputs.verified_count }}' || '0'; + const unverifiedCount = '${{ steps.parse.outputs.unverified_count }}' || '0'; + + let severity, icon, action; + if (hasVerified) { + severity = 'CRITICAL'; + icon = ':rotating_light:'; + action = 'This PR is **blocked** until verified secrets are removed.'; + } else { + severity = 'Warning'; + icon = ':warning:'; + action = 'This PR can proceed, but please review the potential secrets below.'; + } + const body = `${commentMarker} - ## :rotating_light: Secret Scanning Alert + ## ${icon} Secret Scanning ${severity} - **TruffleHog detected potential secrets in this pull request.** + **TruffleHog scan results:** + - **Verified (active) secrets:** ${verifiedCount} ${verifiedCount > 0 ? ':x:' : ':white_check_mark:'} + - **Unverified (potential) secrets:** ${unverifiedCount} ${unverifiedCount > 0 ? ':warning:' : ':white_check_mark:'} **Scanned commit:** \`${shortSha}\` ([${commitSha}](${{ github.server_url }}/${{ github.repository }}/commit/${commitSha})) + ${action} + ### What to do: - 1. **Review the workflow logs** for detailed findings (file, line number, secret type) - 2. **Remove the exposed secret** from your code - 3. **Rotate the credential immediately** - assume it's compromised + 1. **Review the workflow annotations** - they point to exact file and line locations + 2. **Remove any exposed secrets** from your code + 3. **Rotate compromised credentials** - especially verified ones 4. **Push the fix** to this branch - ### Finding Details - Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for: - - File paths containing secrets - - Line numbers - - Secret types (API key, password, token, etc.) - - Verification status (verified = confirmed active) + ### Understanding Results + | Type | Meaning | Action Required | + |------|---------|-----------------| + | **Verified** | Confirmed active credential | **Must remove & rotate** - PR blocked | + | **Unverified** | Potential secret pattern | Review recommended - PR can proceed | + + Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details. --- - *This scan only checks files modified in this PR. Secrets are classified as **verified** (confirmed active) or **unverified** (potential match).* + *Verified secrets are confirmed active by TruffleHog. Unverified secrets match known patterns but couldn't be validated.* `; // Find existing comment @@ -195,8 +227,8 @@ jobs: }); } - - name: Fail workflow if secrets found - if: steps.process.outputs.has_secrets == 'true' + - name: Fail workflow if verified secrets found + if: steps.process.outputs.has_verified == 'true' run: | - echo "::error::Secrets detected in PR. Review the logs and PR comment for details." + echo "::error::VERIFIED SECRETS DETECTED - These are confirmed active credentials that must be removed and rotated immediately." exit 1 \ No newline at end of file From d57e46b87dcba267d45aae52cc50c7ed2dbe9779 Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 9 Jan 2026 15:23:32 +0530 Subject: [PATCH 27/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 54 +++++++++++++++++++++------ 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index a1edaad..8bfd230 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -150,17 +150,57 @@ jobs: fi - name: Post PR comment on findings - if: steps.process.outputs.has_secrets == 'true' && github.event_name != 'workflow_dispatch' + if: github.event_name != 'workflow_dispatch' uses: actions/github-script@v7 with: script: | const commentMarker = ''; const commitSha = '${{ github.event.pull_request.head.sha }}'; const shortSha = commitSha.substring(0, 7); + const hasSecrets = '${{ steps.process.outputs.has_secrets }}' === 'true'; const hasVerified = '${{ steps.process.outputs.has_verified }}' === 'true'; const verifiedCount = '${{ steps.parse.outputs.verified_count }}' || '0'; const unverifiedCount = '${{ steps.parse.outputs.unverified_count }}' || '0'; + // Find existing comment + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + per_page: 100 + }); + + const existing = comments.find(c => c.body && c.body.includes(commentMarker)); + + let body; + if (!hasSecrets) { + // No secrets found - post success message or update existing warning + if (existing) { + // Update existing comment to show issue is resolved + body = `${commentMarker} + ## :white_check_mark: Secret Scanning Passed + + **No secrets detected in this pull request.** + + **Scanned commit:** \`${shortSha}\` ([${commitSha}](${{ github.server_url }}/${{ github.repository }}/commit/${commitSha})) + + Previous issues have been resolved. Thank you for addressing the security concerns! + + --- + *This comment will be updated if new secrets are detected in future commits.* + `; + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body: body + }); + } + // If no existing comment and no secrets, don't post anything + return; + } + + // Secrets found - create or update warning comment let severity, icon, action; if (hasVerified) { severity = 'CRITICAL'; @@ -172,7 +212,7 @@ jobs: action = 'This PR can proceed, but please review the potential secrets below.'; } - const body = `${commentMarker} + body = `${commentMarker} ## ${icon} Secret Scanning ${severity} **TruffleHog scan results:** @@ -200,16 +240,6 @@ jobs: --- *Verified secrets are confirmed active by TruffleHog. Unverified secrets match known patterns but couldn't be validated.* `; - - // Find existing comment - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.pull_request.number, - per_page: 100 - }); - - const existing = comments.find(c => c.body && c.body.includes(commentMarker)); if (existing) { await github.rest.issues.updateComment({ From 0bff5609344c349a9e4a257dae9455aa38973803 Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 9 Jan 2026 15:24:29 +0530 Subject: [PATCH 28/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 8bfd230..ca13495 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -174,10 +174,13 @@ jobs: let body; if (!hasSecrets) { - // No secrets found - post success message or update existing warning + // No secrets found if (existing) { - // Update existing comment to show issue is resolved - body = `${commentMarker} + // Check if existing comment was a critical/blocking one (had verified secrets) + const wasBlocking = existing.body.includes('CRITICAL') || existing.body.includes(':rotating_light:'); + if (wasBlocking) { + // Update to show verified secrets are now resolved + body = `${commentMarker} ## :white_check_mark: Secret Scanning Passed **No secrets detected in this pull request.** @@ -189,12 +192,14 @@ jobs: --- *This comment will be updated if new secrets are detected in future commits.* `; - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existing.id, - body: body - }); + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body: body + }); + } + // If it was just a warning (unverified only), leave it as-is } // If no existing comment and no secrets, don't post anything return; From cbfc2c8692460fb4ad776275d80e86d1a846180c Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 9 Jan 2026 17:18:38 +0530 Subject: [PATCH 29/49] Update trufflehog_readme.md --- trufflehog_readme.md | 173 ++++++++++++++++++++++++++++--------------- 1 file changed, 112 insertions(+), 61 deletions(-) diff --git a/trufflehog_readme.md b/trufflehog_readme.md index c5936f6..6ece838 100644 --- a/trufflehog_readme.md +++ b/trufflehog_readme.md @@ -9,8 +9,10 @@ Centralized GitHub Actions workflow that automatically scans all pull requests f - Configurable exclusion patterns using regex - Supports org-level defaults with repo-level overrides - No workflow file needed in individual repos (uses org rulesets) -- Posts PR comments with detailed findings when secrets are detected -- Sets commit status to pass/fail for clear merge blocking +- **Verified secrets block the PR** - confirmed active credentials must be removed +- **Unverified secrets allow PR to proceed** - warnings shown for review +- Posts PR comments with detailed findings (updated when issues are resolved) +- Creates file-level annotations pointing to exact secret locations - Classifies secrets as verified (confirmed active) or unverified (potential match) ## Setup @@ -133,53 +135,37 @@ Fetch PR Head Commits (using refs/pull/{number}/head) | v -Load Default Exclusions +Load Default Exclusions + Custom Patterns | v -Check for TRUFFLEHOG_EXCLUDES variable +Run TruffleHog scan on PR diff +(only modified files between base and head) + | + v +Parse results and create annotations | +------------------+------------------+ | | | v v v - Repo variable Org variable Neither set - exists? exists? | - | | v - v v Use defaults only - Append to Append to | - defaults defaults | + Verified Unverified No secrets + secrets found secrets only found | | | - +------------------+------------------+ - | - v - Create .trufflehog-ignore file - (defaults + custom patterns) - | - v - Run TruffleHog scan on PR diff - (only modified files between base and head) - | - +-----------+-----------+ - | | - v v - Secrets found No secrets found - | | - v v - Post/Update PR Check for previous - comment with alert comment - scanned commit | - SHA + findings +--------+--------+ - | | | - v v v - Set commit status Exists? No comment - to failure | (clean PR) - | v | - v Update to v - FAIL - PR "Resolved" Set commit status - blocked status to success - | | - v v - Set commit PASS - PR allowed - to success + v v v + Error annotations Warning annotations Check for + on files on files previous comment + | | | + v v +-----+-----+ + Post CRITICAL Post Warning | | + PR comment PR comment v v + (blocking) (non-blocking) Was it No previous + | | CRITICAL? comment + v v | | + FAIL workflow PASS workflow v v + PR blocked PR can proceed Update to Do nothing + "Passed" (clean PR) + | + v + PASS workflow ``` **Scan scope:** Only files modified in the PR are scanned, not the entire repository. @@ -188,34 +174,81 @@ Check for TRUFFLEHOG_EXCLUDES variable TruffleHog classifies detected secrets into two categories: -| Type | Description | Action | -|------|-------------|--------| -| **Verified** | Confirmed active/valid credentials | Blocks PR, requires immediate rotation | -| **Unverified** | Potential secrets that couldn't be validated | Warning in logs, review recommended | +| Type | Description | Workflow Result | PR Status | +|------|-------------|-----------------|-----------| +| **Verified** | Confirmed active/valid credentials | **Fails** | Blocked until fixed | +| **Unverified** | Potential secrets that couldn't be validated | **Passes** | Can proceed (review recommended) | + +### Behavior Summary + +| Scenario | Workflow | PR Comment | Annotations | +|----------|----------|------------|-------------| +| Verified secrets found | Fails | Critical alert posted | Error annotations on files | +| Only unverified secrets | Passes | Warning posted | Warning annotations on files | +| No secrets detected | Passes | No comment (or updates to "Passed" if previously blocked) | None | + +**Why this approach?** +- **Verified secrets** are confirmed active credentials that pose immediate risk and must be removed +- **Unverified secrets** match known patterns but couldn't be validated (may be false positives, test data, or inactive credentials) +- Blocking only on verified secrets reduces friction while still catching real exposures ## PR Comments The workflow manages PR comments to provide clear feedback throughout the remediation process: -### When Secrets Are Detected +### When Verified Secrets Are Detected (Blocking) -A comment is posted with: -- **Scanned commit SHA** (short hash with link to full commit) so you can verify the scan ran on your latest changes -- Link to workflow logs for detailed findings +A **CRITICAL** comment is posted with: +- Red alert icon +- Count of verified vs unverified secrets +- **Scanned commit SHA** (short hash with link to full commit) +- Clear message that PR is blocked - Instructions for removing and rotating secrets -- Information about file paths, line numbers, and secret types +- Link to workflow logs for file paths and line numbers + +### When Only Unverified Secrets Are Detected (Non-blocking) -### When Secrets Are Resolved +A **Warning** comment is posted with: +- Warning icon +- Count of unverified secrets +- **Scanned commit SHA** +- Message that PR can proceed but review is recommended +- Same remediation instructions -If you fix the secrets and push again: +### When Verified Secrets Are Resolved + +If you fix verified secrets and push again: - The **same comment is updated** to show a "Passed" status - Shows the new commit SHA that resolved the issue -- Includes a reminder to rotate any previously exposed credentials +- Thanks the contributor for addressing security concerns + +### Unverified Warnings Persist + +If only unverified secrets were found and you push new commits: +- The warning comment **stays as-is** (no override) +- This ensures the warning remains visible for review +- The workflow still passes ### Clean PRs If a PR never had secrets detected, no comment is posted to keep the PR clean. +## Annotations + +The workflow creates GitHub annotations that point to exact locations in your code: + +| Secret Type | Annotation Level | Appears In | +|-------------|------------------|------------| +| Verified | Error (red) | Files changed tab, Annotations panel | +| Unverified | Warning (yellow) | Files changed tab, Annotations panel | + +Annotations include: +- File path +- Line number +- Secret type (e.g., AWS, Slack, Postgres) +- Verification status +- Remediation guidance + ## Workflow Triggers The workflow uses `pull_request_target` to handle all PR types with a single trigger: @@ -254,15 +287,33 @@ The workflow fully supports PRs from forked repositories: ## Handling Detected Secrets -If the scan fails: +### Verified Secrets (PR Blocked) + +If verified secrets are detected: + +1. **PR is blocked** - cannot be merged until fixed +2. **Remove the secret** from your code +3. **Rotate the secret immediately** - assume it's compromised +4. **Push the fix** to your PR branch +5. Scan re-runs automatically +6. PR comment updates to show "Passed" status when fixed + +### Unverified Secrets (PR Can Proceed) + +If only unverified secrets are detected: + +1. **PR can still be merged** - workflow passes +2. **Review the warnings** - check if they're real credentials +3. If real: remove and rotate as above +4. If false positive: add pattern to `TRUFFLEHOG_EXCLUDES` +5. Warning comment remains visible for awareness -1. **Remove the secret** from your code -2. **Rotate the secret** immediately (assume it's compromised) -3. **Push the fix** to your PR branch -4. Scan re-runs automatically -5. PR comment updates to show "Resolved" status when fixed +### False Positives -**For false positives:** Add the file/pattern to repo-level `TRUFFLEHOG_EXCLUDES` (patterns are additive to defaults). +To exclude files/patterns that trigger false positives: +- Add the pattern to repo-level `TRUFFLEHOG_EXCLUDES` +- Patterns are additive to defaults +- See [Exclusion Patterns](#exclusion-patterns) for syntax ## Manual Scan From f610f23bc08646d7f329ffd92744fe9fccb7cd53 Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 9 Jan 2026 17:31:24 +0530 Subject: [PATCH 30/49] Update trufflehog_readme.md --- trufflehog_readme.md | 48 +++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/trufflehog_readme.md b/trufflehog_readme.md index 6ece838..7eff1d7 100644 --- a/trufflehog_readme.md +++ b/trufflehog_readme.md @@ -16,6 +16,18 @@ Centralized GitHub Actions workflow that automatically scans all pull requests f - Classifies secrets as verified (confirmed active) or unverified (potential match) ## Setup + +### Required Permissions + +The workflow requires these GitHub token permissions: + +| Permission | Access | Purpose | +|------------|--------|----------| +| `contents` | read | Checkout repository and fetch PR commits | +| `pull-requests` | write | Post and update PR comments | + +These are configured in the workflow file and apply automatically. + ### Set Default Exclusions (Optional) Set organization-wide exclusion patterns: @@ -151,23 +163,30 @@ Parse results and create annotations secrets found secrets only found | | | v v v - Error annotations Warning annotations Check for - on files on files previous comment + Error annotations Warning annotations Check for previous + (red) on files (yellow) on files CRITICAL comment | | | v v +-----+-----+ Post CRITICAL Post Warning | | PR comment PR comment v v - (blocking) (non-blocking) Was it No previous - | | CRITICAL? comment + (blocking) (non-blocking) Was Not blocking + | | CRITICAL? or no comment v v | | FAIL workflow PASS workflow v v - PR blocked PR can proceed Update to Do nothing - "Passed" (clean PR) - | + PR blocked PR allowed Update to Do nothing + "Passed" (keep warning + | if exists) v PASS workflow ``` +**Key behaviors:** +- **Verified secrets** → Error annotations + CRITICAL comment + workflow fails +- **Unverified only** → Warning annotations + Warning comment + workflow passes +- **Clean after CRITICAL** → Comment updated to "Passed" +- **Clean after Warning** → Warning comment stays (for visibility) +- **Always clean** → No comment posted + **Scan scope:** Only files modified in the PR are scanned, not the entire repository. ## Secret Classification @@ -276,11 +295,16 @@ The workflow fully supports PRs from forked repositories: ## Viewing Results 1. Go to the **Pull Request** > **Checks** tab -2. Look for **TruffleHog Secret Scan** commit status -3. If secrets are found: - - A PR comment will be posted with remediation steps - - Click the status link to view detailed logs -4. View logs for: +2. Look for **TruffleHog Secret Scan / Scan PR for Secrets** +3. Check the workflow result: + - **Failed** = Verified secrets found (PR blocked) + - **Passed with warnings** = Only unverified secrets (review recommended) + - **Passed** = No secrets detected +4. If secrets are found: + - Check the **Annotations** panel for file/line locations + - Review the PR comment for remediation steps + - Click the workflow link to view detailed logs +5. Logs show: - Applied exclusion patterns - Detected secrets (file, line, secret type) - Verification status (verified = confirmed active) From 47a4ca6dd74af167686b36dfd8bd652f5470c5e1 Mon Sep 17 00:00:00 2001 From: Brijesh Kumar Patel Date: Mon, 12 Jan 2026 15:14:57 +0530 Subject: [PATCH 31/49] PDP-684: updated the workflow for updating the pullrequest comment --- .github/workflows/trufflehog-scan-brijesh.yml | 264 ++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 .github/workflows/trufflehog-scan-brijesh.yml diff --git a/.github/workflows/trufflehog-scan-brijesh.yml b/.github/workflows/trufflehog-scan-brijesh.yml new file mode 100644 index 0000000..d914908 --- /dev/null +++ b/.github/workflows/trufflehog-scan-brijesh.yml @@ -0,0 +1,264 @@ +name: TruffleHog Secret Scan + +on: + pull_request_target: + types: [opened, synchronize, reopened] + workflow_dispatch: + +permissions: + contents: read + pull-requests: write + +# Default exclusion patterns (regex format) +# Supports: exact filenames, wildcards, regex patterns +# Examples: +# Exact file: ^config/settings\.json$ +# Directory: ^node_modules/ +# Extension: \.lock$ +# Wildcard: .*\.min\.js$ +# Regex: ^src/test/.*_test\.py$ + +env: + DEFAULT_EXCLUDES: | + ^node_modules/ + ^vendor/ + ^\.git/ + \.lock$ + ^package-lock\.json$ + ^yarn\.lock$ + ^pnpm-lock\.yaml$ + \.min\.js$ + \.min\.css$ + +jobs: + trufflehog-scan: + name: Scan PR for Secrets + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Fetch PR head commits + if: github.event_name != 'workflow_dispatch' + run: | + # Fetch PR commits using GitHub's merge ref (works for all PRs including forks) + git fetch origin +refs/pull/${{ github.event.pull_request.number }}/head:refs/remotes/origin/pr-head + echo "Fetched PR #${{ github.event.pull_request.number }} head commit: ${{ github.event.pull_request.head.sha }}" + + - name: Setup exclude config + id: config + run: | + # Always include default exclusions + echo "Adding default exclusions" + cat << 'EOF' > .trufflehog-ignore + ${{ env.DEFAULT_EXCLUDES }} + EOF + + # Append repo/org-level custom exclusions if defined + if [ -n "${{ vars.TRUFFLEHOG_EXCLUDES }}" ]; then + echo "Adding repo/org-level TRUFFLEHOG_EXCLUDES patterns" + # Support both comma-separated and newline-separated patterns + echo "${{ vars.TRUFFLEHOG_EXCLUDES }}" | tr ',' '\n' | sed '/^$/d' >> .trufflehog-ignore + fi + + echo "Exclusion patterns:" + cat .trufflehog-ignore + echo "exclude_args=--exclude-paths=.trufflehog-ignore" >> $GITHUB_OUTPUT + + - name: TruffleHog Scan + id: trufflehog + uses: trufflesecurity/trufflehog@main + continue-on-error: true + with: + base: ${{ github.event.pull_request.base.sha }} + head: ${{ github.event.pull_request.head.sha }} + extra_args: --json ${{ steps.config.outputs.exclude_args }} + + - name: Parse scan results + id: parse + if: github.event_name != 'workflow_dispatch' + run: | + # Capture TruffleHog JSON output by re-running with same args + echo "Parsing TruffleHog results..." + + VERIFIED_COUNT=0 + UNVERIFIED_COUNT=0 + + SCAN_OUTPUT=$(docker run --rm -v "$(pwd)":/tmp -w /tmp \ + ghcr.io/trufflesecurity/trufflehog:latest \ + git file:///tmp/ \ + --since-commit ${{ github.event.pull_request.base.sha }} \ + --branch ${{ github.event.pull_request.head.sha }} \ + --json \ + ${{ steps.config.outputs.exclude_args }} \ + --no-update 2>/dev/null || true) + + # Parse JSON lines and create GitHub annotations + if [ -n "$SCAN_OUTPUT" ]; then + while IFS= read -r line; do + # Skip non-JSON lines (info logs) + if ! echo "$line" | jq -e '.DetectorName' > /dev/null 2>&1; then + continue + fi + + FILE=$(echo "$line" | jq -r '.SourceMetadata.Data.Git.file // "unknown"') + LINE_NUM=$(echo "$line" | jq -r '.SourceMetadata.Data.Git.line // 1') + DETECTOR=$(echo "$line" | jq -r '.DetectorName // "Secret"') + VERIFIED=$(echo "$line" | jq -r '.Verified // false') + + if [ "$VERIFIED" == "true" ]; then + VERIFIED_COUNT=$((VERIFIED_COUNT + 1)) + # Error annotation for verified secrets + echo "::error file=${FILE},line=${LINE_NUM},title=${DETECTOR} [VERIFIED]::VERIFIED ACTIVE CREDENTIAL: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. This secret is confirmed active. Remove and rotate immediately!" + else + UNVERIFIED_COUNT=$((UNVERIFIED_COUNT + 1)) + # Warning annotation for unverified secrets + echo "::warning file=${FILE},line=${LINE_NUM},title=${DETECTOR} [Unverified]::Potential secret: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. Review and remove if this is a real credential." + fi + done <<< "$SCAN_OUTPUT" + fi + + echo "verified_count=${VERIFIED_COUNT}" >> $GITHUB_OUTPUT + echo "unverified_count=${UNVERIFIED_COUNT}" >> $GITHUB_OUTPUT + echo "Scan complete: ${VERIFIED_COUNT} verified, ${UNVERIFIED_COUNT} unverified secrets found" + + - name: Process scan results + id: process + if: github.event_name != 'workflow_dispatch' + run: | + VERIFIED=${{ steps.parse.outputs.verified_count || 0 }} + UNVERIFIED=${{ steps.parse.outputs.unverified_count || 0 }} + + if [ "$VERIFIED" -gt 0 ]; then + # Verified secrets found - must fail + echo "has_verified=true" >> $GITHUB_OUTPUT + echo "has_secrets=true" >> $GITHUB_OUTPUT + echo "description=Found ${VERIFIED} verified (active) secrets - action required" >> $GITHUB_OUTPUT + elif [ "$UNVERIFIED" -gt 0 ]; then + # Only unverified secrets - warn but pass + echo "has_verified=false" >> $GITHUB_OUTPUT + echo "has_secrets=true" >> $GITHUB_OUTPUT + echo "description=Found ${UNVERIFIED} unverified potential secrets - review recommended" >> $GITHUB_OUTPUT + else + # No secrets + echo "has_verified=false" >> $GITHUB_OUTPUT + echo "has_secrets=false" >> $GITHUB_OUTPUT + echo "description=No secrets detected in PR changes" >> $GITHUB_OUTPUT + fi + + - name: Post PR comment on findings + if: github.event_name != 'workflow_dispatch' + uses: actions/github-script@v7 + with: + script: | + const commentMarker = ''; + const commitSha = '${{ github.event.pull_request.head.sha }}'; + const shortSha = commitSha.substring(0, 7); + const hasSecrets = '${{ steps.process.outputs.has_secrets }}' === 'true'; + const hasVerified = '${{ steps.process.outputs.has_verified }}' === 'true'; + const verifiedCount = '${{ steps.parse.outputs.verified_count }}' || '0'; + const unverifiedCount = '${{ steps.parse.outputs.unverified_count }}' || '0'; + + // Find existing comment + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + per_page: 100 + }); + + const existing = comments.find(c => c.body && c.body.includes(commentMarker)); + + let body; + if (!hasSecrets) { + // No secrets found + if (existing) { + // Update existing comment to show secrets are now resolved + body = `${commentMarker} + ## :white_check_mark: Secret Scanning Passed + + **No secrets detected in this pull request.** + + **Scanned commit:** \`${shortSha}\` ([${commitSha}](${{ github.server_url }}/${{ github.repository }}/commit/${commitSha})) + + Previous issues have been resolved. Thank you for addressing the security concerns! + + --- + *This comment will be updated if new secrets are detected in future commits.* + `; + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body: body + }); + } + // If no existing comment and no secrets, don't post anything + return; + } + + // Secrets found - create or update warning comment + let severity, icon, action; + if (hasVerified) { + severity = 'CRITICAL'; + icon = ':rotating_light:'; + action = 'This PR is **blocked** until verified secrets are removed.'; + } else { + severity = 'Warning'; + icon = ':warning:'; + action = 'This PR can proceed, but please review the potential secrets below.'; + } + + body = `${commentMarker} + ## ${icon} Secret Scanning ${severity} + + **TruffleHog scan results:** + - **Verified (active) secrets:** ${verifiedCount} ${verifiedCount > 0 ? ':x:' : ':white_check_mark:'} + - **Unverified (potential) secrets:** ${unverifiedCount} ${unverifiedCount > 0 ? ':warning:' : ':white_check_mark:'} + + **Scanned commit:** \`${shortSha}\` ([${commitSha}](${{ github.server_url }}/${{ github.repository }}/commit/${commitSha})) + + ${action} + + ### What to do: + 1. **Review the workflow annotations** - they point to exact file and line locations + 2. **Remove any exposed secrets** from your code + 3. **Rotate compromised credentials** - especially verified ones + 4. **Push the fix** to this branch + + ### Understanding Results + | Type | Meaning | Action Required | + |------|---------|-----------------| + | **Verified** | Confirmed active credential | **Must remove & rotate** - PR blocked | + | **Unverified** | Potential secret pattern | Review recommended - PR can proceed | + + Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details. + + --- + *Verified secrets are confirmed active by TruffleHog. Unverified secrets match known patterns but couldn't be validated.* + `; + + if (existing) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body: body + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + body: body + }); + } + + - name: Fail workflow if verified secrets found + if: steps.process.outputs.has_verified == 'true' + run: | + echo "::error::VERIFIED SECRETS DETECTED - These are confirmed active credentials that must be removed and rotated immediately." + exit 1 \ No newline at end of file From 7ad1df9451817e01dd9cb6482e3897b3f531e918 Mon Sep 17 00:00:00 2001 From: Brijesh Kumar Patel Date: Mon, 12 Jan 2026 15:27:19 +0530 Subject: [PATCH 32/49] PDP-684: Updated to update the comment --- .github/workflows/trufflehog-scan-brijesh.yml | 264 ------------------ .github/workflows/trufflehog-scan.yml | 21 +- 2 files changed, 8 insertions(+), 277 deletions(-) delete mode 100644 .github/workflows/trufflehog-scan-brijesh.yml diff --git a/.github/workflows/trufflehog-scan-brijesh.yml b/.github/workflows/trufflehog-scan-brijesh.yml deleted file mode 100644 index d914908..0000000 --- a/.github/workflows/trufflehog-scan-brijesh.yml +++ /dev/null @@ -1,264 +0,0 @@ -name: TruffleHog Secret Scan - -on: - pull_request_target: - types: [opened, synchronize, reopened] - workflow_dispatch: - -permissions: - contents: read - pull-requests: write - -# Default exclusion patterns (regex format) -# Supports: exact filenames, wildcards, regex patterns -# Examples: -# Exact file: ^config/settings\.json$ -# Directory: ^node_modules/ -# Extension: \.lock$ -# Wildcard: .*\.min\.js$ -# Regex: ^src/test/.*_test\.py$ - -env: - DEFAULT_EXCLUDES: | - ^node_modules/ - ^vendor/ - ^\.git/ - \.lock$ - ^package-lock\.json$ - ^yarn\.lock$ - ^pnpm-lock\.yaml$ - \.min\.js$ - \.min\.css$ - -jobs: - trufflehog-scan: - name: Scan PR for Secrets - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Fetch PR head commits - if: github.event_name != 'workflow_dispatch' - run: | - # Fetch PR commits using GitHub's merge ref (works for all PRs including forks) - git fetch origin +refs/pull/${{ github.event.pull_request.number }}/head:refs/remotes/origin/pr-head - echo "Fetched PR #${{ github.event.pull_request.number }} head commit: ${{ github.event.pull_request.head.sha }}" - - - name: Setup exclude config - id: config - run: | - # Always include default exclusions - echo "Adding default exclusions" - cat << 'EOF' > .trufflehog-ignore - ${{ env.DEFAULT_EXCLUDES }} - EOF - - # Append repo/org-level custom exclusions if defined - if [ -n "${{ vars.TRUFFLEHOG_EXCLUDES }}" ]; then - echo "Adding repo/org-level TRUFFLEHOG_EXCLUDES patterns" - # Support both comma-separated and newline-separated patterns - echo "${{ vars.TRUFFLEHOG_EXCLUDES }}" | tr ',' '\n' | sed '/^$/d' >> .trufflehog-ignore - fi - - echo "Exclusion patterns:" - cat .trufflehog-ignore - echo "exclude_args=--exclude-paths=.trufflehog-ignore" >> $GITHUB_OUTPUT - - - name: TruffleHog Scan - id: trufflehog - uses: trufflesecurity/trufflehog@main - continue-on-error: true - with: - base: ${{ github.event.pull_request.base.sha }} - head: ${{ github.event.pull_request.head.sha }} - extra_args: --json ${{ steps.config.outputs.exclude_args }} - - - name: Parse scan results - id: parse - if: github.event_name != 'workflow_dispatch' - run: | - # Capture TruffleHog JSON output by re-running with same args - echo "Parsing TruffleHog results..." - - VERIFIED_COUNT=0 - UNVERIFIED_COUNT=0 - - SCAN_OUTPUT=$(docker run --rm -v "$(pwd)":/tmp -w /tmp \ - ghcr.io/trufflesecurity/trufflehog:latest \ - git file:///tmp/ \ - --since-commit ${{ github.event.pull_request.base.sha }} \ - --branch ${{ github.event.pull_request.head.sha }} \ - --json \ - ${{ steps.config.outputs.exclude_args }} \ - --no-update 2>/dev/null || true) - - # Parse JSON lines and create GitHub annotations - if [ -n "$SCAN_OUTPUT" ]; then - while IFS= read -r line; do - # Skip non-JSON lines (info logs) - if ! echo "$line" | jq -e '.DetectorName' > /dev/null 2>&1; then - continue - fi - - FILE=$(echo "$line" | jq -r '.SourceMetadata.Data.Git.file // "unknown"') - LINE_NUM=$(echo "$line" | jq -r '.SourceMetadata.Data.Git.line // 1') - DETECTOR=$(echo "$line" | jq -r '.DetectorName // "Secret"') - VERIFIED=$(echo "$line" | jq -r '.Verified // false') - - if [ "$VERIFIED" == "true" ]; then - VERIFIED_COUNT=$((VERIFIED_COUNT + 1)) - # Error annotation for verified secrets - echo "::error file=${FILE},line=${LINE_NUM},title=${DETECTOR} [VERIFIED]::VERIFIED ACTIVE CREDENTIAL: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. This secret is confirmed active. Remove and rotate immediately!" - else - UNVERIFIED_COUNT=$((UNVERIFIED_COUNT + 1)) - # Warning annotation for unverified secrets - echo "::warning file=${FILE},line=${LINE_NUM},title=${DETECTOR} [Unverified]::Potential secret: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. Review and remove if this is a real credential." - fi - done <<< "$SCAN_OUTPUT" - fi - - echo "verified_count=${VERIFIED_COUNT}" >> $GITHUB_OUTPUT - echo "unverified_count=${UNVERIFIED_COUNT}" >> $GITHUB_OUTPUT - echo "Scan complete: ${VERIFIED_COUNT} verified, ${UNVERIFIED_COUNT} unverified secrets found" - - - name: Process scan results - id: process - if: github.event_name != 'workflow_dispatch' - run: | - VERIFIED=${{ steps.parse.outputs.verified_count || 0 }} - UNVERIFIED=${{ steps.parse.outputs.unverified_count || 0 }} - - if [ "$VERIFIED" -gt 0 ]; then - # Verified secrets found - must fail - echo "has_verified=true" >> $GITHUB_OUTPUT - echo "has_secrets=true" >> $GITHUB_OUTPUT - echo "description=Found ${VERIFIED} verified (active) secrets - action required" >> $GITHUB_OUTPUT - elif [ "$UNVERIFIED" -gt 0 ]; then - # Only unverified secrets - warn but pass - echo "has_verified=false" >> $GITHUB_OUTPUT - echo "has_secrets=true" >> $GITHUB_OUTPUT - echo "description=Found ${UNVERIFIED} unverified potential secrets - review recommended" >> $GITHUB_OUTPUT - else - # No secrets - echo "has_verified=false" >> $GITHUB_OUTPUT - echo "has_secrets=false" >> $GITHUB_OUTPUT - echo "description=No secrets detected in PR changes" >> $GITHUB_OUTPUT - fi - - - name: Post PR comment on findings - if: github.event_name != 'workflow_dispatch' - uses: actions/github-script@v7 - with: - script: | - const commentMarker = ''; - const commitSha = '${{ github.event.pull_request.head.sha }}'; - const shortSha = commitSha.substring(0, 7); - const hasSecrets = '${{ steps.process.outputs.has_secrets }}' === 'true'; - const hasVerified = '${{ steps.process.outputs.has_verified }}' === 'true'; - const verifiedCount = '${{ steps.parse.outputs.verified_count }}' || '0'; - const unverifiedCount = '${{ steps.parse.outputs.unverified_count }}' || '0'; - - // Find existing comment - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.pull_request.number, - per_page: 100 - }); - - const existing = comments.find(c => c.body && c.body.includes(commentMarker)); - - let body; - if (!hasSecrets) { - // No secrets found - if (existing) { - // Update existing comment to show secrets are now resolved - body = `${commentMarker} - ## :white_check_mark: Secret Scanning Passed - - **No secrets detected in this pull request.** - - **Scanned commit:** \`${shortSha}\` ([${commitSha}](${{ github.server_url }}/${{ github.repository }}/commit/${commitSha})) - - Previous issues have been resolved. Thank you for addressing the security concerns! - - --- - *This comment will be updated if new secrets are detected in future commits.* - `; - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existing.id, - body: body - }); - } - // If no existing comment and no secrets, don't post anything - return; - } - - // Secrets found - create or update warning comment - let severity, icon, action; - if (hasVerified) { - severity = 'CRITICAL'; - icon = ':rotating_light:'; - action = 'This PR is **blocked** until verified secrets are removed.'; - } else { - severity = 'Warning'; - icon = ':warning:'; - action = 'This PR can proceed, but please review the potential secrets below.'; - } - - body = `${commentMarker} - ## ${icon} Secret Scanning ${severity} - - **TruffleHog scan results:** - - **Verified (active) secrets:** ${verifiedCount} ${verifiedCount > 0 ? ':x:' : ':white_check_mark:'} - - **Unverified (potential) secrets:** ${unverifiedCount} ${unverifiedCount > 0 ? ':warning:' : ':white_check_mark:'} - - **Scanned commit:** \`${shortSha}\` ([${commitSha}](${{ github.server_url }}/${{ github.repository }}/commit/${commitSha})) - - ${action} - - ### What to do: - 1. **Review the workflow annotations** - they point to exact file and line locations - 2. **Remove any exposed secrets** from your code - 3. **Rotate compromised credentials** - especially verified ones - 4. **Push the fix** to this branch - - ### Understanding Results - | Type | Meaning | Action Required | - |------|---------|-----------------| - | **Verified** | Confirmed active credential | **Must remove & rotate** - PR blocked | - | **Unverified** | Potential secret pattern | Review recommended - PR can proceed | - - Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details. - - --- - *Verified secrets are confirmed active by TruffleHog. Unverified secrets match known patterns but couldn't be validated.* - `; - - if (existing) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existing.id, - body: body - }); - } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.pull_request.number, - body: body - }); - } - - - name: Fail workflow if verified secrets found - if: steps.process.outputs.has_verified == 'true' - run: | - echo "::error::VERIFIED SECRETS DETECTED - These are confirmed active credentials that must be removed and rotated immediately." - exit 1 \ No newline at end of file diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index ca13495..d914908 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -176,11 +176,8 @@ jobs: if (!hasSecrets) { // No secrets found if (existing) { - // Check if existing comment was a critical/blocking one (had verified secrets) - const wasBlocking = existing.body.includes('CRITICAL') || existing.body.includes(':rotating_light:'); - if (wasBlocking) { - // Update to show verified secrets are now resolved - body = `${commentMarker} + // Update existing comment to show secrets are now resolved + body = `${commentMarker} ## :white_check_mark: Secret Scanning Passed **No secrets detected in this pull request.** @@ -192,14 +189,12 @@ jobs: --- *This comment will be updated if new secrets are detected in future commits.* `; - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existing.id, - body: body - }); - } - // If it was just a warning (unverified only), leave it as-is + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body: body + }); } // If no existing comment and no secrets, don't post anything return; From 0666566b8b4e25e918b844ed2e53740371054deb Mon Sep 17 00:00:00 2001 From: brijeshp56 Date: Tue, 13 Jan 2026 07:27:58 +0530 Subject: [PATCH 33/49] PDP-684: Update the existing pull request comment if secrets are resolved. (#18) * PDP-684: updated the workflow for updating the pullrequest comment * PDP-684: Updated to update the comment --- .github/workflows/trufflehog-scan.yml | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index ca13495..d914908 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -176,11 +176,8 @@ jobs: if (!hasSecrets) { // No secrets found if (existing) { - // Check if existing comment was a critical/blocking one (had verified secrets) - const wasBlocking = existing.body.includes('CRITICAL') || existing.body.includes(':rotating_light:'); - if (wasBlocking) { - // Update to show verified secrets are now resolved - body = `${commentMarker} + // Update existing comment to show secrets are now resolved + body = `${commentMarker} ## :white_check_mark: Secret Scanning Passed **No secrets detected in this pull request.** @@ -192,14 +189,12 @@ jobs: --- *This comment will be updated if new secrets are detected in future commits.* `; - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existing.id, - body: body - }); - } - // If it was just a warning (unverified only), leave it as-is + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body: body + }); } // If no existing comment and no secrets, don't post anything return; From 06a1ac7f2c1fef9d8c2186d001b146896780c329 Mon Sep 17 00:00:00 2001 From: Brijesh Kumar Patel Date: Wed, 14 Jan 2026 14:26:28 +0530 Subject: [PATCH 34/49] PDP-684: Updated workflow to make sure to scan renamed files --- .github/workflows/trufflehog-scan.yml | 42 ++++----------------------- 1 file changed, 5 insertions(+), 37 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index d914908..ad72122 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -9,15 +9,6 @@ permissions: contents: read pull-requests: write -# Default exclusion patterns (regex format) -# Supports: exact filenames, wildcards, regex patterns -# Examples: -# Exact file: ^config/settings\.json$ -# Directory: ^node_modules/ -# Extension: \.lock$ -# Wildcard: .*\.min\.js$ -# Regex: ^src/test/.*_test\.py$ - env: DEFAULT_EXCLUDES: | ^node_modules/ @@ -44,23 +35,19 @@ jobs: - name: Fetch PR head commits if: github.event_name != 'workflow_dispatch' run: | - # Fetch PR commits using GitHub's merge ref (works for all PRs including forks) git fetch origin +refs/pull/${{ github.event.pull_request.number }}/head:refs/remotes/origin/pr-head echo "Fetched PR #${{ github.event.pull_request.number }} head commit: ${{ github.event.pull_request.head.sha }}" - name: Setup exclude config id: config run: | - # Always include default exclusions echo "Adding default exclusions" cat << 'EOF' > .trufflehog-ignore ${{ env.DEFAULT_EXCLUDES }} EOF - # Append repo/org-level custom exclusions if defined if [ -n "${{ vars.TRUFFLEHOG_EXCLUDES }}" ]; then echo "Adding repo/org-level TRUFFLEHOG_EXCLUDES patterns" - # Support both comma-separated and newline-separated patterns echo "${{ vars.TRUFFLEHOG_EXCLUDES }}" | tr ',' '\n' | sed '/^$/d' >> .trufflehog-ignore fi @@ -81,13 +68,17 @@ jobs: id: parse if: github.event_name != 'workflow_dispatch' run: | - # Capture TruffleHog JSON output by re-running with same args echo "Parsing TruffleHog results..." VERIFIED_COUNT=0 UNVERIFIED_COUNT=0 + git config diff.renames false + SCAN_OUTPUT=$(docker run --rm -v "$(pwd)":/tmp -w /tmp \ + -e GIT_CONFIG_COUNT=1 \ + -e GIT_CONFIG_KEY_0=diff.renames \ + -e GIT_CONFIG_VALUE_0=false \ ghcr.io/trufflesecurity/trufflehog:latest \ git file:///tmp/ \ --since-commit ${{ github.event.pull_request.base.sha }} \ @@ -96,10 +87,8 @@ jobs: ${{ steps.config.outputs.exclude_args }} \ --no-update 2>/dev/null || true) - # Parse JSON lines and create GitHub annotations if [ -n "$SCAN_OUTPUT" ]; then while IFS= read -r line; do - # Skip non-JSON lines (info logs) if ! echo "$line" | jq -e '.DetectorName' > /dev/null 2>&1; then continue fi @@ -111,11 +100,9 @@ jobs: if [ "$VERIFIED" == "true" ]; then VERIFIED_COUNT=$((VERIFIED_COUNT + 1)) - # Error annotation for verified secrets echo "::error file=${FILE},line=${LINE_NUM},title=${DETECTOR} [VERIFIED]::VERIFIED ACTIVE CREDENTIAL: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. This secret is confirmed active. Remove and rotate immediately!" else UNVERIFIED_COUNT=$((UNVERIFIED_COUNT + 1)) - # Warning annotation for unverified secrets echo "::warning file=${FILE},line=${LINE_NUM},title=${DETECTOR} [Unverified]::Potential secret: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. Review and remove if this is a real credential." fi done <<< "$SCAN_OUTPUT" @@ -133,17 +120,14 @@ jobs: UNVERIFIED=${{ steps.parse.outputs.unverified_count || 0 }} if [ "$VERIFIED" -gt 0 ]; then - # Verified secrets found - must fail echo "has_verified=true" >> $GITHUB_OUTPUT echo "has_secrets=true" >> $GITHUB_OUTPUT echo "description=Found ${VERIFIED} verified (active) secrets - action required" >> $GITHUB_OUTPUT elif [ "$UNVERIFIED" -gt 0 ]; then - # Only unverified secrets - warn but pass echo "has_verified=false" >> $GITHUB_OUTPUT echo "has_secrets=true" >> $GITHUB_OUTPUT echo "description=Found ${UNVERIFIED} unverified potential secrets - review recommended" >> $GITHUB_OUTPUT else - # No secrets echo "has_verified=false" >> $GITHUB_OUTPUT echo "has_secrets=false" >> $GITHUB_OUTPUT echo "description=No secrets detected in PR changes" >> $GITHUB_OUTPUT @@ -162,7 +146,6 @@ jobs: const verifiedCount = '${{ steps.parse.outputs.verified_count }}' || '0'; const unverifiedCount = '${{ steps.parse.outputs.unverified_count }}' || '0'; - // Find existing comment const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, @@ -174,18 +157,12 @@ jobs: let body; if (!hasSecrets) { - // No secrets found if (existing) { - // Update existing comment to show secrets are now resolved body = `${commentMarker} ## :white_check_mark: Secret Scanning Passed - **No secrets detected in this pull request.** - **Scanned commit:** \`${shortSha}\` ([${commitSha}](${{ github.server_url }}/${{ github.repository }}/commit/${commitSha})) - Previous issues have been resolved. Thank you for addressing the security concerns! - --- *This comment will be updated if new secrets are detected in future commits.* `; @@ -196,11 +173,9 @@ jobs: body: body }); } - // If no existing comment and no secrets, don't post anything return; } - // Secrets found - create or update warning comment let severity, icon, action; if (hasVerified) { severity = 'CRITICAL'; @@ -214,29 +189,22 @@ jobs: body = `${commentMarker} ## ${icon} Secret Scanning ${severity} - **TruffleHog scan results:** - **Verified (active) secrets:** ${verifiedCount} ${verifiedCount > 0 ? ':x:' : ':white_check_mark:'} - **Unverified (potential) secrets:** ${unverifiedCount} ${unverifiedCount > 0 ? ':warning:' : ':white_check_mark:'} - **Scanned commit:** \`${shortSha}\` ([${commitSha}](${{ github.server_url }}/${{ github.repository }}/commit/${commitSha})) - ${action} - ### What to do: 1. **Review the workflow annotations** - they point to exact file and line locations 2. **Remove any exposed secrets** from your code 3. **Rotate compromised credentials** - especially verified ones 4. **Push the fix** to this branch - ### Understanding Results | Type | Meaning | Action Required | |------|---------|-----------------| | **Verified** | Confirmed active credential | **Must remove & rotate** - PR blocked | | **Unverified** | Potential secret pattern | Review recommended - PR can proceed | - Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details. - --- *Verified secrets are confirmed active by TruffleHog. Unverified secrets match known patterns but couldn't be validated.* `; From e7e2b957b85c7fcf7b172db8656b882db268bf1a Mon Sep 17 00:00:00 2001 From: brijeshp56 Date: Wed, 14 Jan 2026 17:13:28 +0530 Subject: [PATCH 35/49] PDP-684 : Update trufflehog-scan.yml PDP-684 : Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 30 ++++++++++++++++++++------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index ad72122..037f7d2 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -30,8 +30,18 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: + ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 + - name: Debug - Show files changed in PR + if: github.event_name != 'workflow_dispatch' + run: | + echo "=== Files changed in this PR (including renames) ===" + git diff --name-status --no-renames ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} + echo "" + echo "=== Total files changed: ===" + git diff --name-only --no-renames ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} | wc -l + - name: Fetch PR head commits if: github.event_name != 'workflow_dispatch' run: | @@ -62,7 +72,7 @@ jobs: with: base: ${{ github.event.pull_request.base.sha }} head: ${{ github.event.pull_request.head.sha }} - extra_args: --json ${{ steps.config.outputs.exclude_args }} + extra_args: --json --no-update ${{ steps.config.outputs.exclude_args }} - name: Parse scan results id: parse @@ -73,19 +83,19 @@ jobs: VERIFIED_COUNT=0 UNVERIFIED_COUNT=0 - git config diff.renames false - + echo "=== Running TruffleHog scan with rename detection disabled ===" SCAN_OUTPUT=$(docker run --rm -v "$(pwd)":/tmp -w /tmp \ - -e GIT_CONFIG_COUNT=1 \ - -e GIT_CONFIG_KEY_0=diff.renames \ - -e GIT_CONFIG_VALUE_0=false \ ghcr.io/trufflesecurity/trufflehog:latest \ git file:///tmp/ \ --since-commit ${{ github.event.pull_request.base.sha }} \ --branch ${{ github.event.pull_request.head.sha }} \ --json \ ${{ steps.config.outputs.exclude_args }} \ - --no-update 2>/dev/null || true) + --no-update 2>&1 || true) + + echo "=== Debug: Files scanned by TruffleHog ===" + echo "$SCAN_OUTPUT" | jq -r 'select(.SourceMetadata.Data.Git.file) | .SourceMetadata.Data.Git.file' | sort -u || echo "No files found in scan output" + echo "" if [ -n "$SCAN_OUTPUT" ]; then while IFS= read -r line; do @@ -98,6 +108,8 @@ jobs: DETECTOR=$(echo "$line" | jq -r '.DetectorName // "Secret"') VERIFIED=$(echo "$line" | jq -r '.Verified // false') + echo "Found secret: ${DETECTOR} in ${FILE}:${LINE_NUM} (Verified: ${VERIFIED})" + if [ "$VERIFIED" == "true" ]; then VERIFIED_COUNT=$((VERIFIED_COUNT + 1)) echo "::error file=${FILE},line=${LINE_NUM},title=${DETECTOR} [VERIFIED]::VERIFIED ACTIVE CREDENTIAL: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. This secret is confirmed active. Remove and rotate immediately!" @@ -106,6 +118,8 @@ jobs: echo "::warning file=${FILE},line=${LINE_NUM},title=${DETECTOR} [Unverified]::Potential secret: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. Review and remove if this is a real credential." fi done <<< "$SCAN_OUTPUT" + else + echo "No scan output received from TruffleHog" fi echo "verified_count=${VERIFIED_COUNT}" >> $GITHUB_OUTPUT @@ -229,4 +243,4 @@ jobs: if: steps.process.outputs.has_verified == 'true' run: | echo "::error::VERIFIED SECRETS DETECTED - These are confirmed active credentials that must be removed and rotated immediately." - exit 1 \ No newline at end of file + exit 1 From 09da2096c9653d59f493a2a23b3665baeac4691f Mon Sep 17 00:00:00 2001 From: brijeshp56 Date: Wed, 14 Jan 2026 17:18:21 +0530 Subject: [PATCH 36/49] PDP-684 : Update trufflehog-scan.yml PDP-684 : Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 037f7d2..d75c0a6 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -72,7 +72,7 @@ jobs: with: base: ${{ github.event.pull_request.base.sha }} head: ${{ github.event.pull_request.head.sha }} - extra_args: --json --no-update ${{ steps.config.outputs.exclude_args }} + extra_args: --json ${{ steps.config.outputs.exclude_args }} - name: Parse scan results id: parse From 08e940a1a0c0a1bc0f592eee55fdd2d5b359b68c Mon Sep 17 00:00:00 2001 From: brijeshp56 Date: Wed, 14 Jan 2026 17:25:44 +0530 Subject: [PATCH 37/49] PDP-684 : Update trufflehog-scan.yml PDP-684 : Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 41 +++++++++++++++++++++------ 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index d75c0a6..c7cc6a6 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -33,14 +33,22 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 + - name: Configure Git to disable rename detection + run: | + git config --global diff.renames false + git config --global diff.renameLimit 0 + - name: Debug - Show files changed in PR if: github.event_name != 'workflow_dispatch' run: | - echo "=== Files changed in this PR (including renames) ===" - git diff --name-status --no-renames ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} + echo "=== Files changed in this PR (renames shown as delete+add) ===" + git diff --name-status ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} + echo "" + echo "=== File list with line counts ===" + git diff --stat ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} echo "" echo "=== Total files changed: ===" - git diff --name-only --no-renames ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} | wc -l + git diff --name-only ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} | wc -l - name: Fetch PR head commits if: github.event_name != 'workflow_dispatch' @@ -83,18 +91,35 @@ jobs: VERIFIED_COUNT=0 UNVERIFIED_COUNT=0 - echo "=== Running TruffleHog scan with rename detection disabled ===" + echo "=== TruffleHog scanning PR changes (renamed files as delete+add) ===" + echo "Base commit: ${{ github.event.pull_request.base.sha }}" + echo "Head commit: ${{ github.event.pull_request.head.sha }}" + echo "" + SCAN_OUTPUT=$(docker run --rm -v "$(pwd)":/tmp -w /tmp \ + -e GIT_CONFIG_COUNT=2 \ + -e GIT_CONFIG_KEY_0=diff.renames \ + -e GIT_CONFIG_VALUE_0=false \ + -e GIT_CONFIG_KEY_1=diff.renameLimit \ + -e GIT_CONFIG_VALUE_1=0 \ ghcr.io/trufflesecurity/trufflehog:latest \ git file:///tmp/ \ --since-commit ${{ github.event.pull_request.base.sha }} \ --branch ${{ github.event.pull_request.head.sha }} \ --json \ ${{ steps.config.outputs.exclude_args }} \ - --no-update 2>&1 || true) + --no-update --debug 2>&1 || true) - echo "=== Debug: Files scanned by TruffleHog ===" - echo "$SCAN_OUTPUT" | jq -r 'select(.SourceMetadata.Data.Git.file) | .SourceMetadata.Data.Git.file' | sort -u || echo "No files found in scan output" + echo "=== Files scanned by TruffleHog ===" + SCANNED_FILES=$(echo "$SCAN_OUTPUT" | jq -r 'select(.SourceMetadata.Data.Git.file) | .SourceMetadata.Data.Git.file' | sort -u 2>/dev/null || echo "") + if [ -n "$SCANNED_FILES" ]; then + echo "$SCANNED_FILES" + else + echo "No files with secrets found. TruffleHog scanned changed files but found no secrets." + echo "" + echo "=== Changed files that were scanned ===" + git diff --name-only ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} + fi echo "" if [ -n "$SCAN_OUTPUT" ]; then @@ -118,8 +143,6 @@ jobs: echo "::warning file=${FILE},line=${LINE_NUM},title=${DETECTOR} [Unverified]::Potential secret: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. Review and remove if this is a real credential." fi done <<< "$SCAN_OUTPUT" - else - echo "No scan output received from TruffleHog" fi echo "verified_count=${VERIFIED_COUNT}" >> $GITHUB_OUTPUT From d385450369448f679fee65d8667d534152401c6a Mon Sep 17 00:00:00 2001 From: brijeshp56 Date: Wed, 14 Jan 2026 18:31:38 +0530 Subject: [PATCH 38/49] PDP-684 : Update trufflehog-scan.yml PDP-684 : Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 41 +++++++++++++-------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index c7cc6a6..2cb21da 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -33,22 +33,22 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 - - name: Configure Git to disable rename detection + - name: Configure Git for rename detection run: | git config --global diff.renames false git config --global diff.renameLimit 0 + echo "Git configured to treat renames as delete+add" - name: Debug - Show files changed in PR if: github.event_name != 'workflow_dispatch' run: | - echo "=== Files changed in this PR (renames shown as delete+add) ===" + echo "========================================" + echo "FILES CHANGED (BASE vs HEAD)" + echo "========================================" git diff --name-status ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} echo "" - echo "=== File list with line counts ===" - git diff --stat ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} - echo "" - echo "=== Total files changed: ===" - git diff --name-only ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} | wc -l + echo "Total: $(git diff --name-only ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} | wc -l) files" + echo "========================================" - name: Fetch PR head commits if: github.event_name != 'workflow_dispatch' @@ -86,16 +86,13 @@ jobs: id: parse if: github.event_name != 'workflow_dispatch' run: | - echo "Parsing TruffleHog results..." + echo "========================================" + echo "SCANNING PR CHANGES" + echo "========================================" VERIFIED_COUNT=0 UNVERIFIED_COUNT=0 - echo "=== TruffleHog scanning PR changes (renamed files as delete+add) ===" - echo "Base commit: ${{ github.event.pull_request.base.sha }}" - echo "Head commit: ${{ github.event.pull_request.head.sha }}" - echo "" - SCAN_OUTPUT=$(docker run --rm -v "$(pwd)":/tmp -w /tmp \ -e GIT_CONFIG_COUNT=2 \ -e GIT_CONFIG_KEY_0=diff.renames \ @@ -108,19 +105,19 @@ jobs: --branch ${{ github.event.pull_request.head.sha }} \ --json \ ${{ steps.config.outputs.exclude_args }} \ - --no-update --debug 2>&1 || true) + --no-update 2>&1 || true) - echo "=== Files scanned by TruffleHog ===" + echo "========================================" + echo "FILES SCANNED BY TRUFFLEHOG" + echo "========================================" SCANNED_FILES=$(echo "$SCAN_OUTPUT" | jq -r 'select(.SourceMetadata.Data.Git.file) | .SourceMetadata.Data.Git.file' | sort -u 2>/dev/null || echo "") if [ -n "$SCANNED_FILES" ]; then echo "$SCANNED_FILES" else - echo "No files with secrets found. TruffleHog scanned changed files but found no secrets." - echo "" - echo "=== Changed files that were scanned ===" + echo "No secrets found. Files that were scanned:" git diff --name-only ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} fi - echo "" + echo "========================================" if [ -n "$SCAN_OUTPUT" ]; then while IFS= read -r line; do @@ -133,7 +130,7 @@ jobs: DETECTOR=$(echo "$line" | jq -r '.DetectorName // "Secret"') VERIFIED=$(echo "$line" | jq -r '.Verified // false') - echo "Found secret: ${DETECTOR} in ${FILE}:${LINE_NUM} (Verified: ${VERIFIED})" + echo "Found: ${DETECTOR} in ${FILE}:${LINE_NUM} (Verified: ${VERIFIED})" if [ "$VERIFIED" == "true" ]; then VERIFIED_COUNT=$((VERIFIED_COUNT + 1)) @@ -145,9 +142,11 @@ jobs: done <<< "$SCAN_OUTPUT" fi + echo "" + echo "Verified: ${VERIFIED_COUNT}, Unverified: ${UNVERIFIED_COUNT}" + echo "verified_count=${VERIFIED_COUNT}" >> $GITHUB_OUTPUT echo "unverified_count=${UNVERIFIED_COUNT}" >> $GITHUB_OUTPUT - echo "Scan complete: ${VERIFIED_COUNT} verified, ${UNVERIFIED_COUNT} unverified secrets found" - name: Process scan results id: process From 34848a09327b60f5e0b1f447969111d2329dc639 Mon Sep 17 00:00:00 2001 From: brijeshp56 Date: Fri, 16 Jan 2026 11:44:14 +0530 Subject: [PATCH 39/49] PDP-684 : Update trufflehog-scan.yml for detecting the renamed files PDP-684 : Update trufflehog-scan.yml for detecting the renamed files --- .github/workflows/trufflehog-scan.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index d914908..7e774a8 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -41,6 +41,12 @@ jobs: with: fetch-depth: 0 + - name: Configure Git for rename detection + run: | + git config --global diff.renames false + git config --global diff.renameLimit 0 + echo "Git configured to treat renames as delete+add" + - name: Fetch PR head commits if: github.event_name != 'workflow_dispatch' run: | @@ -88,6 +94,11 @@ jobs: UNVERIFIED_COUNT=0 SCAN_OUTPUT=$(docker run --rm -v "$(pwd)":/tmp -w /tmp \ + -e GIT_CONFIG_COUNT=2 \ + -e GIT_CONFIG_KEY_0=diff.renames \ + -e GIT_CONFIG_VALUE_0=false \ + -e GIT_CONFIG_KEY_1=diff.renameLimit \ + -e GIT_CONFIG_VALUE_1=0 \ ghcr.io/trufflesecurity/trufflehog:latest \ git file:///tmp/ \ --since-commit ${{ github.event.pull_request.base.sha }} \ @@ -261,4 +272,4 @@ jobs: if: steps.process.outputs.has_verified == 'true' run: | echo "::error::VERIFIED SECRETS DETECTED - These are confirmed active credentials that must be removed and rotated immediately." - exit 1 \ No newline at end of file + exit 1 From a0015e2227a70e7af72f447076271d8ea25161db Mon Sep 17 00:00:00 2001 From: brijeshp56 Date: Fri, 16 Jan 2026 13:46:28 +0530 Subject: [PATCH 40/49] PDP-684 : Update trufflehog-scan.yml PDP-684 : Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 2856a00..1cfd81c 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -115,6 +115,12 @@ jobs: fi FILE=$(echo "$line" | jq -r '.SourceMetadata.Data.Git.file // "unknown"') + + # Skip if file doesn't exist in current state (deleted/renamed) + if [ ! -f "$FILE" ]; then + continue + fi + LINE_NUM=$(echo "$line" | jq -r '.SourceMetadata.Data.Git.line // 1') DETECTOR=$(echo "$line" | jq -r '.DetectorName // "Secret"') VERIFIED=$(echo "$line" | jq -r '.Verified // false') From f4e15b581df4def67894d9c72e05edf43e1f2a45 Mon Sep 17 00:00:00 2001 From: brijeshp56 Date: Fri, 16 Jan 2026 14:12:30 +0530 Subject: [PATCH 41/49] PDP-684 : Update trufflehog-scan.yml PDP-684 : Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 1cfd81c..38908e8 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -75,9 +75,7 @@ jobs: id: parse if: github.event_name != 'workflow_dispatch' run: | - echo "========================================" - echo "SCANNING PR CHANGES" - echo "========================================" + echo "Parsing TruffleHog results..." VERIFIED_COUNT=0 UNVERIFIED_COUNT=0 @@ -94,19 +92,7 @@ jobs: --branch ${{ github.event.pull_request.head.sha }} \ --json \ ${{ steps.config.outputs.exclude_args }} \ - --no-update 2>&1 || true) - - echo "========================================" - echo "FILES SCANNED BY TRUFFLEHOG" - echo "========================================" - SCANNED_FILES=$(echo "$SCAN_OUTPUT" | jq -r 'select(.SourceMetadata.Data.Git.file) | .SourceMetadata.Data.Git.file' | sort -u 2>/dev/null || echo "") - if [ -n "$SCANNED_FILES" ]; then - echo "$SCANNED_FILES" - else - echo "No secrets found. Files that were scanned:" - git diff --name-only ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} - fi - echo "========================================" + --no-update 2>/dev/null || true) if [ -n "$SCAN_OUTPUT" ]; then while IFS= read -r line; do @@ -125,8 +111,6 @@ jobs: DETECTOR=$(echo "$line" | jq -r '.DetectorName // "Secret"') VERIFIED=$(echo "$line" | jq -r '.Verified // false') - echo "Found: ${DETECTOR} in ${FILE}:${LINE_NUM} (Verified: ${VERIFIED})" - if [ "$VERIFIED" == "true" ]; then VERIFIED_COUNT=$((VERIFIED_COUNT + 1)) echo "::error file=${FILE},line=${LINE_NUM},title=${DETECTOR} [VERIFIED]::VERIFIED ACTIVE CREDENTIAL: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. This secret is confirmed active. Remove and rotate immediately!" @@ -137,11 +121,9 @@ jobs: done <<< "$SCAN_OUTPUT" fi - echo "" - echo "Verified: ${VERIFIED_COUNT}, Unverified: ${UNVERIFIED_COUNT}" - echo "verified_count=${VERIFIED_COUNT}" >> $GITHUB_OUTPUT echo "unverified_count=${UNVERIFIED_COUNT}" >> $GITHUB_OUTPUT + echo "Scan complete: ${VERIFIED_COUNT} verified, ${UNVERIFIED_COUNT} unverified secrets found" - name: Process scan results id: process From 9494c79964ed189b41509817e2dba6a8dac2ce90 Mon Sep 17 00:00:00 2001 From: brijeshp56 Date: Fri, 16 Jan 2026 14:34:41 +0530 Subject: [PATCH 42/49] PDP-684 : Update trufflehog-scan.yml PDP-684 : Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 38908e8..d93710d 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -80,6 +80,12 @@ jobs: VERIFIED_COUNT=0 UNVERIFIED_COUNT=0 + # Get changed files list + CHANGED_FILES=$(git diff --name-only --diff-filter=ACMR ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}) + echo "Changed files:" + echo "$CHANGED_FILES" + + # Scan only HEAD commit (current state), not history SCAN_OUTPUT=$(docker run --rm -v "$(pwd)":/tmp -w /tmp \ -e GIT_CONFIG_COUNT=2 \ -e GIT_CONFIG_KEY_0=diff.renames \ @@ -88,8 +94,8 @@ jobs: -e GIT_CONFIG_VALUE_1=0 \ ghcr.io/trufflesecurity/trufflehog:latest \ git file:///tmp/ \ - --since-commit ${{ github.event.pull_request.base.sha }} \ --branch ${{ github.event.pull_request.head.sha }} \ + --max-depth=1 \ --json \ ${{ steps.config.outputs.exclude_args }} \ --no-update 2>/dev/null || true) @@ -102,8 +108,8 @@ jobs: FILE=$(echo "$line" | jq -r '.SourceMetadata.Data.Git.file // "unknown"') - # Skip if file doesn't exist in current state (deleted/renamed) - if [ ! -f "$FILE" ]; then + # Only report if file is in the changed files list + if ! echo "$CHANGED_FILES" | grep -qxF "$FILE"; then continue fi @@ -113,7 +119,7 @@ jobs: if [ "$VERIFIED" == "true" ]; then VERIFIED_COUNT=$((VERIFIED_COUNT + 1)) - echo "::error file=${FILE},line=${LINE_NUM},title=${DETECTOR} [VERIFIED]::VERIFIED ACTIVE CREDENTIAL: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. This secret is confirmed active. Remove and rotate immediately!" + echo "::error file=${FILE},line=${LINE_NUM},title=${DETECTOR} [VERIFIED]::VERIFIED ACTIVE CREDENTIAL: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. Remove and rotate immediately!" else UNVERIFIED_COUNT=$((UNVERIFIED_COUNT + 1)) echo "::warning file=${FILE},line=${LINE_NUM},title=${DETECTOR} [Unverified]::Potential secret: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. Review and remove if this is a real credential." From 0a33191062f634b3734b77accd47c3d5c680b523 Mon Sep 17 00:00:00 2001 From: Brijesh Kumar Patel Date: Mon, 19 Jan 2026 16:05:15 +0530 Subject: [PATCH 43/49] PDP-684: updated workflow to checkout only head commit --- .github/workflows/trufflehog-scan.yml | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index d914908..ce7563b 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -39,14 +39,8 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - fetch-depth: 0 - - - name: Fetch PR head commits - if: github.event_name != 'workflow_dispatch' - run: | - # Fetch PR commits using GitHub's merge ref (works for all PRs including forks) - git fetch origin +refs/pull/${{ github.event.pull_request.number }}/head:refs/remotes/origin/pr-head - echo "Fetched PR #${{ github.event.pull_request.number }} head commit: ${{ github.event.pull_request.head.sha }}" + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 1 - name: Setup exclude config id: config @@ -73,7 +67,7 @@ jobs: uses: trufflesecurity/trufflehog@main continue-on-error: true with: - base: ${{ github.event.pull_request.base.sha }} + base: ${{ github.event.pull_request.head.sha }}~1 head: ${{ github.event.pull_request.head.sha }} extra_args: --json ${{ steps.config.outputs.exclude_args }} @@ -90,7 +84,7 @@ jobs: SCAN_OUTPUT=$(docker run --rm -v "$(pwd)":/tmp -w /tmp \ ghcr.io/trufflesecurity/trufflehog:latest \ git file:///tmp/ \ - --since-commit ${{ github.event.pull_request.base.sha }} \ + --since-commit ${{ github.event.pull_request.head.sha }}~1 \ --branch ${{ github.event.pull_request.head.sha }} \ --json \ ${{ steps.config.outputs.exclude_args }} \ @@ -176,7 +170,7 @@ jobs: if (!hasSecrets) { // No secrets found if (existing) { - // Update existing comment to show secrets are now resolved + // Update to show secrets are now resolved (whether verified or unverified) body = `${commentMarker} ## :white_check_mark: Secret Scanning Passed From d653ce828b5c67b144fd4aaf68dca2cc5ffd08af Mon Sep 17 00:00:00 2001 From: Brijesh Kumar Patel Date: Mon, 19 Jan 2026 16:13:16 +0530 Subject: [PATCH 44/49] PDP-684: updated workflow to checkout only head commit1 --- .github/workflows/trufflehog-scan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index ce7563b..269a0e5 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -40,7 +40,7 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} - fetch-depth: 1 + fetch-depth: 2 - name: Setup exclude config id: config From a7b8ecd341df30f66e184f7330720134cea840ce Mon Sep 17 00:00:00 2001 From: Brijesh Kumar Patel Date: Mon, 19 Jan 2026 17:34:02 +0530 Subject: [PATCH 45/49] PDP-684 : Reverting my changes for trufflehog --- .github/workflows/trufflehog-scan.yml | 90 ++++++++++++++++++--------- 1 file changed, 59 insertions(+), 31 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 316d837..3250a2b 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -9,6 +9,15 @@ permissions: contents: read pull-requests: write +# Default exclusion patterns (regex format) +# Supports: exact filenames, wildcards, regex patterns +# Examples: +# Exact file: ^config/settings\.json$ +# Directory: ^node_modules/ +# Extension: \.lock$ +# Wildcard: .*\.min\.js$ +# Regex: ^src/test/.*_test\.py$ + env: DEFAULT_EXCLUDES: | ^node_modules/ @@ -30,19 +39,28 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - ref: ${{ github.event.pull_request.head.sha }} - fetch-depth: 2 + fetch-depth: 0 + + - name: Fetch PR head commits + if: github.event_name != 'workflow_dispatch' + run: | + # Fetch PR commits using GitHub's merge ref (works for all PRs including forks) + git fetch origin +refs/pull/${{ github.event.pull_request.number }}/head:refs/remotes/origin/pr-head + echo "Fetched PR #${{ github.event.pull_request.number }} head commit: ${{ github.event.pull_request.head.sha }}" - name: Setup exclude config id: config run: | + # Always include default exclusions echo "Adding default exclusions" cat << 'EOF' > .trufflehog-ignore ${{ env.DEFAULT_EXCLUDES }} EOF + # Append repo/org-level custom exclusions if defined if [ -n "${{ vars.TRUFFLEHOG_EXCLUDES }}" ]; then echo "Adding repo/org-level TRUFFLEHOG_EXCLUDES patterns" + # Support both comma-separated and newline-separated patterns echo "${{ vars.TRUFFLEHOG_EXCLUDES }}" | tr ',' '\n' | sed '/^$/d' >> .trufflehog-ignore fi @@ -55,7 +73,7 @@ jobs: uses: trufflesecurity/trufflehog@main continue-on-error: true with: - base: ${{ github.event.pull_request.head.sha }}~1 + base: ${{ github.event.pull_request.base.sha }} head: ${{ github.event.pull_request.head.sha }} extra_args: --json ${{ steps.config.outputs.exclude_args }} @@ -63,54 +81,41 @@ jobs: id: parse if: github.event_name != 'workflow_dispatch' run: | + # Capture TruffleHog JSON output by re-running with same args echo "Parsing TruffleHog results..." VERIFIED_COUNT=0 UNVERIFIED_COUNT=0 - # Get changed files list - CHANGED_FILES=$(git diff --name-only --diff-filter=ACMR ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}) - echo "Changed files:" - echo "$CHANGED_FILES" - - # Scan only HEAD commit (current state), not history SCAN_OUTPUT=$(docker run --rm -v "$(pwd)":/tmp -w /tmp \ - -e GIT_CONFIG_COUNT=2 \ - -e GIT_CONFIG_KEY_0=diff.renames \ - -e GIT_CONFIG_VALUE_0=false \ - -e GIT_CONFIG_KEY_1=diff.renameLimit \ - -e GIT_CONFIG_VALUE_1=0 \ ghcr.io/trufflesecurity/trufflehog:latest \ git file:///tmp/ \ - --since-commit ${{ github.event.pull_request.head.sha }}~1 \ + --since-commit ${{ github.event.pull_request.base.sha }} \ --branch ${{ github.event.pull_request.head.sha }} \ - --max-depth=1 \ --json \ ${{ steps.config.outputs.exclude_args }} \ --no-update 2>/dev/null || true) + # Parse JSON lines and create GitHub annotations if [ -n "$SCAN_OUTPUT" ]; then while IFS= read -r line; do + # Skip non-JSON lines (info logs) if ! echo "$line" | jq -e '.DetectorName' > /dev/null 2>&1; then continue fi FILE=$(echo "$line" | jq -r '.SourceMetadata.Data.Git.file // "unknown"') - - # Only report if file is in the changed files list - if ! echo "$CHANGED_FILES" | grep -qxF "$FILE"; then - continue - fi - LINE_NUM=$(echo "$line" | jq -r '.SourceMetadata.Data.Git.line // 1') DETECTOR=$(echo "$line" | jq -r '.DetectorName // "Secret"') VERIFIED=$(echo "$line" | jq -r '.Verified // false') if [ "$VERIFIED" == "true" ]; then VERIFIED_COUNT=$((VERIFIED_COUNT + 1)) - echo "::error file=${FILE},line=${LINE_NUM},title=${DETECTOR} [VERIFIED]::VERIFIED ACTIVE CREDENTIAL: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. Remove and rotate immediately!" + # Error annotation for verified secrets + echo "::error file=${FILE},line=${LINE_NUM},title=${DETECTOR} [VERIFIED]::VERIFIED ACTIVE CREDENTIAL: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. This secret is confirmed active. Remove and rotate immediately!" else UNVERIFIED_COUNT=$((UNVERIFIED_COUNT + 1)) + # Warning annotation for unverified secrets echo "::warning file=${FILE},line=${LINE_NUM},title=${DETECTOR} [Unverified]::Potential secret: ${DETECTOR} found in ${FILE} at line ${LINE_NUM}. Review and remove if this is a real credential." fi done <<< "$SCAN_OUTPUT" @@ -128,14 +133,17 @@ jobs: UNVERIFIED=${{ steps.parse.outputs.unverified_count || 0 }} if [ "$VERIFIED" -gt 0 ]; then + # Verified secrets found - must fail echo "has_verified=true" >> $GITHUB_OUTPUT echo "has_secrets=true" >> $GITHUB_OUTPUT echo "description=Found ${VERIFIED} verified (active) secrets - action required" >> $GITHUB_OUTPUT elif [ "$UNVERIFIED" -gt 0 ]; then + # Only unverified secrets - warn but pass echo "has_verified=false" >> $GITHUB_OUTPUT echo "has_secrets=true" >> $GITHUB_OUTPUT echo "description=Found ${UNVERIFIED} unverified potential secrets - review recommended" >> $GITHUB_OUTPUT else + # No secrets echo "has_verified=false" >> $GITHUB_OUTPUT echo "has_secrets=false" >> $GITHUB_OUTPUT echo "description=No secrets detected in PR changes" >> $GITHUB_OUTPUT @@ -154,6 +162,7 @@ jobs: const verifiedCount = '${{ steps.parse.outputs.verified_count }}' || '0'; const unverifiedCount = '${{ steps.parse.outputs.unverified_count }}' || '0'; + // Find existing comment const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, @@ -165,26 +174,38 @@ jobs: let body; if (!hasSecrets) { + // No secrets found if (existing) { - // Update to show secrets are now resolved (whether verified or unverified) - body = `${commentMarker} + // Check if existing comment was a critical/blocking one (had verified secrets) + const wasBlocking = existing.body.includes('CRITICAL') || existing.body.includes(':rotating_light:'); + if (wasBlocking) { + // Update to show verified secrets are now resolved + body = `${commentMarker} ## :white_check_mark: Secret Scanning Passed + **No secrets detected in this pull request.** + **Scanned commit:** \`${shortSha}\` ([${commitSha}](${{ github.server_url }}/${{ github.repository }}/commit/${commitSha})) + Previous issues have been resolved. Thank you for addressing the security concerns! + --- *This comment will be updated if new secrets are detected in future commits.* `; - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existing.id, - body: body - }); + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body: body + }); + } + // If it was just a warning (unverified only), leave it as-is } + // If no existing comment and no secrets, don't post anything return; } + // Secrets found - create or update warning comment let severity, icon, action; if (hasVerified) { severity = 'CRITICAL'; @@ -198,22 +219,29 @@ jobs: body = `${commentMarker} ## ${icon} Secret Scanning ${severity} + **TruffleHog scan results:** - **Verified (active) secrets:** ${verifiedCount} ${verifiedCount > 0 ? ':x:' : ':white_check_mark:'} - **Unverified (potential) secrets:** ${unverifiedCount} ${unverifiedCount > 0 ? ':warning:' : ':white_check_mark:'} + **Scanned commit:** \`${shortSha}\` ([${commitSha}](${{ github.server_url }}/${{ github.repository }}/commit/${commitSha})) + ${action} + ### What to do: 1. **Review the workflow annotations** - they point to exact file and line locations 2. **Remove any exposed secrets** from your code 3. **Rotate compromised credentials** - especially verified ones 4. **Push the fix** to this branch + ### Understanding Results | Type | Meaning | Action Required | |------|---------|-----------------| | **Verified** | Confirmed active credential | **Must remove & rotate** - PR blocked | | **Unverified** | Potential secret pattern | Review recommended - PR can proceed | + Check the [workflow run logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details. + --- *Verified secrets are confirmed active by TruffleHog. Unverified secrets match known patterns but couldn't be validated.* `; From f21206b0e998978511c86c501548bbdf528ccbda Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Thu, 22 Jan 2026 12:17:31 +0530 Subject: [PATCH 46/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 3250a2b..74a30dc 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -176,10 +176,13 @@ jobs: if (!hasSecrets) { // No secrets found if (existing) { - // Check if existing comment was a critical/blocking one (had verified secrets) - const wasBlocking = existing.body.includes('CRITICAL') || existing.body.includes(':rotating_light:'); - if (wasBlocking) { - // Update to show verified secrets are now resolved + // Check if existing comment had any critical/blocking ones/warnings (verified or unverified) + const hadVerified = existing.body.includes('CRITICAL') || existing.body.includes(':rotating_light:'); + const hadUnverified = existing.body.includes('Warning') || existing.body.includes('Unverified'); + + if (hadVerified || hadUnverified) { + // Update to show all secrets are now resolved + const previousType = hadVerified ? 'verified secrets' : 'potential secrets'; body = `${commentMarker} ## :white_check_mark: Secret Scanning Passed @@ -187,7 +190,7 @@ jobs: **Scanned commit:** \`${shortSha}\` ([${commitSha}](${{ github.server_url }}/${{ github.repository }}/commit/${commitSha})) - Previous issues have been resolved. Thank you for addressing the security concerns! + Previous ${previousType} have been resolved. Thank you for addressing the security concerns! --- *This comment will be updated if new secrets are detected in future commits.* @@ -199,7 +202,6 @@ jobs: body: body }); } - // If it was just a warning (unverified only), leave it as-is } // If no existing comment and no secrets, don't post anything return; From 1be8f215bb2fb661b0d8d392189cf03a3bef1953 Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 30 Jan 2026 00:59:18 +0530 Subject: [PATCH 47/49] Improve secret scan comment update logic Refines the logic for updating PR comments after secret scanning. Now checks if the 'Passed' state is already present before updating, and determines the type of previously found secrets for more accurate messaging. --- .github/workflows/trufflehog-scan.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 74a30dc..88430a0 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -176,13 +176,15 @@ jobs: if (!hasSecrets) { // No secrets found if (existing) { - // Check if existing comment had any critical/blocking ones/warnings (verified or unverified) - const hadVerified = existing.body.includes('CRITICAL') || existing.body.includes(':rotating_light:'); - const hadUnverified = existing.body.includes('Warning') || existing.body.includes('Unverified'); + // Check if existing comment already shows "Passed" state + const alreadyPassed = existing.body.includes(':white_check_mark: Secret Scanning Passed'); - if (hadVerified || hadUnverified) { + if (!alreadyPassed) { // Update to show all secrets are now resolved + // Determine what type of secrets were previously found + const hadVerified = existing.body.includes('CRITICAL') || existing.body.includes(':rotating_light:'); const previousType = hadVerified ? 'verified secrets' : 'potential secrets'; + body = `${commentMarker} ## :white_check_mark: Secret Scanning Passed From 99b8e96c007831437c50ef240c2ba1982c016022 Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 30 Jan 2026 01:00:43 +0530 Subject: [PATCH 48/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 38 ++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 88430a0..3b7ca76 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -81,22 +81,38 @@ jobs: id: parse if: github.event_name != 'workflow_dispatch' run: | - # Capture TruffleHog JSON output by re-running with same args + # Scan the current state of PR files (not git history) + # This ensures renamed files and removed secrets are handled correctly echo "Parsing TruffleHog results..." VERIFIED_COUNT=0 UNVERIFIED_COUNT=0 + # Checkout PR head to scan current file state + git checkout ${{ github.event.pull_request.head.sha }} --quiet + + # Get list of files changed in this PR + CHANGED_FILES=$(git diff --name-only --diff-filter=ACMR ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }} | grep -v '^$' || true) + + if [ -z "$CHANGED_FILES" ]; then + echo "No files changed in PR" + echo "verified_count=0" >> $GITHUB_OUTPUT + echo "unverified_count=0" >> $GITHUB_OUTPUT + exit 0 + fi + + echo "Scanning changed files:" + echo "$CHANGED_FILES" + + # Scan only the changed files in their current state using filesystem scanner SCAN_OUTPUT=$(docker run --rm -v "$(pwd)":/tmp -w /tmp \ ghcr.io/trufflesecurity/trufflehog:latest \ - git file:///tmp/ \ - --since-commit ${{ github.event.pull_request.base.sha }} \ - --branch ${{ github.event.pull_request.head.sha }} \ + filesystem /tmp/ \ --json \ ${{ steps.config.outputs.exclude_args }} \ --no-update 2>/dev/null || true) - # Parse JSON lines and create GitHub annotations + # Parse JSON lines and filter to only changed files if [ -n "$SCAN_OUTPUT" ]; then while IFS= read -r line; do # Skip non-JSON lines (info logs) @@ -104,8 +120,16 @@ jobs: continue fi - FILE=$(echo "$line" | jq -r '.SourceMetadata.Data.Git.file // "unknown"') - LINE_NUM=$(echo "$line" | jq -r '.SourceMetadata.Data.Git.line // 1') + FILE=$(echo "$line" | jq -r '.SourceMetadata.Data.Filesystem.file // "unknown"') + # Remove /tmp/ prefix if present + FILE="${FILE#/tmp/}" + + # Only count secrets in files that are part of this PR + if ! echo "$CHANGED_FILES" | grep -qx "$FILE"; then + continue + fi + + LINE_NUM=$(echo "$line" | jq -r '.SourceMetadata.Data.Filesystem.line // 1') DETECTOR=$(echo "$line" | jq -r '.DetectorName // "Secret"') VERIFIED=$(echo "$line" | jq -r '.Verified // false') From c395591a90151be093e740c0326afab443fe6fd6 Mon Sep 17 00:00:00 2001 From: Aditya Varma Gottumukkala Date: Fri, 30 Jan 2026 13:03:43 +0530 Subject: [PATCH 49/49] Update trufflehog-scan.yml --- .github/workflows/trufflehog-scan.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/trufflehog-scan.yml b/.github/workflows/trufflehog-scan.yml index 3b7ca76..6f04b7e 100644 --- a/.github/workflows/trufflehog-scan.yml +++ b/.github/workflows/trufflehog-scan.yml @@ -91,8 +91,10 @@ jobs: # Checkout PR head to scan current file state git checkout ${{ github.event.pull_request.head.sha }} --quiet - # Get list of files changed in this PR - CHANGED_FILES=$(git diff --name-only --diff-filter=ACMR ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }} | grep -v '^$' || true) + # Get list of files changed in this PR (with rename detection) + # -M enables rename detection, showing only the new filename for renamed files + # --diff-filter=d excludes deleted files (we only want files that exist in the PR head) + CHANGED_FILES=$(git diff --name-only -M --diff-filter=d ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }} | grep -v '^$' || true) if [ -z "$CHANGED_FILES" ]; then echo "No files changed in PR"