diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml index db86f47..0916f8c 100644 --- a/.github/workflows/secret-scan.yml +++ b/.github/workflows/secret-scan.yml @@ -43,6 +43,9 @@ jobs: - name: Scan full history for credentials env: SECRET_SCAN_SLACK_BOT_TOKEN: ${{ secrets.SECRET_SCAN_SLACK_BOT_TOKEN }} + PR_URL: ${{ github.event.pull_request.html_url }} + PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} run: | set +e gitleaks git --redact --verbose --exit-code 2 . \ @@ -78,13 +81,35 @@ jobs: -f 'output[title]'="${count} credential finding(s) in git history" \ -f 'output[summary]'="Rotate the credential(s) NOW (pushed = compromised; deleting the line does not help). Details and remediation steps: see the step summary of the Secret scan workflow run. This check is informational and does not block merging." \ || echo "could not create check run (read-only token?)" - if [ -n "$SECRET_SCAN_SLACK_BOT_TOKEN" ]; then + # Slack dedup: on PRs, notify only when the PR's own + # commits contain findings. Historical findings alerted when they + # were introduced and stay visible in the check run + step summary; + # re-posting them on every push drowned the channel. + notify=/tmp/credentials.json + scope="in git history" + if [ -n "$PR_BASE_SHA" ]; then + set +e + gitleaks git --redact --exit-code 2 . \ + --log-opts "${PR_BASE_SHA}..${PR_HEAD_SHA}" \ + --report-format json --report-path /tmp/credentials-new.json + set -e + notify=/tmp/credentials-new.json + scope="new in this PR, ${count} total in history" + fi + notify_count=$(jq length "$notify" 2>/dev/null || echo 0) + if [ -n "$SECRET_SCAN_SLACK_BOT_TOKEN" ] && [ "$notify_count" -gt 0 ]; then + sample=$(jq -r '.[:5][] | "> \(.RuleID) \(.File):\(.StartLine)"' "$notify") + txt=":rotating_light: *Credential leak detected* in \`${GITHUB_REPOSITORY}\` (${notify_count} ${scope}). Rotate immediately. + ${sample} + ${PR_URL:+PR: ${PR_URL} + }Run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" curl -sf -X POST https://slack.com/api/chat.postMessage \ -H "Authorization: Bearer $SECRET_SCAN_SLACK_BOT_TOKEN" \ -H "Content-Type: application/json; charset=utf-8" \ - -d "$(jq -n --arg ch "$SLACK_CHANNEL_ID" \ - --arg txt ":rotating_light: *Credential leak detected* in \`${GITHUB_REPOSITORY}\` (${count} finding(s) in git history). Rotate immediately. Run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \ + -d "$(jq -n --arg ch "$SLACK_CHANNEL_ID" --arg txt "$txt" \ '{channel: $ch, text: $txt}')" | jq -r '.ok // "post failed"' + else + echo "Slack: skipped (no token, or findings are historical only)" fi elif [ "$status" -ne 0 ]; then exit "$status" @@ -93,6 +118,9 @@ jobs: internal-refs: name: Internal references (warn-only) runs-on: ubuntu-latest + # Per-repo opt-out: set the repo Actions variable + # SECRET_SCAN_SKIP_INTERNAL_REFS=true to skip this job. + if: ${{ vars.SECRET_SCAN_SKIP_INTERNAL_REFS != 'true' }} steps: - uses: actions/checkout@v4 with: @@ -122,6 +150,9 @@ jobs: if: ${{ steps.config.outputs.skip != 'true' }} env: SECRET_SCAN_SLACK_BOT_TOKEN: ${{ secrets.SECRET_SCAN_SLACK_BOT_TOKEN }} + PR_URL: ${{ github.event.pull_request.html_url }} + PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} run: | set +e gitleaks git --config /tmp/gitleaks-internal.toml --verbose --exit-code 2 . \ @@ -147,13 +178,33 @@ jobs: -f 'output[title]'="${count} internal Astera reference(s) in git history" \ -f 'output[summary]'="Fine while this repo is private; must be resolved before making it public. Details: see the step summary of the Secret scan workflow run. This check is informational and does not block merging." \ || echo "could not create check run (read-only token?)" - if [ -n "$SECRET_SCAN_SLACK_BOT_TOKEN" ]; then + # Slack dedup: same rule as the credentials job: on PRs, + # notify only for findings introduced by the PR's own commits. + notify=/tmp/internal-refs.json + scope="in git history" + if [ -n "$PR_BASE_SHA" ]; then + set +e + gitleaks git --config /tmp/gitleaks-internal.toml --exit-code 2 . \ + --log-opts "${PR_BASE_SHA}..${PR_HEAD_SHA}" \ + --report-format json --report-path /tmp/internal-refs-new.json + set -e + notify=/tmp/internal-refs-new.json + scope="new in this PR, ${count} total in history" + fi + notify_count=$(jq length "$notify" 2>/dev/null || echo 0) + if [ -n "$SECRET_SCAN_SLACK_BOT_TOKEN" ] && [ "$notify_count" -gt 0 ]; then + sample=$(jq -r '.[:5][] | "> \(.RuleID) \(.File):\(.StartLine)"' "$notify") + txt=":warning: Internal Astera references in \`${GITHUB_REPOSITORY}\` (${notify_count} ${scope}). Fine while private; must be resolved before the repo goes public. + ${sample} + ${PR_URL:+PR: ${PR_URL} + }Run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" curl -sf -X POST https://slack.com/api/chat.postMessage \ -H "Authorization: Bearer $SECRET_SCAN_SLACK_BOT_TOKEN" \ -H "Content-Type: application/json; charset=utf-8" \ - -d "$(jq -n --arg ch "$SLACK_CHANNEL_ID" \ - --arg txt ":warning: Internal Astera references in \`${GITHUB_REPOSITORY}\` (${count} in git history). Fine while private; must be resolved before the repo goes public. Run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \ + -d "$(jq -n --arg ch "$SLACK_CHANNEL_ID" --arg txt "$txt" \ '{channel: $ch, text: $txt}')" | jq -r '.ok // "post failed"' + else + echo "Slack: skipped (no token, or findings are historical only)" fi elif [ "$status" -ne 0 ]; then echo "gitleaks failed with status $status" >&2