Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/SECRET_DETECTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Secret detection and prevention control

Owner: Security Engineering
Control: CI secret detection, with native GitHub push protection tracked separately
Review cadence: at least annually and after a material repository or scanner change

## Required control

The `Secret detection` workflow scans the complete reachable Git history on every push and pull request and once each week. A finding fails the workflow, so a change cannot satisfy a branch rule that requires this check until the finding is treated. The scanner binary and download checksum are pinned. Checkout fetches only the triggering commit and its complete reachable ancestry with the workflow's read-only token; it does not initialize submodules.

This workflow is a **detective CI control**, not native GitHub push protection. Repository administrators must separately record whether GitHub secret scanning and push protection are enabled. CI coverage must never be reported as native push prevention.

## Historical findings

After this workflow reaches the repository's protected base branch, run it manually once and retain the run URL, repository SHA, UTC time, and result. Triage every finding without copying a secret value into logs, issues, or evidence:

1. Confirm whether it is a real credential, a synthetic test value, or a false positive.
2. For a real credential, follow the incident-response process and obtain authorization before revocation, rotation, or history rewriting.
3. Link the treatment record to the scanner run and record owner, disposition, and completion time.
4. Re-run the full-history scan and retain the passing result. A history rewrite is destructive and requires separate explicit approval.

An initial failing historical scan proves detection is operating; it does not establish remediation or compliance.

## Exceptions

Exceptions must be narrow, time-bounded, and approved by both the repository owner and Security Engineering. The linked tracking record must contain the detector/fingerprint, justification, compensating control, owner, approval, and an expiry no later than 90 days. Add only the approved fingerprint to `.gitleaksignore`. Broad path, regex, detector, or repository-wide allowlists are prohibited unless the same approval record explicitly authorizes them. Remove an exception when it expires and re-run the workflow.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

INFO: The exception process references .gitleaksignore for approved fingerprints, but the file does not exist in the repository. This is expected since no exceptions have been approved yet, but the absence should be noted during the initial historical scan triage.

Suggestion: Create an empty .gitleaksignore with a header comment explaining the approval process, or add a note in the doc that the file will be created when the first exception is approved.


## Safe validation

Use an invalid synthetic credential in a temporary branch; never use a live credential. Verify that the workflow fails and redacts the value, remove the fixture, and verify that the same workflow passes. Retain both run URLs and the tested SHA. Native push-protection validation is a separate GitHub-administrator test and must identify itself as such.
55 changes: 55 additions & 0 deletions .github/workflows/secret-detection.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Secret detection

on:
push:
pull_request:
schedule:
- cron: "17 6 * * 1"
workflow_dispatch:

permissions:
contents: read

concurrency:
group: secret-detection-${{ github.repository }}-${{ github.ref }}
cancel-in-progress: true

jobs:
gitleaks:
name: Gitleaks (complete history)
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out complete history
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
git init .
git remote add origin "https://github.com/${GITHUB_REPOSITORY}.git"
auth_header="AUTHORIZATION: basic $(printf 'x-access-token:%s' "${GH_TOKEN}" | base64 --wrap=0)"
readonly auth_header
git -c "http.https://github.com/.extraheader=${auth_header}" fetch \
--no-tags --prune origin "+${GITHUB_SHA}:refs/remotes/origin/scan"
git checkout --detach refs/remotes/origin/scan

- name: Install pinned Gitleaks release
shell: bash
run: |
set -euo pipefail
readonly version="8.28.0"
readonly archive="gitleaks_${version}_linux_x64.tar.gz"
readonly expected_sha256="a65b5253807a68ac0cafa4414031fd740aeb55f54fb7e55f386acb52e6a840eb"
curl --fail --silent --show-error --location --retry 3 \
"https://github.com/gitleaks/gitleaks/releases/download/v${version}/${archive}" \
--output "${RUNNER_TEMP}/${archive}"
echo "${expected_sha256} ${RUNNER_TEMP}/${archive}" | sha256sum --check --strict
tar --extract --gzip --file "${RUNNER_TEMP}/${archive}" --directory "${RUNNER_TEMP}" gitleaks
"${RUNNER_TEMP}/gitleaks" version

- name: Scan complete repository history
shell: bash
run: |
set -euo pipefail
"${RUNNER_TEMP}/gitleaks" git --redact=100 --no-banner --verbose .

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Scan results are emitted only to ephemeral CI logs. GitHub retains workflow run logs for a limited window (default 90 days). For legal-grade audit trail requirements, the raw scan output should be preserved as a permanent, immutable artifact — especially for scheduled and manual runs where findings may be triaged over time.

Suggestion: Add --report-path gitleaks-report.json (or .sarif) to the scan step, then use actions/upload-artifact to persist the report. This creates a durable, machine-readable record of what was detected and when, independent of log retention.

Loading