From ed76d410b3a634e2f32384864ab7e1f68cc8a415 Mon Sep 17 00:00:00 2001 From: geoffg-sentry <165922362+geoffg-sentry@users.noreply.github.com> Date: Fri, 11 Sep 2026 10:59:02 -0400 Subject: [PATCH] fix(secret-scan): softer failures for download issues --- .github/workflows/secret-scan.yml | 82 ++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 8 deletions(-) diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml index 1300e88..cdbfa80 100644 --- a/.github/workflows/secret-scan.yml +++ b/.github/workflows/secret-scan.yml @@ -13,10 +13,14 @@ jobs: - name: Checkout Code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Cosign + id: cosign + continue-on-error: true # v4 of the action install v3 of the CLI. v4 of the CLI will deprecate some features so be aware. uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 - name: Pin Trufflehog to a known good release id: trufflehog_release + if: ${{ steps.cosign.outcome == 'success' }} + continue-on-error: true shell: bash env: GH_TOKEN: ${{ github.token }} @@ -35,7 +39,7 @@ jobs: done if [[ -z "$LATEST_TAG_NAME" ]]; then - echo "::error::No usable TruffleHog release found" + echo "::warning::No usable TruffleHog release found" exit 1 fi @@ -43,6 +47,9 @@ jobs: echo "latest_tag_name=$LATEST_TAG_NAME" >> "$GITHUB_OUTPUT" echo "latest_release=${LATEST_TAG_NAME#v}" >> "$GITHUB_OUTPUT" - name: Download and verify TruffleHog release + id: download + if: ${{ steps.trufflehog_release.outcome == 'success' }} + continue-on-error: true run: | curl -fsSL --retry 8 --retry-max-time 120 --retry-all-errors -w '%{url_effective} %{http_code}\n' -O https://github.com/trufflesecurity/trufflehog/releases/download/${{ steps.trufflehog_release.outputs.latest_tag_name }}/trufflehog_${{ steps.trufflehog_release.outputs.latest_release }}_checksums.txt curl -fsSL --retry 8 --retry-max-time 120 --retry-all-errors -w '%{url_effective} %{http_code}\n' -O https://github.com/trufflesecurity/trufflehog/releases/download/${{ steps.trufflehog_release.outputs.latest_tag_name }}/trufflehog_${{ steps.trufflehog_release.outputs.latest_release }}_checksums.txt.pem @@ -57,28 +64,61 @@ jobs: sha256sum --ignore-missing -c trufflehog_${{ steps.trufflehog_release.outputs.latest_release }}_checksums.txt - name: Extract TruffleHog + id: extract + if: ${{ steps.download.outcome == 'success' }} + continue-on-error: true run: | tar xzf trufflehog_${{ steps.trufflehog_release.outputs.latest_release }}_linux_amd64.tar.gz -C /usr/local/bin chmod +x /usr/local/bin/trufflehog - name: Run TruffleHog scan - continue-on-error: true id: scan + if: ${{ steps.extract.outcome == 'success' }} + continue-on-error: true run: | + set +e if [ -e .secret_scan_ignore ]; then - trufflehog git file://. --only-verified --github-actions --fail --exclude-paths=.secret_scan_ignore --exclude-detectors="datadogtoken,lob" + trufflehog git file://. --only-verified --json --fail --exclude-paths=.secret_scan_ignore --exclude-detectors="datadogtoken,lob" > "$RUNNER_TEMP/findings.jsonl" else - trufflehog git file://. --only-verified --github-actions --fail --exclude-detectors="datadogtoken,lob" + trufflehog git file://. --only-verified --json --fail --exclude-detectors="datadogtoken,lob" > "$RUNNER_TEMP/findings.jsonl" fi + exit_code=$? + set -e + + findings=$(grep -c '[^[:space:]]' "$RUNNER_TEMP/findings.jsonl" || true) + echo "exit_code=$exit_code" >> "$GITHUB_OUTPUT" + echo "findings=$findings" >> "$GITHUB_OUTPUT" + + # --json takes the stdout --github-actions used, so re-emit annotations here. + jq -r '"::error file=\(.SourceMetadata.Data.Git.file // "unknown"),line=\(.SourceMetadata.Data.Git.line // 1)::Verified \(.DetectorName) secret detected"' "$RUNNER_TEMP/findings.jsonl" + + exit "$exit_code" + - name: Report scan skipped + if: ${{ !cancelled() && steps.scan.outcome == 'skipped' }} + env: + COSIGN_OUTCOME: ${{ steps.cosign.outcome }} + RELEASE_OUTCOME: ${{ steps.trufflehog_release.outcome }} + DOWNLOAD_OUTCOME: ${{ steps.download.outcome }} + EXTRACT_OUTCOME: ${{ steps.extract.outcome }} + run: | + echo "::warning::TruffleHog unavailable, this change was not scanned: cosign=$COSIGN_OUTCOME release=$RELEASE_OUTCOME download=$DOWNLOAD_OUTCOME extract=$EXTRACT_OUTCOME" - name: Send Alert to SIEM id: alert + if: ${{ !cancelled() }} env: SIEM_WEBHOOK_URL: ${{ vars.SECRET_SCAN_SIEM_WEBHOOK_URL }} SCAN_OUTCOME: ${{ steps.scan.outcome }} + SCAN_EXIT_CODE: ${{ steps.scan.outputs.exit_code }} + SCAN_FINDINGS: ${{ steps.scan.outputs.findings }} + COSIGN_OUTCOME: ${{ steps.cosign.outcome }} + RELEASE_OUTCOME: ${{ steps.trufflehog_release.outcome }} + DOWNLOAD_OUTCOME: ${{ steps.download.outcome }} + EXTRACT_OUTCOME: ${{ steps.extract.outcome }} REPO: ${{ github.repository }} PR_NUMBER: ${{ github.event.pull_request.number }} PR_CREATED_AT: ${{ github.event.pull_request.created_at }} PR_ACTOR: ${{ github.event.pull_request.user.login }} EVENT_ACTOR: ${{ github.actor }} + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | if [[ -z "$SIEM_WEBHOOK_URL" ]]; then exit 0 @@ -94,19 +134,45 @@ jobs: pull_request="" fi + findings="${SCAN_FINDINGS:-0}" + failed_step="" + if [[ "$SCAN_OUTCOME" == "success" ]]; then + status="success" + elif [[ "$SCAN_EXIT_CODE" == "183" || "$findings" -gt 0 ]]; then # 183: verified secret + status="failure" + else + status="cancelled" # no verdict reached, so not a detection + # Listed in execution order: the first stage that did not succeed is the one that broke. + for stage in "install_cosign:$COSIGN_OUTCOME" \ + "pin_trufflehog_release:$RELEASE_OUTCOME" \ + "download_and_verify_trufflehog:$DOWNLOAD_OUTCOME" \ + "extract_trufflehog:$EXTRACT_OUTCOME" \ + "run_trufflehog_scan:$SCAN_OUTCOME"; do + if [[ "${stage#*:}" != "success" ]]; then + failed_step="${stage%%:*}" + break + fi + done + fi + jq -n \ --arg event "github_secret_scanning" \ - --arg status "$SCAN_OUTCOME" \ + --arg status "$status" \ + --arg failedStep "$failed_step" \ + --arg exitCode "$SCAN_EXIT_CODE" \ + --arg findings "$findings" \ --arg createdAt "$created_at" \ --arg repo "$REPO" \ --arg pull_request "$pull_request" \ --arg actor "$actor" \ - '{event: $event, status: $status, createdAt: $createdAt, repo: $repo, pull_request: $pull_request, actor: $actor}' \ + --arg runUrl "$RUN_URL" \ + '{event: $event, status: $status, failedStep: $failedStep, exitCode: $exitCode, findings: $findings, createdAt: $createdAt, repo: $repo, pull_request: $pull_request, actor: $actor, runUrl: $runUrl}' \ | curl --fail --silent --show-error \ -H "Content-Type: application/json" \ --data @- \ "$SIEM_WEBHOOK_URL" \ || echo "::warning::SIEM alert failed (non-blocking)" - - name: Fail workflow if secret detected - if: ${{ !cancelled() && steps.scan.outcome != 'success' }} + # A skipped scan does not block, only one that ran and failed does. + - name: Fail workflow if scan did not pass + if: ${{ !cancelled() && (steps.scan.outcome == 'failure' || (steps.scan.outputs.findings != '' && steps.scan.outputs.findings != '0')) }} run: exit 1