Add debug step to verify JSON file creation #4
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: Reusable TruffleHog Secret Scan | ||
|
Check failure on line 1 in .github/workflows/reusable-trufflehog.yml
|
||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| fail-on-verified: | ||
| description: "Fail workflow on verified secrets" | ||
| required: false | ||
| default: "true" | ||
| type: string | ||
| fail-on-unverified: | ||
| description: "Fail workflow on unverified secrets" | ||
| required: false | ||
| default: "false" | ||
| type: string | ||
| runs-on: | ||
| description: "The runner to use for the job" | ||
| required: false | ||
| default: "ubuntu-latest" | ||
| type: string | ||
| permissions: | ||
| contents: read | ||
| pull-requests: write | ||
| env: | ||
| # renovate: datasource=github-releases depName=trufflesecurity/trufflehog | ||
| TRUFFLEHOG_VERSION: 3.89.2 | ||
| jobs: | ||
| trufflehog-scan: | ||
| runs-on: ${{ inputs.runs-on }} | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 | ||
| with: | ||
| fetch-depth: 100 | ||
| persist-credentials: false | ||
| - name: Install TruffleHog | ||
| run: | | ||
| # Download binary directly from GitHub releases for supply chain security | ||
| VERSION="v${{ env.TRUFFLEHOG_VERSION }}" | ||
| # Auto-detect architecture for cross-platform support | ||
| if [[ "$(uname -m)" == "aarch64" ]]; then | ||
| ARCH="linux_arm64" | ||
| else | ||
| ARCH="linux_amd64" | ||
| fi | ||
| BINARY_URL="https://github.com/trufflesecurity/trufflehog/releases/download/${VERSION}/trufflehog_${VERSION#v}_${ARCH}.tar.gz" | ||
| curl -sSfL "${BINARY_URL}" | tar -xz -C /tmp | ||
| sudo mv /tmp/trufflehog /usr/local/bin/trufflehog | ||
| sudo chmod +x /usr/local/bin/trufflehog | ||
| trufflehog --version | ||
| - name: Scan for secrets | ||
| id: scan | ||
| run: | | ||
| set +e | ||
| echo "[]" > results.json | ||
| if [[ "${{ github.event_name }}" == "pull_request" ]]; then | ||
| # PR: Scan only changed files | ||
| echo "Scanning changed files in PR..." | ||
| git diff --name-only ${{ github.event.pull_request.base.sha }}..${{ github.sha }} > changed-files.txt | ||
| if [[ -s changed-files.txt ]]; then | ||
| while IFS= read -r file; do | ||
| if [[ -f "${file}" ]]; then | ||
| echo "Scanning: ${file}" | ||
| trufflehog filesystem "${file}" --json --no-update --results=verified,unverified >> results.ndjson || true | ||
| fi | ||
| done < changed-files.txt | ||
| else | ||
| echo "No files changed" | ||
| fi | ||
| else | ||
| # Push to main: Scan current filesystem | ||
| echo "Scanning current filesystem..." | ||
| trufflehog filesystem . --json --no-update --results=verified,unverified > results.ndjson || true | ||
| fi | ||
| # Process results | ||
| if [[ -s results.ndjson ]]; then | ||
| grep -v '^$' results.ndjson | jq -s '.' > results.json 2>/dev/null || echo "[]" > results.json | ||
| fi | ||
| # Count secrets | ||
| if jq empty results.json 2>/dev/null; then | ||
| VERIFIED=$(jq '[.[] | select(.Verified==true)] | length' results.json 2>/dev/null || echo "0") | ||
| UNVERIFIED=$(jq '[.[] | select(.Verified==false)] | length' results.json 2>/dev/null || echo "0") | ||
| else | ||
| VERIFIED=0 | ||
| UNVERIFIED=0 | ||
| echo "[]" > results.json | ||
| fi | ||
| TOTAL=$((VERIFIED+UNVERIFIED)) | ||
| echo "Found ${TOTAL} potential secrets (${VERIFIED} verified, ${UNVERIFIED} unverified)" | ||
| echo "verified=${VERIFIED}" >> "${GITHUB_OUTPUT}" | ||
| echo "unverified=${UNVERIFIED}" >> "${GITHUB_OUTPUT}" | ||
| echo "total=${TOTAL}" >> "${GITHUB_OUTPUT}" | ||
| - name: Generate PR comment | ||
| if: ${{ !cancelled() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository }} | ||
| id: comment-body | ||
| run: | | ||
| VERIFIED=${{ steps.scan.outputs.verified }} | ||
| UNVERIFIED=${{ steps.scan.outputs.unverified }} | ||
| TOTAL=$((VERIFIED+UNVERIFIED)) | ||
| if [[ $TOTAL -eq 0 ]]; then | ||
| { | ||
| echo 'body<<EOF' | ||
| echo '**TruffleHog Scan Results**' | ||
| echo '' | ||
| echo 'No secrets detected in this PR.' | ||
| echo 'EOF' | ||
| } >> "$GITHUB_OUTPUT" | ||
| else | ||
| # Generate findings list | ||
| FINDINGS=$(jq -r '.[] | | ||
| "- " + | ||
| (if .Verified then "**VERIFIED SECRET**" else "**Possible secret**" 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") | ||
| ACTION_TEXT="" | ||
| if [[ $VERIFIED -gt 0 ]]; then | ||
| ACTION_TEXT="**ACTION REQUIRED:** Rotate verified credentials immediately." | ||
| else | ||
| ACTION_TEXT="**Review:** Check if unverified secrets are false positives." | ||
| fi | ||
| { | ||
| echo 'body<<EOF' | ||
| echo "**TruffleHog Scan Results**" | ||
| echo '' | ||
| echo "**Summary:** Found ${TOTAL} potential secrets (${VERIFIED} verified, ${UNVERIFIED} unverified)" | ||
| echo '' | ||
| echo "${FINDINGS}" | ||
| echo '' | ||
| echo "${ACTION_TEXT}" | ||
| echo 'EOF' | ||
| } >> "$GITHUB_OUTPUT" | ||
| fi | ||
| - name: Post PR comment | ||
| if: ${{ !cancelled() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository }} | ||
| uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2 | ||
| with: | ||
| # We retain the final trufflehog-secret-scan-comment marker in case we want to chang the action to another one. | ||
| message: | | ||
| ${{ steps.comment-body.outputs.body }} | ||
| <!-- trufflehog-secret-scan-comment --> | ||
| message-id: trufflehog-secret-scan-comment | ||
| - name: Create scan report | ||
| env: | ||
| GITHUB_REF_NAME: ${{ github.ref_name }} | ||
| run: | | ||
| { | ||
| echo "TruffleHog Scan Report" | ||
| echo "=====================" | ||
| echo "Date: $(date)" | ||
| echo "Repository: ${{ github.repository }}" | ||
| echo "Branch: ${GITHUB_REF_NAME}" | ||
| echo "Commit: ${{ github.sha }}" | ||
| echo "" | ||
| echo "Summary:" | ||
| echo "- Total secrets: ${{ steps.scan.outputs.total }}" | ||
| echo "- Verified: ${{ steps.scan.outputs.verified }}" | ||
| echo "- Unverified: ${{ steps.scan.outputs.unverified }}" | ||
| echo "" | ||
| echo "Detailed Results:" | ||
| if [[ -f "results.json" && -s "results.json" ]] && jq empty results.json 2>/dev/null; then | ||
| 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" | ||
| else | ||
| echo "No secrets detected" | ||
| fi | ||
| } > trufflehog_scan.txt | ||
| - name: Verify JSON file exists | ||
| if: always() | ||
| run: | | ||
| if [[ -f "results.json" ]]; then | ||
| echo "✅ results.json exists" | ||
| echo "File size: $(wc -c < results.json) bytes" | ||
| echo "JSON validity check:" | ||
| if jq empty results.json 2>/dev/null; then | ||
| echo "✅ Valid JSON" | ||
| echo "Number of findings: $(jq '. | length' results.json)" | ||
| else | ||
| echo "❌ Invalid JSON" | ||
| fi | ||
| else | ||
| echo "❌ results.json does not exist" | ||
| fi | ||
| - name: Upload scan results | ||
| uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 | ||
| if: always() | ||
| with: | ||
| name: trufflehog_scan | ||
| path: | | ||
| trufflehog_scan.txt | ||
| results.json | ||
| retention-days: 30 | ||
| - name: Setup Python for Grafana integration | ||
| if: ${{ !cancelled() && (secrets.LOKI_URL != '' || secrets.PROMETHEUS_PUSHGATEWAY_URL != '') }} | ||
| uses: actions/setup-python@0b6a380b5a7827e48e69b2e0e596c5c8c2b0e0b0 # v5.1.0 | ||
| with: | ||
| python-version: '3.x' | ||
| - name: Install Python dependencies | ||
| if: ${{ !cancelled() && (secrets.LOKI_URL != '' || secrets.PROMETHEUS_PUSHGATEWAY_URL != '') }} | ||
| run: | | ||
| pip install requests | ||
| - name: Send findings to Loki | ||
| if: ${{ !cancelled() && secrets.LOKI_URL != '' }} | ||
| continue-on-error: true | ||
| env: | ||
| LOKI_URL: ${{ secrets.LOKI_URL }} | ||
| LOKI_USERNAME: ${{ secrets.LOKI_USERNAME }} | ||
| LOKI_PASSWORD: ${{ secrets.LOKI_PASSWORD }} | ||
| REPOSITORY: ${{ github.repository }} | ||
| COMMIT_SHA: ${{ github.sha }} | ||
| BRANCH: ${{ github.ref_name }} | ||
| TRUFFLEHOG_RESULTS_FILE: results.json | ||
| run: | | ||
| python trufflehog/send-to-loki.py | ||
| - name: Send metrics to Prometheus | ||
| if: ${{ !cancelled() && secrets.PROMETHEUS_PUSHGATEWAY_URL != '' }} | ||
| continue-on-error: true | ||
| env: | ||
| PROMETHEUS_PUSHGATEWAY_URL: ${{ secrets.PROMETHEUS_PUSHGATEWAY_URL }} | ||
| REPOSITORY: ${{ github.repository }} | ||
| COMMIT_SHA: ${{ github.sha }} | ||
| BRANCH: ${{ github.ref_name }} | ||
| TRUFFLEHOG_RESULTS_FILE: results.json | ||
| run: | | ||
| python trufflehog/send-to-prometheus.py | ||
| - name: Check failure policy | ||
| env: | ||
| FAIL_ON_VERIFIED: ${{ inputs.fail-on-verified }} | ||
| FAIL_ON_UNVERIFIED: ${{ inputs.fail-on-unverified }} | ||
| VERIFIED_COUNT: ${{ steps.scan.outputs.verified }} | ||
| UNVERIFIED_COUNT: ${{ steps.scan.outputs.unverified }} | ||
| run: | | ||
| SHOULD_FAIL=false | ||
| if [[ "${FAIL_ON_VERIFIED}" == "true" && "${VERIFIED_COUNT}" != "0" ]]; then | ||
| SHOULD_FAIL=true | ||
| fi | ||
| if [[ "${FAIL_ON_UNVERIFIED}" == "true" && "${UNVERIFIED_COUNT}" != "0" ]]; then | ||
| SHOULD_FAIL=true | ||
| fi | ||
| if [[ "${SHOULD_FAIL}" == "true" ]]; then | ||
| echo "Workflow failed due to secrets found (verified: ${VERIFIED_COUNT}, unverified: ${UNVERIFIED_COUNT})" | ||
| exit 1 | ||
| else | ||
| echo "No action needed - secrets within configured thresholds" | ||
| fi | ||