Skip to content

Commit a487395

Browse files
authored
Add TruffleHog shared agentic workflow for secret detection in smoke-codex (#29512)
1 parent 38d9759 commit a487395

4 files changed

Lines changed: 443 additions & 32 deletions

File tree

.changeset/patch-add-trufflehog-shared-workflow.md

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
---
2+
jobs:
3+
trufflehog_scan:
4+
runs-on: ubuntu-latest
5+
needs: [agent, detection]
6+
if: always() && needs.agent.result != 'skipped' && needs.detection.result != 'skipped'
7+
permissions:
8+
contents: read
9+
outputs:
10+
secrets_found: ${{ steps.evaluate.outputs.secrets_found }}
11+
secrets_locations: ${{ steps.evaluate.outputs.secrets_locations }}
12+
steps:
13+
- name: Download agent output artifact
14+
id: download-agent
15+
continue-on-error: true
16+
uses: actions/download-artifact@v8
17+
with:
18+
name: agent
19+
path: /tmp/gh-aw
20+
21+
- name: Download cache-memory artifact
22+
id: download-cache-memory
23+
continue-on-error: true
24+
uses: actions/download-artifact@v8
25+
with:
26+
name: cache-memory
27+
path: /tmp/gh-aw/cache-memory
28+
29+
- name: Download repo-memory artifact
30+
id: download-repo-memory
31+
continue-on-error: true
32+
uses: actions/download-artifact@v8
33+
with:
34+
name: repo-memory-default
35+
path: /tmp/gh-aw/repo-memory/default
36+
37+
- name: Install TruffleHog
38+
id: install-trufflehog
39+
env:
40+
TRUFFLEHOG_VERSION: "3.88.27"
41+
run: |
42+
echo "Installing TruffleHog v${TRUFFLEHOG_VERSION}..."
43+
curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh | sh -s -- -b /usr/local/bin "v${TRUFFLEHOG_VERSION}"
44+
trufflehog --version
45+
46+
- name: Scan agent output for secrets
47+
id: scan-agent-output
48+
continue-on-error: true
49+
run: |
50+
mkdir -p /tmp/gh-aw/trufflehog
51+
SCAN_DIR="/tmp/gh-aw"
52+
OUTPUT_FILE="/tmp/gh-aw/trufflehog/agent-output-results.jsonl"
53+
if [ -d "$SCAN_DIR" ] && find "$SCAN_DIR" -mindepth 1 -maxdepth 1 -quit 2>/dev/null | grep -q .; then
54+
echo "Scanning agent output in $SCAN_DIR"
55+
trufflehog filesystem "$SCAN_DIR" \
56+
--json --no-update --fail \
57+
--exclude-paths /tmp/gh-aw/cache-memory \
58+
--exclude-paths /tmp/gh-aw/repo-memory \
59+
--exclude-paths /tmp/gh-aw/trufflehog \
60+
2>/dev/null | tee "$OUTPUT_FILE" || SCAN_EXIT=${PIPESTATUS[0]}
61+
SCAN_EXIT=${SCAN_EXIT:-0}
62+
else
63+
echo "Agent output directory is empty or missing, skipping"
64+
SCAN_EXIT=0
65+
fi
66+
if [ "$SCAN_EXIT" -eq 183 ]; then
67+
echo "secrets_found=true" >> "$GITHUB_OUTPUT"
68+
fi
69+
70+
- name: Scan cache-memory for secrets
71+
id: scan-cache-memory
72+
continue-on-error: true
73+
run: |
74+
mkdir -p /tmp/gh-aw/trufflehog
75+
SCAN_DIR="/tmp/gh-aw/cache-memory"
76+
OUTPUT_FILE="/tmp/gh-aw/trufflehog/cache-memory-results.jsonl"
77+
if [ -d "$SCAN_DIR" ] && find "$SCAN_DIR" -mindepth 1 -maxdepth 1 -quit 2>/dev/null | grep -q .; then
78+
echo "Scanning cache-memory in $SCAN_DIR"
79+
trufflehog filesystem "$SCAN_DIR" --json --no-update --fail 2>/dev/null | tee "$OUTPUT_FILE" || SCAN_EXIT=${PIPESTATUS[0]}
80+
SCAN_EXIT=${SCAN_EXIT:-0}
81+
else
82+
echo "cache-memory directory is empty or missing, skipping"
83+
SCAN_EXIT=0
84+
fi
85+
if [ "$SCAN_EXIT" -eq 183 ]; then
86+
echo "secrets_found=true" >> "$GITHUB_OUTPUT"
87+
fi
88+
89+
- name: Scan repo-memory for secrets
90+
id: scan-repo-memory
91+
continue-on-error: true
92+
run: |
93+
mkdir -p /tmp/gh-aw/trufflehog
94+
SCAN_DIR="/tmp/gh-aw/repo-memory"
95+
OUTPUT_FILE="/tmp/gh-aw/trufflehog/repo-memory-results.jsonl"
96+
if [ -d "$SCAN_DIR" ] && find "$SCAN_DIR" -mindepth 1 -maxdepth 1 -quit 2>/dev/null | grep -q .; then
97+
echo "Scanning repo-memory in $SCAN_DIR"
98+
trufflehog filesystem "$SCAN_DIR" --json --no-update --fail 2>/dev/null | tee "$OUTPUT_FILE" || SCAN_EXIT=${PIPESTATUS[0]}
99+
SCAN_EXIT=${SCAN_EXIT:-0}
100+
else
101+
echo "repo-memory directory is empty or missing, skipping"
102+
SCAN_EXIT=0
103+
fi
104+
if [ "$SCAN_EXIT" -eq 183 ]; then
105+
echo "secrets_found=true" >> "$GITHUB_OUTPUT"
106+
fi
107+
108+
- name: Evaluate TruffleHog results
109+
id: evaluate
110+
if: always()
111+
env:
112+
AGENT_FOUND: ${{ steps.scan-agent-output.outputs.secrets_found }}
113+
CACHE_FOUND: ${{ steps.scan-cache-memory.outputs.secrets_found }}
114+
REPO_FOUND: ${{ steps.scan-repo-memory.outputs.secrets_found }}
115+
run: |
116+
echo "==================================="
117+
echo "🔍 TruffleHog Scan Summary"
118+
echo "==================================="
119+
echo "Agent output: ${AGENT_FOUND:-clean}"
120+
echo "Cache-memory: ${CACHE_FOUND:-clean}"
121+
echo "Repo-memory: ${REPO_FOUND:-clean}"
122+
echo "==================================="
123+
124+
if [[ "$AGENT_FOUND" == "true" || "$CACHE_FOUND" == "true" || "$REPO_FOUND" == "true" ]]; then
125+
LOCATIONS=()
126+
[[ "$AGENT_FOUND" == "true" ]] && LOCATIONS+=("agent output")
127+
[[ "$CACHE_FOUND" == "true" ]] && LOCATIONS+=("cache-memory")
128+
[[ "$REPO_FOUND" == "true" ]] && LOCATIONS+=("repo-memory")
129+
LOCATIONS_STR=$(IFS=', '; echo "${LOCATIONS[*]}")
130+
echo "secrets_found=true" >> "$GITHUB_OUTPUT"
131+
echo "secrets_locations=${LOCATIONS_STR}" >> "$GITHUB_OUTPUT"
132+
echo "::error::TruffleHog detected secrets in: ${LOCATIONS_STR}"
133+
exit 1
134+
else
135+
echo "secrets_found=false" >> "$GITHUB_OUTPUT"
136+
echo "✅ No secrets detected by TruffleHog"
137+
fi
138+
139+
- name: Upload TruffleHog scan results
140+
if: always()
141+
uses: actions/upload-artifact@v7.0.1
142+
with:
143+
name: trufflehog-scan-results
144+
path: /tmp/gh-aw/trufflehog/
145+
if-no-files-found: ignore
146+
147+
conclusion:
148+
pre-steps:
149+
- name: Report TruffleHog secret scan failure
150+
if: always() && needs.trufflehog_scan.result == 'failure' && needs.trufflehog_scan.outputs.secrets_found == 'true'
151+
continue-on-error: true
152+
uses: actions/github-script@v9
153+
env:
154+
GH_AW_TRUFFLEHOG_SECRETS_LOCATIONS: ${{ needs.trufflehog_scan.outputs.secrets_locations }}
155+
GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
156+
GH_AW_WORKFLOW_NAME: ${{ github.workflow }}
157+
with:
158+
script: |
159+
const locations = process.env.GH_AW_TRUFFLEHOG_SECRETS_LOCATIONS || 'unknown locations';
160+
const runUrl = process.env.GH_AW_RUN_URL;
161+
const workflowName = process.env.GH_AW_WORKFLOW_NAME;
162+
const runNumber = context.runNumber;
163+
const { owner, repo } = context.repo;
164+
core.error(`🔐 TruffleHog detected secrets in: ${locations}`);
165+
const title = `🔐 Secrets detected in workflow run: ${workflowName} #${runNumber}`;
166+
const body = [
167+
'> [!CAUTION]',
168+
'> **TruffleHog detected secrets in the agentic workflow output.**',
169+
'',
170+
`**Locations:** \`${locations}\``,
171+
'',
172+
`**Workflow run:** [${workflowName} #${runNumber}](${runUrl})`,
173+
'',
174+
'Please review the `trufflehog-scan-results` artifact in the workflow run for details.',
175+
'Rotate any exposed credentials immediately.',
176+
].join('\n');
177+
const issue = await github.rest.issues.create({ owner, repo, title, body, labels: ['security'] });
178+
core.info(`Created secret detection issue: ${issue.data.html_url}`);
179+
---
180+
<!--
181+
# TruffleHog Secret Detection
182+
183+
This shared workflow adds [TruffleHog](https://github.com/trufflesecurity/trufflehog) secret scanning
184+
as a dedicated `trufflehog_scan` job that runs after the `detection` job. It scans the agent's output,
185+
cache-memory, and repo-memory for accidentally leaked secrets (API keys, tokens, credentials, etc.).
186+
187+
## How It Works
188+
189+
1. **Separate job** — `trufflehog_scan` runs after the `detection` job completes
190+
2. **Download artifacts** — fetches `agent`, `cache-memory`, and `repo-memory` artifacts (continue-on-error)
191+
3. **Install TruffleHog** — pinned to a specific version
192+
4. **Scan agent output** — scans `/tmp/gh-aw/` (agent output and code patches)
193+
5. **Scan cache-memory** — scans `/tmp/gh-aw/cache-memory/`
194+
6. **Scan repo-memory** — scans `/tmp/gh-aw/repo-memory/`
195+
7. **Evaluate** — aggregates results; sets `secrets_found=true` output and fails the job if secrets detected
196+
8. **Upload results** — saves JSONL scan result files as `trufflehog-scan-results` artifact for review
197+
9. **Failure report** — a `jobs.conclusion.pre-steps` entry creates a GitHub issue with the findings
198+
when secrets are detected
199+
200+
## Job Outputs
201+
202+
| Output | Value |
203+
|--------|-------|
204+
| `secrets_found` | `true` or `false` |
205+
| `secrets_locations` | Comma-separated list of locations where secrets were found |
206+
207+
## Failure Reporting
208+
209+
When `secrets_found=true` the `trufflehog_scan` job fails. The conclusion job (which automatically
210+
depends on all jobs) then runs the pre-step `Report TruffleHog secret scan failure`, which creates
211+
a GitHub issue titled `🔐 Secrets detected in workflow run: <name> #<run>` with details about the
212+
finding and a link to the `trufflehog-scan-results` artifact.
213+
214+
## Usage
215+
216+
```yaml
217+
---
218+
imports:
219+
- shared/trufflehog.md
220+
---
221+
```
222+
-->
223+

0 commit comments

Comments
 (0)