PDP-684 Add TruffleHog secret scanning workflow for PR validation #14
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: TruffleHog Secret Scan | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened] | |
| pull_request_target: | |
| types: [opened, synchronize, reopened] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| statuses: 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 | |
| # 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 | |
| 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: | | |
| 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 | |
| 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: 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 = '<!-- TRUFFLEHOG-SCAN-COMMENT -->'; | |
| 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 |