-
Notifications
You must be signed in to change notification settings - Fork 0
605 lines (519 loc) · 27.8 KB
/
Copy pathsecurity.yml
File metadata and controls
605 lines (519 loc) · 27.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
name: Reusable Security Scan
# Environment variables
env:
TRUFFLEHOG_VERSION: "3.90.5"
on:
workflow_call:
inputs:
show_keys:
description: "Whether to show the keys found by TruffleHog in the output"
required: false
default: false
type: boolean
notify_webhook:
description: "Whether to send webhook notifications for security alerts"
required: false
default: true
type: boolean
ignore_keys:
description: "Comma-separated list of keys to ignore as false positives (e.g., 'AWS_ACCESS_KEY_ID,API_KEY')"
required: false
default: ""
type: string
secrets:
webhook_url:
description: "Webhook URL for security alerts"
required: true
# Minimal permissions for security
permissions:
contents: read
jobs:
validate-inputs:
name: Validate Required Inputs
runs-on: ubuntu-latest
# Set minimal permissions for this job
permissions:
contents: read
steps:
- name: Validate Webhook URL
run: |
WEBHOOK_URL="${{ secrets.webhook_url }}"
if [ -z "$WEBHOOK_URL" ]; then
echo "❌ ERROR: webhook_url input is required but not provided"
echo "Please provide a webhook_url input when calling this workflow"
exit 1
fi
# Validate webhook URL format
if ! echo "$WEBHOOK_URL" | grep -qE '^https?://'; then
echo "❌ ERROR: Invalid webhook URL format - must start with http:// or https://"
exit 1
fi
echo "✅ Webhook URL validation passed"
secret-scan:
name: TruffleHog Secret Scan
runs-on: ubuntu-latest
needs: validate-inputs
# Set minimal permissions for this job
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup TruffleHog Scan Function
run: |
# Create a reusable function for TruffleHog scanning
cat > /tmp/trufflehog_scan.sh << 'EOF'
#!/bin/bash
# Function to determine scan range
get_scan_range() {
local DEFAULT_BRANCH="$1"
local CURRENT_BRANCH="$2"
local BEFORE_COMMIT="$3"
if [ "$(printf %q "$CURRENT_BRANCH")" = "$(printf %q "$DEFAULT_BRANCH")" ]; then
# On default branch - scan only the pushed commits
if [ "$(printf %q "$BEFORE_COMMIT")" = "0000000000000000000000000000000000000000" ] || [ -z "$BEFORE_COMMIT" ]; then
echo "HEAD~1"
else
echo "$BEFORE_COMMIT"
fi
else
# On feature branch - scan all commits not in default branch
echo "origin/$DEFAULT_BRANCH"
fi
}
# Function to run TruffleHog scan
run_trufflehog_scan() {
local OUTPUT_FILE="$1"
local SINCE_COMMIT="$2"
local CURRENT_BRANCH="$3"
local CURRENT_COMMIT="$4"
local IGNORE_KEYS="$5"
echo "🔍 Running TruffleHog scan..."
echo "📋 Scanning from: $(printf %q "$SINCE_COMMIT")"
echo "📋 Scanning to: $(printf %q "$CURRENT_COMMIT")"
echo "🌿 Branch: $(printf %q "$CURRENT_BRANCH")"
# Show ignored keys if any
if [ -n "$IGNORE_KEYS" ]; then
echo "🚫 Ignoring keys: $(printf %q "$IGNORE_KEYS")"
fi
echo ""
# Show commits to be scanned
echo "📝 Commits to be scanned:"
if [ "$(printf %q "$CURRENT_BRANCH")" = "$(printf %q "$DEFAULT_BRANCH")" ]; then
git log --oneline "$(printf %q "$SINCE_COMMIT")..$(printf %q "$CURRENT_COMMIT")" || echo "No commits found in range"
else
git log --oneline "origin/$(printf %q "$DEFAULT_BRANCH")..HEAD" || echo "No commits found in range"
fi
echo ""
# Run TruffleHog
set +e # Don't exit on error, we want to capture the exit code
trufflehog git file://. \
--since-commit="$(printf %q "$SINCE_COMMIT")" \
--branch="HEAD" \
--no-verification \
--force-skip-binaries \
--force-skip-archives \
--no-update \
--json \
--log-level=2 \
> "$OUTPUT_FILE" 2>&1
local TRUFFLEHOG_EXIT_CODE=$?
set -e # Re-enable exit on error
printf "📊 TruffleHog scan completed with exit code: %d\n" "$TRUFFLEHOG_EXIT_CODE"
return $TRUFFLEHOG_EXIT_CODE
}
# Function to filter out ignored keys
filter_ignored_keys() {
local INPUT_FILE="$1"
local OUTPUT_FILE="$2"
local IGNORE_KEYS="$3"
if [ -z "$IGNORE_KEYS" ]; then
# No keys to ignore, just copy the input to output
cp "$INPUT_FILE" "$OUTPUT_FILE"
return 0
fi
echo "🔍 Filtering out ignored keys..."
# Create a temporary file for filtered results
local TEMP_FILTERED="/tmp/filtered_$(basename "$OUTPUT_FILE")"
# Initialize output file
touch "$OUTPUT_FILE"
# Process each line that contains SourceMetadata (actual findings)
while IFS= read -r line; do
if echo "$line" | grep -q '"SourceMetadata"'; then
# Check if this finding should be ignored
local SHOULD_IGNORE=false
# Convert comma-separated ignore keys to array and check each one
IFS=',' read -ra IGNORE_ARRAY <<< "$IGNORE_KEYS"
for ignore_key in "${IGNORE_ARRAY[@]}"; do
# Trim whitespace
ignore_key=$(echo "$ignore_key" | xargs)
# Check if the Raw field contains this ignored key
if echo "$line" | grep -qi "\"Raw\".*\"$ignore_key\""; then
echo "🚫 Ignoring finding containing key: $ignore_key"
SHOULD_IGNORE=true
break
fi
done
# Only add to output if not ignored
if [ "$SHOULD_IGNORE" = false ]; then
echo "$line" >> "$OUTPUT_FILE"
fi
else
# Keep non-finding lines (log messages, etc.)
echo "$line" >> "$OUTPUT_FILE"
fi
done < "$INPUT_FILE"
echo "✅ Filtering completed"
}
EOF
chmod +x /tmp/trufflehog_scan.sh
- name: Install TruffleHog (Secure)
continue-on-error: true
run: |
# Use environment variable for version
TRUFFLEHOG_VERSION="${{ env.TRUFFLEHOG_VERSION }}"
TRUFFLEHOG_ARCH="linux_amd64"
TRUFFLEHOG_BINARY="trufflehog_${TRUFFLEHOG_VERSION}_${TRUFFLEHOG_ARCH}.tar.gz"
TRUFFLEHOG_CHECKSUMS="trufflehog_${TRUFFLEHOG_VERSION}_checksums.txt"
printf "🔒 Installing TruffleHog v%s\n" "$(printf %q "$TRUFFLEHOG_VERSION")"
# Create temporary directory
TEMP_DIR=$(mktemp -d)
cd "$TEMP_DIR"
# Download binary from GitHub releases (more secure than raw script)
echo "📥 Downloading TruffleHog binary..."
curl -sSfL -o "$(printf %q "$TRUFFLEHOG_BINARY")" \
"https://github.com/trufflesecurity/trufflehog/releases/download/v$(printf %q "$TRUFFLEHOG_VERSION")/$(printf %q "$TRUFFLEHOG_BINARY")"
# Download checksums file with correct filename format
echo "📥 Downloading checksums..."
curl -sSfL -o "$(printf %q "$TRUFFLEHOG_CHECKSUMS")" \
"https://github.com/trufflesecurity/trufflehog/releases/download/v$(printf %q "$TRUFFLEHOG_VERSION")/$(printf %q "$TRUFFLEHOG_CHECKSUMS")"
# Verify checksum
echo "🔍 Verifying checksum..."
if ! grep "$(printf %q "$TRUFFLEHOG_BINARY")" "$(printf %q "$TRUFFLEHOG_CHECKSUMS")" | sha256sum -c -; then
echo "❌ Checksum verification failed!"
exit 1
fi
echo "✅ Checksum verified successfully"
# Extract and install
echo "📦 Extracting TruffleHog..."
tar -xzf "$(printf %q "$TRUFFLEHOG_BINARY")"
# Move to system path with proper permissions
sudo install -m 755 trufflehog /usr/local/bin/trufflehog
# Cleanup
cd /
rm -rf "$(printf %q "$TEMP_DIR")"
# Verify installation
echo "🔍 Verifying installation..."
trufflehog --version
echo "✅ TruffleHog installed successfully"
- name: Run TruffleHog Scan
continue-on-error: true
run: |
# Source the reusable functions
source /tmp/trufflehog_scan.sh
# Get the default branch and current commit with proper escaping
DEFAULT_BRANCH="${{ github.event.repository.default_branch }}"
CURRENT_COMMIT="${{ github.sha }}"
CURRENT_BRANCH="${{ github.ref_name }}"
BEFORE_COMMIT="${{ github.event.before }}"
# Determine scan range using the reusable function
SINCE_COMMIT=$(get_scan_range "$DEFAULT_BRANCH" "$CURRENT_BRANCH" "$BEFORE_COMMIT")
# Show scan context
if [ "$(printf %q "$CURRENT_BRANCH")" = "$(printf %q "$DEFAULT_BRANCH")" ]; then
if [ "$(printf %q "$BEFORE_COMMIT")" = "0000000000000000000000000000000000000000" ] || [ -z "$BEFORE_COMMIT" ]; then
echo "🔍 First commit on default branch"
else
printf "🔍 Push to default branch (%s)\n" "$(printf %q "$DEFAULT_BRANCH")"
fi
else
echo "🔍 Feature branch detected"
printf "📋 Scanning all commits in '%s' not in '%s'\n" "$(printf %q "$CURRENT_BRANCH")" "$(printf %q "$DEFAULT_BRANCH")"
fi
# Get ignore keys from workflow input
IGNORE_KEYS="${{ inputs.ignore_keys }}"
# Run TruffleHog scan using the reusable function
run_trufflehog_scan "trufflehog_output.json" "$SINCE_COMMIT" "$CURRENT_BRANCH" "$CURRENT_COMMIT" "$IGNORE_KEYS"
TRUFFLEHOG_EXIT_CODE=$?
# Filter out ignored keys if any are specified
if [ -n "$IGNORE_KEYS" ]; then
echo "🔍 Applying ignore keys filter..."
filter_ignored_keys "trufflehog_output.json" "filtered_output.json" "$IGNORE_KEYS"
mv "filtered_output.json" "trufflehog_output.json"
fi
# Exit with the same code TruffleHog used
exit $TRUFFLEHOG_EXIT_CODE
- name: Process TruffleHog Results
if: always()
run: |
# Filter out log messages, keep only actual findings (lines with SourceMetadata)
# Use grep with -q to avoid logging the actual content
if grep -q '"SourceMetadata"' trufflehog_output.json 2>/dev/null; then
# Create findings file but don't log the content
grep '"SourceMetadata"' trufflehog_output.json > findings.json 2>/dev/null || touch findings.json
TOTAL_COUNT=$(wc -l < findings.json)
printf "Found %d secret findings\n" "$TOTAL_COUNT"
echo "⚠️ SECRETS DETECTED - Details will be shown when the build fails"
else
echo "No secret findings detected"
touch findings.json
fi
# Clean up the raw output file immediately to prevent accidental logging
rm -f trufflehog_output.json
- name: Security Alert
if: always()
continue-on-error: true
run: |
# Check if we have findings directly
if [ -f findings.json ] && [ -s findings.json ]; then
TOTAL_COUNT=$(wc -l < findings.json)
echo "🚨 SECURITY BREACH DETECTED 🚨"
echo ""
printf "TruffleHog detected %d potential credential(s).\n" "$TOTAL_COUNT"
echo ""
echo "IMMEDIATE ACTION REQUIRED:"
echo "1. 🔄 ROTATE ALL DETECTED KEYS/SECRETS IMMEDIATELY"
echo "2. 🧹 Review the raw output above to identify which files contain secrets"
echo "3. 📝 Remove the secrets from your code and update configurations"
echo "4. 🔍 Look for 'file' fields in the JSON above for exact file locations"
echo "5. 🔍 Look for 'commit' fields to see which commits introduced the secrets"
echo ""
echo "💡 Detailed findings shown below with file paths and secret types:"
echo ""
# Always show key metadata (type, file, commit), conditionally show key values
echo "🔑 Secret findings detected:"
echo "================================"
if [ "${{ inputs.show_keys }}" == "true" ]; then
echo "Format: <detector-name> in <file> (Commit: <commit-hash>): <secret-value>"
# Display full information including key values
cat findings.json | jq -r '
if (.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit) then
.DetectorName + " in " + ((.SourceMetadata.Git.File // .SourceMetadata.Data.Filesystem.File // .SourceMetadata.Data.Git.File // .SourceMetadata.Data.Git.file // .file) //
(if .SourceMetadata.Git.Repository then "repository: " + .SourceMetadata.Git.Repository else null end) //
(if (.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit) then "commit: " + ((.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit)[0:8]) else null end) //
"unknown location") + " (Commit: " + (.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit) + "): " + .Raw
else
.DetectorName + " in " + ((.SourceMetadata.Git.File // .SourceMetadata.Data.Filesystem.File // .SourceMetadata.Data.Git.File // .SourceMetadata.Data.Git.file // .file) //
(if .SourceMetadata.Git.Repository then "repository: " + .SourceMetadata.Git.Repository else null end) //
(if (.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit) then "commit: " + ((.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit)[0:8]) else null end) //
"unknown location") + ": " + .Raw
end
' 2>/dev/null || echo "Could not parse findings for display"
else
echo "Format: <detector-name> in <file> (Commit: <commit-hash>)"
# Display metadata only, hide key values
cat findings.json | jq -r '
if (.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit) then
.DetectorName + " in " + ((.SourceMetadata.Git.File // .SourceMetadata.Data.Filesystem.File // .SourceMetadata.Data.Git.File // .SourceMetadata.Data.Git.file // .file) //
(if .SourceMetadata.Git.Repository then "repository: " + .SourceMetadata.Git.Repository else null end) //
(if (.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit) then "commit: " + ((.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit)[0:8]) else null end) //
"unknown location") + " (Commit: " + (.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit) + ")"
else
.DetectorName + " in " + ((.SourceMetadata.Git.File // .SourceMetadata.Data.Filesystem.File // .SourceMetadata.Data.Git.File // .SourceMetadata.Data.Git.file // .file) //
(if .SourceMetadata.Git.Repository then "repository: " + .SourceMetadata.Git.Repository else null end) //
(if (.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit) then "commit: " + ((.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit)[0:8]) else null end) //
"unknown location")
end
' 2>/dev/null || echo "Could not parse findings for display"
echo ""
echo "ℹ️ Key values are hidden. Set show_keys: true to display them."
fi
echo "================================"
echo ""
echo "⚠️ Once committed to Git, consider these credentials compromised!"
else
echo "✅ No secrets detected"
fi
echo ""
exit 1
- name: Send Security Alert to Webhook
if: always() && inputs.notify_webhook
continue-on-error: true
env:
# Webhook URL from workflow secret
SECURITY_WEBHOOK_URL: ${{ secrets.webhook_url }}
# Escape GitHub context variables
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_REF_NAME: ${{ github.ref_name }}
GITHUB_SHA: ${{ github.sha }}
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_RUN_ID: ${{ github.run_id }}
run: |
# Check if we have findings directly
if [ -f findings.json ] && [ -s findings.json ]; then
# Validate webhook URL format
if ! echo "$SECURITY_WEBHOOK_URL" | grep -qE '^https?://'; then
echo "❌ Invalid webhook URL format: must start with http:// or https://"
echo "ℹ️ Skipping webhook notification due to invalid URL format"
exit 0
fi
echo "📡 Sending security alert to webhook..."
# Validate webhook URL format (already validated above, but double-check)
if ! echo "$SECURITY_WEBHOOK_URL" | grep -qE '^https?://'; then
echo "❌ Invalid webhook URL format: must start with http:// or https://"
exit 0
fi
TOTAL_COUNT=$(wc -l < findings.json)
# Create sanitized findings by redacting the Raw field
echo "Sanitizing findings to redact sensitive information..."
cat findings.json | jq -c '. + {"Raw": "***REDACTED***", "RawV2": "***REDACTED***"}' > sanitized_findings.json
# Create payload with repository context and sanitized findings
# Use jq to properly escape all variables in JSON
jq -n \
--arg repository "$(printf %q "$GITHUB_REPOSITORY")" \
--arg branch "$(printf %q "$GITHUB_REF_NAME")" \
--arg commit "$(printf %q "$GITHUB_SHA")" \
--arg actor "$(printf %q "$GITHUB_ACTOR")" \
--arg workflow_run_url "$(printf %q "$GITHUB_SERVER_URL")/$(printf %q "$GITHUB_REPOSITORY")/actions/runs/$(printf %q "$GITHUB_RUN_ID")" \
--argjson secrets_count "$TOTAL_COUNT" \
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--slurpfile findings sanitized_findings.json \
'{
repository: $repository,
branch: $branch,
commit: $commit,
actor: $actor,
workflow_run_url: $workflow_run_url,
secrets_count: $secrets_count,
timestamp: $timestamp,
findings: $findings
}' > webhook_payload.json
# Send to webhook with improved error handling and security headers
echo "🌐 Attempting webhook request..."
if curl -X POST \
-H "Content-Type: application/json" \
-H "User-Agent: GitHub-Actions-Security-Scanner/1.0" \
-d @webhook_payload.json \
"$SECURITY_WEBHOOK_URL" \
--max-time 30 \
--retry 2 \
--retry-delay 5 \
--fail-with-body \
--silent \
--show-error; then
echo "✅ Security alert sent successfully"
else
echo "❌ Failed to send security alert to webhook"
echo "🔍 Curl exit code: $?"
echo "🔍 Please verify the webhook URL is correct and accessible"
fi
# Clean up sensitive payload file
rm -f webhook_payload.json sanitized_findings.json findings.json
else
echo "No secrets detected - skipping webhook notification"
# Clean up any remaining files even if no secrets found
rm -f findings.json 2>/dev/null || true
fi
- name: Handle Webhook Disabled
if: always() && !inputs.notify_webhook
run: |
echo "ℹ️ Webhook notifications are disabled. Set notify_webhook: true to enable them."
# Clean up any remaining files even if no secrets found
rm -f findings.json 2>/dev/null || true
- name: Fail Build After Notification
if: always()
run: |
# Source the reusable functions
source /tmp/trufflehog_scan.sh
# Get the default branch and current commit with proper escaping
DEFAULT_BRANCH="${{ github.event.repository.default_branch }}"
CURRENT_COMMIT="${{ github.sha }}"
CURRENT_BRANCH="${{ github.ref_name }}"
BEFORE_COMMIT="${{ github.event.before }}"
# Determine scan range using the reusable function
SINCE_COMMIT=$(get_scan_range "$DEFAULT_BRANCH" "$CURRENT_BRANCH" "$BEFORE_COMMIT")
echo "🔍 Running final TruffleHog verification scan..."
# Get ignore keys from workflow input
IGNORE_KEYS="${{ inputs.ignore_keys }}"
# Run TruffleHog for final verification using the reusable function
run_trufflehog_scan "final_verification.json" "$SINCE_COMMIT" "$CURRENT_BRANCH" "$CURRENT_COMMIT" "$IGNORE_KEYS"
TRUFFLEHOG_EXIT_CODE=$?
printf "📊 Final verification scan completed with exit code: %d\n" "$TRUFFLEHOG_EXIT_CODE"
# Filter out ignored keys if any are specified
if [ -n "$IGNORE_KEYS" ]; then
echo "🔍 Applying ignore keys filter to final verification..."
filter_ignored_keys "final_verification.json" "filtered_final.json" "$IGNORE_KEYS"
mv "filtered_final.json" "final_verification.json"
fi
# Check if we have findings (without logging the actual content)
if [ -s final_verification.json ]; then
# Filter out log messages, keep only actual findings
if grep -q '"SourceMetadata"' final_verification.json 2>/dev/null; then
grep '"SourceMetadata"' final_verification.json > final_findings.json 2>/dev/null || touch final_findings.json
TOTAL_COUNT=$(wc -l < final_findings.json)
echo "🚨 FINAL VERIFICATION: SECRETS DETECTED 🚨"
printf "TruffleHog found %d potential credential(s) in final verification.\n" "$TOTAL_COUNT"
echo ""
# Show detailed findings with key metadata (type, file, commit), conditionally show key values
echo "🔑 Secret findings detected:"
echo "================================"
if [ "${{ inputs.show_keys }}" == "true" ]; then
echo "Format: <detector-name> in <file> (Commit: <commit-hash>): <secret-value>"
# Display full information including key values
cat final_findings.json | jq -r '
if (.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit) then
.DetectorName + " in " + ((.SourceMetadata.Git.File // .SourceMetadata.Data.Filesystem.File // .SourceMetadata.Data.Git.File // .SourceMetadata.Data.Git.file // .file) //
(if .SourceMetadata.Git.Repository then "repository: " + .SourceMetadata.Git.Repository else null end) //
(if (.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit) then "commit: " + ((.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit)[0:8]) else null end) //
"unknown location") + " (Commit: " + (.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit) + "): " + .Raw
else
.DetectorName + " in " + ((.SourceMetadata.Git.File // .SourceMetadata.Data.Filesystem.File // .SourceMetadata.Data.Git.File // .SourceMetadata.Data.Git.file // .file) //
(if .SourceMetadata.Git.Repository then "repository: " + .SourceMetadata.Git.Repository else null end) //
(if (.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit) then "commit: " + ((.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit)[0:8]) else null end) //
"unknown location") + ": " + .Raw
end
' 2>/dev/null || echo "Could not parse findings for display"
else
echo "Format: <detector-name> in <file> (Commit: <commit-hash>)"
# Display metadata only, hide key values
cat final_findings.json | jq -r '
if (.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit) then
.DetectorName + " in " + ((.SourceMetadata.Git.File // .SourceMetadata.Data.Filesystem.File // .SourceMetadata.Data.Git.File // .SourceMetadata.Data.Git.file // .file) //
(if .SourceMetadata.Git.Repository then "repository: " + .SourceMetadata.Git.Repository else null end) //
(if (.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit) then "commit: " + ((.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit)[0:8]) else null end) //
"unknown location") + " (Commit: " + (.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit) + ")"
else
.DetectorName + " in " + ((.SourceMetadata.Git.File // .SourceMetadata.Data.Filesystem.File // .SourceMetadata.Data.Git.File // .SourceMetadata.Data.Git.file // .file) //
(if .SourceMetadata.Git.Repository then "repository: " + .SourceMetadata.Git.Repository else null end) //
(if (.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit) then "commit: " + ((.SourceMetadata.Git.Commit // .SourceMetadata.Data.Git.commit)[0:8]) else null end) //
"unknown location")
end
' 2>/dev/null || echo "Could not parse findings for display"
echo ""
echo "ℹ️ Key values are hidden. Set show_keys: true to display them."
fi
echo "================================"
echo ""
echo "🚨 FAILING BUILD DUE TO DETECTED SECRETS 🚨"
# Clean up sensitive files before exiting
rm -f final_verification.json final_findings.json 2>/dev/null || true
exit 1
fi
fi
echo "✅ Final verification: No secrets detected - build can proceed"
# Clean up verification files
rm -f final_verification.json final_findings.json 2>/dev/null || true
# Final cleanup of any remaining sensitive files
rm -f trufflehog_output.json findings.json sanitized_findings.json webhook_payload.json 2>/dev/null || true
security-gate:
name: Security Gate
runs-on: ubuntu-latest
needs: [validate-inputs, secret-scan]
if: always()
# Set minimal permissions for this job
permissions:
contents: read
steps:
- name: Check secret scan result
env:
SECRET_SCAN_RESULT: ${{ needs.secret-scan.result }}
run: |
# Check if the secret-scan job failed (which means secrets were found)
if [ "$(printf %q "$SECRET_SCAN_RESULT")" == "failure" ]; then
echo "❌ Build blocked due to detected secrets"
exit 1
else
echo "✅ No secrets detected - Build is secure"
fi