-
Notifications
You must be signed in to change notification settings - Fork 375
Add TruffleHog shared agentic workflow for secret detection in smoke-codex #29512
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8a23e9b
3e4ad98
97bd71b
86408e8
1e4ba70
b493ca6
dc1d9bb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,223 @@ | ||
| --- | ||
| jobs: | ||
| trufflehog_scan: | ||
| runs-on: ubuntu-latest | ||
| needs: [agent, detection] | ||
| if: always() && needs.agent.result != 'skipped' && needs.detection.result != 'skipped' | ||
| permissions: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The if: always() condition is good, but consider also checking needs.agent.outputs.artifact_uploaded == true to avoid wasted scans when no artifact was produced. |
||
| contents: read | ||
| outputs: | ||
| secrets_found: ${{ steps.evaluate.outputs.secrets_found }} | ||
| secrets_locations: ${{ steps.evaluate.outputs.secrets_locations }} | ||
| steps: | ||
| - name: Download agent output artifact | ||
| id: download-agent | ||
| continue-on-error: true | ||
| uses: actions/download-artifact@v8 | ||
| with: | ||
| name: agent | ||
| path: /tmp/gh-aw | ||
|
|
||
| - name: Download cache-memory artifact | ||
| id: download-cache-memory | ||
| continue-on-error: true | ||
| uses: actions/download-artifact@v8 | ||
| with: | ||
| name: cache-memory | ||
| path: /tmp/gh-aw/cache-memory | ||
|
|
||
| - name: Download repo-memory artifact | ||
| id: download-repo-memory | ||
| continue-on-error: true | ||
| uses: actions/download-artifact@v8 | ||
| with: | ||
|
Comment on lines
+13
to
+33
|
||
| name: repo-memory-default | ||
| path: /tmp/gh-aw/repo-memory/default | ||
|
|
||
| - name: Install TruffleHog | ||
| id: install-trufflehog | ||
| env: | ||
| TRUFFLEHOG_VERSION: "3.88.27" | ||
| run: | | ||
| echo "Installing TruffleHog v${TRUFFLEHOG_VERSION}..." | ||
| curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh | sh -s -- -b /usr/local/bin "v${TRUFFLEHOG_VERSION}" | ||
|
|
||
| trufflehog --version | ||
|
|
||
| - name: Scan agent output for secrets | ||
| id: scan-agent-output | ||
| continue-on-error: true | ||
| run: | | ||
| mkdir -p /tmp/gh-aw/trufflehog | ||
| SCAN_DIR="/tmp/gh-aw" | ||
| OUTPUT_FILE="/tmp/gh-aw/trufflehog/agent-output-results.jsonl" | ||
| if [ -d "$SCAN_DIR" ] && find "$SCAN_DIR" -mindepth 1 -maxdepth 1 -quit 2>/dev/null | grep -q .; then | ||
| echo "Scanning agent output in $SCAN_DIR" | ||
| trufflehog filesystem "$SCAN_DIR" \ | ||
| --json --no-update --fail \ | ||
| --exclude-paths /tmp/gh-aw/cache-memory \ | ||
| --exclude-paths /tmp/gh-aw/repo-memory \ | ||
| --exclude-paths /tmp/gh-aw/trufflehog \ | ||
| 2>/dev/null | tee "$OUTPUT_FILE" || SCAN_EXIT=${PIPESTATUS[0]} | ||
| SCAN_EXIT=${SCAN_EXIT:-0} | ||
| else | ||
| echo "Agent output directory is empty or missing, skipping" | ||
| SCAN_EXIT=0 | ||
| fi | ||
| if [ "$SCAN_EXIT" -eq 183 ]; then | ||
| echo "secrets_found=true" >> "$GITHUB_OUTPUT" | ||
| fi | ||
|
|
||
| - name: Scan cache-memory for secrets | ||
| id: scan-cache-memory | ||
| continue-on-error: true | ||
| run: | | ||
| mkdir -p /tmp/gh-aw/trufflehog | ||
| SCAN_DIR="/tmp/gh-aw/cache-memory" | ||
| OUTPUT_FILE="/tmp/gh-aw/trufflehog/cache-memory-results.jsonl" | ||
| if [ -d "$SCAN_DIR" ] && find "$SCAN_DIR" -mindepth 1 -maxdepth 1 -quit 2>/dev/null | grep -q .; then | ||
| echo "Scanning cache-memory in $SCAN_DIR" | ||
| trufflehog filesystem "$SCAN_DIR" --json --no-update --fail 2>/dev/null | tee "$OUTPUT_FILE" || SCAN_EXIT=${PIPESTATUS[0]} | ||
| SCAN_EXIT=${SCAN_EXIT:-0} | ||
| else | ||
| echo "cache-memory directory is empty or missing, skipping" | ||
| SCAN_EXIT=0 | ||
| fi | ||
| if [ "$SCAN_EXIT" -eq 183 ]; then | ||
| echo "secrets_found=true" >> "$GITHUB_OUTPUT" | ||
| fi | ||
|
Comment on lines
+48
to
+87
|
||
|
|
||
| - name: Scan repo-memory for secrets | ||
| id: scan-repo-memory | ||
| continue-on-error: true | ||
| run: | | ||
| mkdir -p /tmp/gh-aw/trufflehog | ||
| SCAN_DIR="/tmp/gh-aw/repo-memory" | ||
| OUTPUT_FILE="/tmp/gh-aw/trufflehog/repo-memory-results.jsonl" | ||
| if [ -d "$SCAN_DIR" ] && find "$SCAN_DIR" -mindepth 1 -maxdepth 1 -quit 2>/dev/null | grep -q .; then | ||
| echo "Scanning repo-memory in $SCAN_DIR" | ||
| trufflehog filesystem "$SCAN_DIR" --json --no-update --fail 2>/dev/null | tee "$OUTPUT_FILE" || SCAN_EXIT=${PIPESTATUS[0]} | ||
| SCAN_EXIT=${SCAN_EXIT:-0} | ||
| else | ||
| echo "repo-memory directory is empty or missing, skipping" | ||
| SCAN_EXIT=0 | ||
| fi | ||
| if [ "$SCAN_EXIT" -eq 183 ]; then | ||
| echo "secrets_found=true" >> "$GITHUB_OUTPUT" | ||
| fi | ||
|
|
||
| - name: Evaluate TruffleHog results | ||
| id: evaluate | ||
| if: always() | ||
| env: | ||
| AGENT_FOUND: ${{ steps.scan-agent-output.outputs.secrets_found }} | ||
| CACHE_FOUND: ${{ steps.scan-cache-memory.outputs.secrets_found }} | ||
| REPO_FOUND: ${{ steps.scan-repo-memory.outputs.secrets_found }} | ||
| run: | | ||
| echo "===================================" | ||
| echo "🔍 TruffleHog Scan Summary" | ||
| echo "===================================" | ||
| echo "Agent output: ${AGENT_FOUND:-clean}" | ||
| echo "Cache-memory: ${CACHE_FOUND:-clean}" | ||
| echo "Repo-memory: ${REPO_FOUND:-clean}" | ||
| echo "===================================" | ||
|
|
||
| if [[ "$AGENT_FOUND" == "true" || "$CACHE_FOUND" == "true" || "$REPO_FOUND" == "true" ]]; then | ||
| LOCATIONS=() | ||
| [[ "$AGENT_FOUND" == "true" ]] && LOCATIONS+=("agent output") | ||
| [[ "$CACHE_FOUND" == "true" ]] && LOCATIONS+=("cache-memory") | ||
| [[ "$REPO_FOUND" == "true" ]] && LOCATIONS+=("repo-memory") | ||
| LOCATIONS_STR=$(IFS=', '; echo "${LOCATIONS[*]}") | ||
| echo "secrets_found=true" >> "$GITHUB_OUTPUT" | ||
| echo "secrets_locations=${LOCATIONS_STR}" >> "$GITHUB_OUTPUT" | ||
| echo "::error::TruffleHog detected secrets in: ${LOCATIONS_STR}" | ||
| exit 1 | ||
| else | ||
| echo "secrets_found=false" >> "$GITHUB_OUTPUT" | ||
| echo "✅ No secrets detected by TruffleHog" | ||
| fi | ||
|
|
||
| - name: Upload TruffleHog scan results | ||
| if: always() | ||
| uses: actions/upload-artifact@v7.0.1 | ||
| with: | ||
| name: trufflehog-scan-results | ||
| path: /tmp/gh-aw/trufflehog/ | ||
| if-no-files-found: ignore | ||
|
|
||
| conclusion: | ||
| pre-steps: | ||
| - name: Report TruffleHog secret scan failure | ||
| if: always() && needs.trufflehog_scan.result == 'failure' && needs.trufflehog_scan.outputs.secrets_found == 'true' | ||
| continue-on-error: true | ||
| uses: actions/github-script@v9 | ||
| env: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔬 Smoke Test Review Comment — The |
||
| GH_AW_TRUFFLEHOG_SECRETS_LOCATIONS: ${{ needs.trufflehog_scan.outputs.secrets_locations }} | ||
| GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | ||
| GH_AW_WORKFLOW_NAME: ${{ github.workflow }} | ||
| with: | ||
| script: | | ||
| const locations = process.env.GH_AW_TRUFFLEHOG_SECRETS_LOCATIONS || 'unknown locations'; | ||
| const runUrl = process.env.GH_AW_RUN_URL; | ||
| const workflowName = process.env.GH_AW_WORKFLOW_NAME; | ||
| const runNumber = context.runNumber; | ||
| const { owner, repo } = context.repo; | ||
| core.error(`🔐 TruffleHog detected secrets in: ${locations}`); | ||
| const title = `🔐 Secrets detected in workflow run: ${workflowName} #${runNumber}`; | ||
| const body = [ | ||
| '> [!CAUTION]', | ||
| '> **TruffleHog detected secrets in the agentic workflow output.**', | ||
| '', | ||
| `**Locations:** \`${locations}\``, | ||
| '', | ||
| `**Workflow run:** [${workflowName} #${runNumber}](${runUrl})`, | ||
| '', | ||
| 'Please review the `trufflehog-scan-results` artifact in the workflow run for details.', | ||
| 'Rotate any exposed credentials immediately.', | ||
| ].join('\n'); | ||
| const issue = await github.rest.issues.create({ owner, repo, title, body, labels: ['security'] }); | ||
| core.info(`Created secret detection issue: ${issue.data.html_url}`); | ||
| --- | ||
| <!-- | ||
| # TruffleHog Secret Detection | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot Wrap in xml comments
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 97bd71b — the markdown documentation section is now wrapped in |
||
|
|
||
| This shared workflow adds [TruffleHog](https://github.com/trufflesecurity/trufflehog) secret scanning | ||
| as a dedicated `trufflehog_scan` job that runs after the `detection` job. It scans the agent's output, | ||
| cache-memory, and repo-memory for accidentally leaked secrets (API keys, tokens, credentials, etc.). | ||
|
|
||
| ## How It Works | ||
|
|
||
| 1. **Separate job** — `trufflehog_scan` runs after the `detection` job completes | ||
| 2. **Download artifacts** — fetches `agent`, `cache-memory`, and `repo-memory` artifacts (continue-on-error) | ||
| 3. **Install TruffleHog** — pinned to a specific version | ||
| 4. **Scan agent output** — scans `/tmp/gh-aw/` (agent output and code patches) | ||
| 5. **Scan cache-memory** — scans `/tmp/gh-aw/cache-memory/` | ||
| 6. **Scan repo-memory** — scans `/tmp/gh-aw/repo-memory/` | ||
| 7. **Evaluate** — aggregates results; sets `secrets_found=true` output and fails the job if secrets detected | ||
| 8. **Upload results** — saves JSONL scan result files as `trufflehog-scan-results` artifact for review | ||
| 9. **Failure report** — a `jobs.conclusion.pre-steps` entry creates a GitHub issue with the findings | ||
| when secrets are detected | ||
|
|
||
| ## Job Outputs | ||
|
|
||
| | Output | Value | | ||
| |--------|-------| | ||
| | `secrets_found` | `true` or `false` | | ||
| | `secrets_locations` | Comma-separated list of locations where secrets were found | | ||
|
|
||
| ## Failure Reporting | ||
|
|
||
| When `secrets_found=true` the `trufflehog_scan` job fails. The conclusion job (which automatically | ||
| depends on all jobs) then runs the pre-step `Report TruffleHog secret scan failure`, which creates | ||
| a GitHub issue titled `🔐 Secrets detected in workflow run: <name> #<run>` with details about the | ||
| finding and a link to the `trufflehog-scan-results` artifact. | ||
|
|
||
| ## Usage | ||
|
|
||
| ```yaml | ||
| --- | ||
| imports: | ||
| - shared/trufflehog.md | ||
| --- | ||
| ``` | ||
| --> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔬 Smoke Test Review Comment — The
needs: [agent, detection]dependency ensures TruffleHog runs only after the agent has produced its output artifacts. This is a sound pattern for security scanning jobs. Consider also addingupdate_cache_memorytoneedsif cache artifacts need scanning, or document why they're excluded.