@@ -48,18 +48,55 @@ jobs:
4848 env :
4949 PR_BASE_SHA : ${{ github.event.pull_request.base.sha }}
5050 PR_HEAD_SHA : ${{ github.event.pull_request.head.sha }}
51- run : git fetch --depth=1 origin "$PR_BASE_SHA" "$PR_HEAD_SHA"
51+ run : git fetch --depth=1 origin "${ PR_BASE_SHA} " "${ PR_HEAD_SHA} "
5252
5353 - name : Fetch base and head commits (merge_group)
5454 if : github.event_name == 'merge_group'
5555 env :
5656 MERGE_GROUP_BASE_SHA : ${{ github.event.merge_group.base_sha }}
5757 MERGE_GROUP_HEAD_SHA : ${{ github.event.merge_group.head_sha }}
58- run : git fetch --depth=1 origin "$MERGE_GROUP_BASE_SHA" "$MERGE_GROUP_HEAD_SHA"
58+ run : git fetch --depth=1 origin "${ MERGE_GROUP_BASE_SHA} " "${ MERGE_GROUP_HEAD_SHA} "
5959
6060 - name : Remove persisted credentials
6161 run : git config --unset-all http.https://github.com/.extraheader
6262
63+ - name : Fetch org-wide TruffleHog exclude patterns
64+ env :
65+ GH_TOKEN : ${{ github.token }}
66+ WORKFLOW_REF : ${{ github.workflow_ref }}
67+ run : |
68+ DEST=/tmp/trufflehog-exclude.txt
69+ REPO=grafana/security-github-actions
70+ FILE_PATH=trufflehog/exclude-paths.txt
71+
72+ # Resolve the ref the calling workflow used (e.g. @main, @feature/branch, @v1).
73+ CALLER_REF="main"
74+ if [[ -n "${WORKFLOW_REF}" ]]; then
75+ CALLER_REF=$(echo "${WORKFLOW_REF}" | sed 's|.*@||; s|^refs/heads/||; s|^refs/tags/||')
76+ fi
77+
78+ LOADED=false
79+ for REF in main "${CALLER_REF}"; do
80+ [[ "$LOADED" == "true" ]] && break
81+ if gh api "repos/${REPO}/contents/${FILE_PATH}?ref=${REF}" \
82+ -H "Accept: application/vnd.github.v3.raw" -o "${DEST}" 2>/dev/null && [[ -s "${DEST}" ]]; then
83+ echo "Loaded exclude patterns from ${REPO}@${REF} (GitHub API)"
84+ LOADED=true
85+ elif curl -fsSL "https://raw.githubusercontent.com/${REPO}/${REF}/${FILE_PATH}" \
86+ -o "${DEST}" 2>/dev/null && [[ -s "${DEST}" ]]; then
87+ echo "Loaded exclude patterns from raw.githubusercontent.com@${REF}"
88+ LOADED=true
89+ fi
90+ done
91+
92+ if [[ "$LOADED" != "true" ]]; then
93+ echo "::warning::Could not fetch TruffleHog exclude patterns from ${REPO} (tried main and ${CALLER_REF}). Scanning without exclusions."
94+ touch "${DEST}"
95+ fi
96+
97+ echo "--- effective exclude patterns ---"
98+ cat "${DEST}"
99+
63100 - name : Install TruffleHog
64101 run : |
65102 # Download binary directly from GitHub releases for supply chain security
@@ -77,33 +114,10 @@ jobs:
77114 sudo chmod +x /usr/local/bin/trufflehog
78115 trufflehog --version
79116
80- - name : Create global exclusions
81- run : |
82- # Create centralized exclusion patterns for common false positives
83- cat > /tmp/trufflehog-exclude.txt <<'EOF'
84- # Lock files and checksums (contain hashes, not secrets)
85- path:go\.sum$
86- path:go\.mod$
87- # Dependency manifests (contain URLs that trigger false positives)
88- path:package\.json$
89- path:package-lock\.json$
90- path:pnpm-lock\.yaml$
91- path:yarn\.lock$
92- path:poetry\.lock$
93- path:Pipfile\.lock$
94- path:uv\.lock$
95- path:Cargo\.lock$
96- path:Gemfile\.lock$
97- # Grafana plugin metadata
98- path:grafana\.json$
99- EOF
100-
101- echo "Created global exclusion patterns:"
102- cat /tmp/trufflehog-exclude.txt
103-
104117 - name : Scan for secrets
105118 id : scan
106119 env :
120+ EVENT_NAME : ${{ github.event_name }}
107121 PR_BASE_SHA : ${{ github.event.pull_request.base.sha }}
108122 PR_HEAD_SHA : ${{ github.event.pull_request.head.sha }}
109123 MERGE_GROUP_BASE_SHA : ${{ github.event.merge_group.base_sha }}
@@ -112,28 +126,24 @@ jobs:
112126 set +e
113127 echo "[]" > results.json
114128
115- if [[ "${{ github.event_name }}" == "pull_request" ]] || [[ "${{ github.event_name }}" == "merge_group" ]]; then
116- # PR / merge queue: scan only paths that differ from base..head (not the entire checkout)
117- if [[ "${{ github.event_name }}" == "pull_request" ]]; then
129+ # Extract non-comment, non-blank patterns for the shell pre-filter.
130+ grep -vE '^\s*#|^\s*$' /tmp/trufflehog-exclude.txt > /tmp/exclude-regexes.txt 2>/dev/null || true
131+
132+ if [[ "${EVENT_NAME}" == "pull_request" ]] || [[ "${EVENT_NAME}" == "merge_group" ]]; then
133+ if [[ "${EVENT_NAME}" == "pull_request" ]]; then
118134 echo "Scanning changed files in PR..."
119- git diff --name-only "$PR_BASE_SHA" "$PR_HEAD_SHA" > changed-files.txt
135+ git diff --name-only "${ PR_BASE_SHA} " "${ PR_HEAD_SHA} " > changed-files.txt
120136 else
121137 echo "Scanning changed files in merge group..."
122- git diff --name-only "$MERGE_GROUP_BASE_SHA" "$MERGE_GROUP_HEAD_SHA" > changed-files.txt
138+ git diff --name-only "${ MERGE_GROUP_BASE_SHA} " "${ MERGE_GROUP_HEAD_SHA} " > changed-files.txt
123139 fi
124140
125141 if [[ -s changed-files.txt ]]; then
126142 while IFS= read -r file; do
127- # Get just the filename
128- filename=$(basename "$file")
129-
130- # Skip excluded files (use case statement for cleaner matching)
131- case "$filename" in
132- go.sum|go.mod|package.json|package-lock.json|pnpm-lock.yaml|yarn.lock|poetry.lock|Pipfile.lock|uv.lock|Cargo.lock|Gemfile.lock|grafana.json)
133- echo "Skipping: ${file} (excluded manifest/lock file)"
134- continue
135- ;;
136- esac
143+ if [[ -s /tmp/exclude-regexes.txt ]] && echo "$file" | grep -qEf /tmp/exclude-regexes.txt 2>/dev/null; then
144+ echo "Skipping: ${file} (matches exclude pattern)"
145+ continue
146+ fi
137147
138148 if [[ -f "${file}" ]]; then
139149 echo "Scanning: ${file}"
@@ -144,7 +154,6 @@ jobs:
144154 echo "No files changed"
145155 fi
146156 else
147- # push to main (and any other events): full filesystem scan
148157 echo "Scanning current filesystem..."
149158 trufflehog filesystem . --exclude-paths /tmp/trufflehog-exclude.txt --concurrency 16 --json --no-update --results=verified,unverified > results.ndjson || true
150159 fi
@@ -161,8 +170,12 @@ jobs:
161170 # Filter out CHANGELOG git hashes if we have results
162171 if jq -e 'length > 0' results.json >/dev/null 2>&1; then
163172 jq '[.[] | select(
164- ((.SourceMetadata?.Data?.Filesystem?.file // .SourceMetadata?.Data?.Git?.file) // "") as $file |
165- !(($file | test("CHANGELOG|HISTORY\\.md|NEWS\\.md"; "i")) and (.Raw | test("^[0-9a-f]{7,40}$"; "i")))
173+ (
174+ (try .SourceMetadata.Data.Filesystem.file catch null) //
175+ (try .SourceMetadata.Data.Git.file catch null) //
176+ ""
177+ ) as $file |
178+ ((($file | test("CHANGELOG|HISTORY\\.md|NEWS\\.md"; "i")) and (.Raw | test("^[0-9a-f]{7,40}$"; "i"))) | not)
166179 )]' results.json > results.json.tmp && mv results.json.tmp results.json
167180 fi
168181 else
@@ -196,26 +209,29 @@ jobs:
196209
197210 - name : Delete resolved comment
198211 if : ${{ !cancelled() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && steps.scan.outputs.total == '0' }}
212+ env :
213+ GH_TOKEN : ${{ github.token }}
214+ GH_REPOSITORY : ${{ github.repository }}
215+ PR_NUMBER : ${{ github.event.pull_request.number }}
199216 run : |
200217 # Find and delete TruffleHog comment if secrets have been resolved
201- COMMENT_ID=$(gh api repos/${{ github.repository }} /issues/${{ github.event.pull_request.number }} /comments --jq '.[] | select(.body | contains("<!-- trufflehog-secret-scan-comment -->")) | .id' | head -1 || echo "")
218+ COMMENT_ID=$(gh api " repos/${GH_REPOSITORY} /issues/${PR_NUMBER} /comments" --jq '.[] | select(.body | contains("<!-- trufflehog-secret-scan-comment -->")) | .id' | head -1 || echo "")
202219
203220 if [[ -n "$COMMENT_ID" ]]; then
204221 echo "Secrets resolved - deleting previous warning comment (ID: ${COMMENT_ID})"
205- gh api -X DELETE /repos/${{ github.repository }} /issues/comments/${COMMENT_ID}
222+ gh api -X DELETE " /repos/${GH_REPOSITORY} /issues/comments/${COMMENT_ID}"
206223 echo "Comment deleted successfully"
207224 else
208225 echo "No existing TruffleHog comment to delete"
209226 fi
210- env :
211- GH_TOKEN : ${{ github.token }}
212227
213228 - name : Generate PR comment
214229 if : ${{ !cancelled() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && steps.scan.outputs.total > 0 }}
215230 id : comment-body
231+ env :
232+ VERIFIED : ${{ steps.scan.outputs.verified }}
233+ UNVERIFIED : ${{ steps.scan.outputs.unverified }}
216234 run : |
217- VERIFIED=${{ steps.scan.outputs.verified }}
218- UNVERIFIED=${{ steps.scan.outputs.unverified }}
219235 TOTAL=$((VERIFIED+UNVERIFIED))
220236
221237 if [[ $TOTAL -eq 0 ]]; then
@@ -232,9 +248,9 @@ jobs:
232248 "- " +
233249 (if .Verified then "**VERIFIED SECRET**" else "**Possible secret**" end) +
234250 " (" + .DetectorName + ") at `" +
235- ((.SourceMetadata? .Data? .Filesystem? .file // .SourceMetadata? .Data? .Git? .file) // "unknown") +
251+ (((try .SourceMetadata.Data.Filesystem.file catch null) // (try .SourceMetadata.Data.Git.file catch null) ) // "unknown") +
236252 ":" +
237- ((.SourceMetadata? .Data? .Filesystem? .line // .SourceMetadata? .Data? .Git? .line) | tostring) +
253+ (((try .SourceMetadata.Data.Filesystem.line catch null) // (try .SourceMetadata.Data.Git.line catch null) ) | tostring) +
238254 "` → `" +
239255 (if (.Raw | length) > 8 then (.Raw[:4] + "***" + .Raw[-4:]) else "***" end) +
240256 "`"' results.json 2>/dev/null || echo "- Error processing results")
@@ -281,23 +297,28 @@ jobs:
281297 - name : Create scan report
282298 env :
283299 GITHUB_REF_NAME : ${{ github.ref_name }}
300+ GH_REPOSITORY : ${{ github.repository }}
301+ GH_SHA : ${{ github.sha }}
302+ TOTAL_SECRETS : ${{ steps.scan.outputs.total }}
303+ VERIFIED_SECRETS : ${{ steps.scan.outputs.verified }}
304+ UNVERIFIED_SECRETS : ${{ steps.scan.outputs.unverified }}
284305 run : |
285306 {
286307 echo "TruffleHog Scan Report"
287308 echo "====================="
288309 echo "Date: $(date)"
289- echo "Repository: ${{ github.repository } }"
310+ echo "Repository: ${GH_REPOSITORY }"
290311 echo "Branch: ${GITHUB_REF_NAME}"
291- echo "Commit: ${{ github.sha } }"
312+ echo "Commit: ${GH_SHA }"
292313 echo ""
293314 echo "Summary:"
294- echo "- Total secrets: ${{ steps.scan.outputs.total } }"
295- echo "- Verified: ${{ steps.scan.outputs.verified } }"
296- echo "- Unverified: ${{ steps.scan.outputs.unverified } }"
315+ echo "- Total secrets: ${TOTAL_SECRETS }"
316+ echo "- Verified: ${VERIFIED_SECRETS }"
317+ echo "- Unverified: ${UNVERIFIED_SECRETS }"
297318 echo ""
298319 echo "Detailed Results:"
299320 if [[ -f "results.json" && -s "results.json" ]] && jq empty results.json 2>/dev/null; then
300- jq -r '.[] | "- " + (if .Verified then "VERIFIED" else "Unverified" end) + " " + .DetectorName + " at " + ((.SourceMetadata? .Data? .Filesystem? .file // .SourceMetadata? .Data? .Git? .file) // "unknown") + ":" + ((.SourceMetadata? .Data? .Filesystem? .line // .SourceMetadata? .Data? .Git? .line) | tostring) + " → " + (if (.Raw | length) > 8 then (.Raw[:4] + "***" + .Raw[-4:]) else "***" end)' results.json 2>/dev/null || echo "Error processing results"
321+ jq -r '.[] | "- " + (if .Verified then "VERIFIED" else "Unverified" end) + " " + .DetectorName + " at " + (((try .SourceMetadata.Data.Filesystem.file catch null) // (try .SourceMetadata.Data.Git.file catch null)) // "unknown") + ":" + (((try .SourceMetadata.Data.Filesystem.line catch null) // (try .SourceMetadata.Data.Git.line catch null) ) | tostring) + " → " + (if (.Raw | length) > 8 then (.Raw[:4] + "***" + .Raw[-4:]) else "***" end)' results.json 2>/dev/null || echo "Error processing results"
301322 else
302323 echo "No secrets detected"
303324 fi
0 commit comments