From 8b358a26202ce757041f87567dc7cc70198a5725 Mon Sep 17 00:00:00 2001 From: JacobPEvans <20714140+JacobPEvans-personal@users.noreply.github.com> Date: Sun, 31 May 2026 11:40:18 -0400 Subject: [PATCH 1/3] feat(security): add fail-closed secret-scan gate + document it Adds a gitleaks secret-scan gate so no sensitive value can reach docs.jacobpevans.com: - scripts/secret-scan.sh installs pinned, checksum-verified gitleaks and scans the committed tree (git archive HEAD), fail-closed. - .github/workflows/secret-scan.yml runs it on PRs (incl. drafts) + push to main. - .gitleaks.toml is the public-safe generic baseline (credential rules + placeholder allowlist). The org-specific ruleset stays private in docs-starlight and reaches CI as the redacted GITLEAKS_PRIVATE_CONFIG secret; fork PRs fall back to the baseline. Mirrors the change into the docs: documents the gate in security/scrubbed-values.mdx and registers the new Docs Sync routine in automation/scheduled-routines/claude-code-routines.mdx. The gitleaks Action requires a license for org accounts, so the Apache-2.0 binary is installed directly instead. Assisted-by: Claude:claude-opus-4-8 --- .github/workflows/secret-scan.yml | 35 ++++++++++++++++ .gitleaks.toml | 23 +++++++++++ .../claude-code-routines.mdx | 7 ++-- scripts/secret-scan.sh | 40 +++++++++++++++++++ security/scrubbed-values.mdx | 11 +++++ 5 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/secret-scan.yml create mode 100644 .gitleaks.toml create mode 100755 scripts/secret-scan.sh diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml new file mode 100644 index 0000000..f1cb260 --- /dev/null +++ b/.github/workflows/secret-scan.yml @@ -0,0 +1,35 @@ +# Secret-scan gate for the public docs site. +# +# Blocks any sensitive value from reaching docs.jacobpevans.com. Runs the full +# private org ruleset when the GITLEAKS_PRIVATE_CONFIG secret is available +# (same-repo PRs incl. drafts, and push to main), falling back to the committed +# generic .gitleaks.toml for fork PRs. Fail-closed. See scripts/secret-scan.sh +# and security/scrubbed-values.mdx. +# +# Make this a required check via branch protection on `main` after merge. +name: Secret Scan + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + push: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + gitleaks: + name: gitleaks (redacted) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Scan for secrets and sensitive values + env: + GITLEAKS_PRIVATE_CONFIG: ${{ secrets.GITLEAKS_PRIVATE_CONFIG }} + run: ./scripts/secret-scan.sh diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..f94a490 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,23 @@ +# Committed, public-safe gitleaks baseline for docs.jacobpevans.com. +# +# Used by .github/workflows/secret-scan.yml as the FALLBACK when the private full +# ruleset is unavailable (fork PRs that cannot read the GITLEAKS_PRIVATE_CONFIG +# secret). It intentionally contains NOTHING org-specific: only gitleaks' built-in +# credential rules plus an allowlist of approved documentation placeholders. The +# org-specific patterns live solely in the private docs-starlight repo and reach +# this repo's CI as an encrypted secret. See security/scrubbed-values.mdx. + +title = "docs public secret-scan baseline" + +[extend] +useDefault = true + +[allowlist] +description = "Approved public documentation placeholders — never flag these." +regexes = [ + '''192\.168\.0\.[0-9]{1,3}''', + '''(192\.0\.2|198\.51\.100|203\.0\.113)\.[0-9]{1,3}''', + '''2001:db8:''', + '''example\.(com|org|local|test)''', + '''(your-token-here||)''', +] diff --git a/automation/scheduled-routines/claude-code-routines.mdx b/automation/scheduled-routines/claude-code-routines.mdx index 8542565..d3b96f3 100644 --- a/automation/scheduled-routines/claude-code-routines.mdx +++ b/automation/scheduled-routines/claude-code-routines.mdx @@ -1,14 +1,14 @@ --- title: "claude-code-routines" -description: "Six cron-scheduled Claude Code routines that watch the whole org without per-repo wiring. Pick up loose ends the event-triggered pipeline misses." +description: "Seven cron-scheduled Claude Code routines that watch the whole org without per-repo wiring. Pick up loose ends the event-triggered pipeline misses." tier: 2 --- > Cron, not events. Org-wide, not per-repo. One configuration, scans everything. -[`JacobPEvans/claude-code-routines`](https://github.com/JacobPEvans/claude-code-routines) ships six prompt files that run on schedule inside Anthropic's cloud-hosted Claude Code sandbox. They discover repos under `$GH_OWNER` via `gh search`, do their work using `gh` CLI calls, and report results to Slack via the official Slack MCP connector. There is no per-repo wiring — drop a repo into the org and the routines pick it up on the next run. +[`JacobPEvans/claude-code-routines`](https://github.com/JacobPEvans/claude-code-routines) ships seven prompt files that run on schedule inside Anthropic's cloud-hosted Claude Code sandbox. They discover repos under `$GH_OWNER` via `gh search`, do their work using `gh` CLI calls, and report results to Slack via the official Slack MCP connector. There is no per-repo wiring — drop a repo into the org and the routines pick it up on the next run. -## The six routines +## The seven routines | Routine | Schedule (CT) | What it does | | --- | --- | --- | @@ -17,6 +17,7 @@ tier: 2 | The Custodian | Daily 2:00 AM | Weighted-random maintenance: stale branch cleanup, missing labels, repo health audit | | Issue Solver | Daily 7:00 AM + 7:00 PM | Picks one open issue, takes it from triage → draft PR in six phases | | Daily Polish | Daily 11:00 PM | Deep-clean one repo per day: README gaps, missing `.gitignore`, CI config, test coverage | +| Docs Sync | Daily 3:13 AM | Reads 48h of estate change; opens a public `docs` PR (scrubbed) and a private `docs-starlight` PR (DRY links to docs.jacobpevans.com) | | Weekly Scorecard | Mondays 5:00 AM | Portfolio health scores: repo count, open-issue median, CI pass rate, test coverage % | ## How the Issue Solver picks work diff --git a/scripts/secret-scan.sh b/scripts/secret-scan.sh new file mode 100755 index 0000000..9fe0c76 --- /dev/null +++ b/scripts/secret-scan.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# Secret-scan gate for the public docs repo (docs.jacobpevans.com). +# +# Scans the committed tree with gitleaks. When the private org ruleset is present +# as the GITLEAKS_PRIVATE_CONFIG secret (same-repo PRs + push to main), it is used +# with --redact so matches never appear in logs. Fork PRs cannot read the secret +# and fall back to the committed generic .gitleaks.toml (credential detection only). +# Fail-closed: any finding exits non-zero and fails the job. +# +# gitleaks is installed from its pinned Apache-2.0 release binary rather than the +# gitleaks GitHub Action, which requires a paid license for organization accounts. +set -euo pipefail + +VERSION="8.30.1" +SHA256="551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb" +ASSET="gitleaks_${VERSION}_linux_x64.tar.gz" + +tmp="$(mktemp -d)" +trap 'rm -rf "${tmp}"' EXIT + +curl -fsSL "https://github.com/gitleaks/gitleaks/releases/download/v${VERSION}/${ASSET}" -o "${tmp}/${ASSET}" +echo "${SHA256} ${tmp}/${ASSET}" | sha256sum -c - +tar -xzf "${tmp}/${ASSET}" -C "${tmp}" gitleaks + +# Scan exactly the committed content (no .git, no untracked scratch). +mkdir -p "${tmp}/tree" +git archive HEAD | tar -x -C "${tmp}/tree" + +config=".gitleaks.toml" +if [ -n "${GITLEAKS_PRIVATE_CONFIG:-}" ]; then + config="${tmp}/private.toml" + printf '%s' "${GITLEAKS_PRIVATE_CONFIG}" > "${config}" + echo "Scanning with the private org ruleset (redacted)." +else + echo "Private ruleset unavailable (fork PR?) — using the committed generic baseline." +fi + +"${tmp}/gitleaks" detect --no-git --no-banner --redact \ + --source "${tmp}/tree" --config "${config}" +echo "Secret scan passed: no leaks found." diff --git a/security/scrubbed-values.mdx b/security/scrubbed-values.mdx index 41b0b56..606705d 100644 --- a/security/scrubbed-values.mdx +++ b/security/scrubbed-values.mdx @@ -22,6 +22,17 @@ tier: 2 Never write a real value even in a "wrong" example — the example becomes committed text. Use `${USER}`, `${REPO_ROOT}`, `${MAINTAINER_EMAIL}`, `${NAS_HOST}`, `` for shape stand-ins where the placeholder needs to look like a variable. +## Enforcement: the secret-scan gate + +The public `docs` repo runs a fail-closed [gitleaks](https://github.com/gitleaks/gitleaks) check (`.github/workflows/secret-scan.yml`) on every PR and push to `main`. A finding fails the job, so a sensitive value cannot merge. + +The rules come in two layers, so the *list of what counts as sensitive stays private*: + +- A committed, generic baseline (`.gitleaks.toml`) — gitleaks' built-in credential rules plus the placeholder allowlist above. It contains nothing org-specific, so it is safe to keep in this public repo. Fork PRs (which cannot read repository secrets) are scanned with this baseline. +- The full org-specific ruleset lives **only** in a private repo and reaches this repo's CI as an encrypted secret. The scan runs with `--redact`, so neither the patterns nor any match ever appear in public logs. + +The same private ruleset is consumed by the [Docs Sync routine](/automation/scheduled-routines/claude-code-routines), which scrubs drafted content before it is ever committed here — the CI gate is the backstop for anything authored by hand. + ## Portable path references **Never commit absolute user paths** (`/Users/{username}/*`, `/home/{username}/*`, `$HOME/*`, `~/*`). From 568859f5c177dfa3009e2e4755fd2ccbce6a3f1f Mon Sep 17 00:00:00 2001 From: JacobPEvans <20714140+JacobPEvans-personal@users.noreply.github.com> Date: Sun, 31 May 2026 13:19:43 -0400 Subject: [PATCH 2/3] refactor(security): run gitleaks via Docker image, drop the install script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces scripts/secret-scan.sh with two declarative workflow steps: select the ruleset (private secret, else committed example) via shell parameter expansion, then scan with the official gitleaks OSS Docker image — no GitHub Action license (required for org accounts) and no custom install logic. - Renames .gitleaks.toml -> .gitleaks.toml.example: it is a template + fork fallback, not the active config. The active ruleset is injected from the GITLEAKS_PRIVATE_CONFIG secret. - Adds a .git path allowlist so VCS metadata is not scanned. Validated: config parses; a planted .git secret is ignored; the gate still flags the pre-existing client-term references in the docs tree. Assisted-by: Claude:claude-opus-4-8 --- .github/workflows/secret-scan.yml | 23 +++++++++++++----- .gitleaks.toml | 23 ------------------ .gitleaks.toml.example | 25 +++++++++++++++++++ scripts/secret-scan.sh | 40 ------------------------------- security/scrubbed-values.mdx | 2 +- 5 files changed, 43 insertions(+), 70 deletions(-) delete mode 100644 .gitleaks.toml create mode 100644 .gitleaks.toml.example delete mode 100755 scripts/secret-scan.sh diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml index f1cb260..31f9ce1 100644 --- a/.github/workflows/secret-scan.yml +++ b/.github/workflows/secret-scan.yml @@ -1,10 +1,13 @@ # Secret-scan gate for the public docs site. # # Blocks any sensitive value from reaching docs.jacobpevans.com. Runs the full -# private org ruleset when the GITLEAKS_PRIVATE_CONFIG secret is available -# (same-repo PRs incl. drafts, and push to main), falling back to the committed -# generic .gitleaks.toml for fork PRs. Fail-closed. See scripts/secret-scan.sh -# and security/scrubbed-values.mdx. +# private org ruleset injected from the GITLEAKS_PRIVATE_CONFIG secret (same-repo +# PRs incl. drafts, and push to main); fork PRs that cannot read the secret fall +# back to the committed generic .gitleaks.toml.example. Fail-closed: any finding +# fails the job. See security/scrubbed-values.mdx. +# +# gitleaks runs from its official OSS Docker image — the gitleaks GitHub Action +# requires a paid license for organization accounts; the image does not. # # Make this a required check via branch protection on `main` after merge. name: Secret Scan @@ -29,7 +32,15 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Scan for secrets and sensitive values + - name: Select ruleset (private secret, else committed example) env: GITLEAKS_PRIVATE_CONFIG: ${{ secrets.GITLEAKS_PRIVATE_CONFIG }} - run: ./scripts/secret-scan.sh + run: printf '%s' "${GITLEAKS_PRIVATE_CONFIG:-$(cat .gitleaks.toml.example)}" > "$RUNNER_TEMP/gitleaks.toml" + + - name: Scan for secrets and sensitive values + run: > + docker run --rm + -v "$PWD:/repo:ro" + -v "$RUNNER_TEMP/gitleaks.toml:/cfg.toml:ro" + ghcr.io/gitleaks/gitleaks:v8.30.1 + detect --source /repo --no-git --config /cfg.toml --no-banner --redact diff --git a/.gitleaks.toml b/.gitleaks.toml deleted file mode 100644 index f94a490..0000000 --- a/.gitleaks.toml +++ /dev/null @@ -1,23 +0,0 @@ -# Committed, public-safe gitleaks baseline for docs.jacobpevans.com. -# -# Used by .github/workflows/secret-scan.yml as the FALLBACK when the private full -# ruleset is unavailable (fork PRs that cannot read the GITLEAKS_PRIVATE_CONFIG -# secret). It intentionally contains NOTHING org-specific: only gitleaks' built-in -# credential rules plus an allowlist of approved documentation placeholders. The -# org-specific patterns live solely in the private docs-starlight repo and reach -# this repo's CI as an encrypted secret. See security/scrubbed-values.mdx. - -title = "docs public secret-scan baseline" - -[extend] -useDefault = true - -[allowlist] -description = "Approved public documentation placeholders — never flag these." -regexes = [ - '''192\.168\.0\.[0-9]{1,3}''', - '''(192\.0\.2|198\.51\.100|203\.0\.113)\.[0-9]{1,3}''', - '''2001:db8:''', - '''example\.(com|org|local|test)''', - '''(your-token-here||)''', -] diff --git a/.gitleaks.toml.example b/.gitleaks.toml.example new file mode 100644 index 0000000..f753441 --- /dev/null +++ b/.gitleaks.toml.example @@ -0,0 +1,25 @@ +# .gitleaks.toml.example — TEMPLATE / fork fallback (NOT the active config). +# +# CI does not use this file directly. .github/workflows/secret-scan.yml runs the +# full org ruleset injected from the private GITLEAKS_PRIVATE_CONFIG secret; this +# committed example is the fallback only when that secret is unavailable (e.g. +# fork PRs). It is intentionally generic — gitleaks' built-in credential rules +# plus approved documentation placeholders — and contains nothing org-specific. +# The real, org-specific ruleset lives privately in dryvist/docs-starlight. See +# security/scrubbed-values.mdx. + +title = "docs public secret-scan baseline (example / fork fallback)" + +[extend] +useDefault = true + +[allowlist] +description = "Skip VCS metadata; never flag approved documentation placeholders." +paths = ['''(^|/)\.git/'''] +regexes = [ + '''192\.168\.0\.[0-9]{1,3}''', + '''(192\.0\.2|198\.51\.100|203\.0\.113)\.[0-9]{1,3}''', + '''2001:db8:''', + '''example\.(com|org|local|test)''', + '''(your-token-here||)''', +] diff --git a/scripts/secret-scan.sh b/scripts/secret-scan.sh deleted file mode 100755 index 9fe0c76..0000000 --- a/scripts/secret-scan.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash -# Secret-scan gate for the public docs repo (docs.jacobpevans.com). -# -# Scans the committed tree with gitleaks. When the private org ruleset is present -# as the GITLEAKS_PRIVATE_CONFIG secret (same-repo PRs + push to main), it is used -# with --redact so matches never appear in logs. Fork PRs cannot read the secret -# and fall back to the committed generic .gitleaks.toml (credential detection only). -# Fail-closed: any finding exits non-zero and fails the job. -# -# gitleaks is installed from its pinned Apache-2.0 release binary rather than the -# gitleaks GitHub Action, which requires a paid license for organization accounts. -set -euo pipefail - -VERSION="8.30.1" -SHA256="551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb" -ASSET="gitleaks_${VERSION}_linux_x64.tar.gz" - -tmp="$(mktemp -d)" -trap 'rm -rf "${tmp}"' EXIT - -curl -fsSL "https://github.com/gitleaks/gitleaks/releases/download/v${VERSION}/${ASSET}" -o "${tmp}/${ASSET}" -echo "${SHA256} ${tmp}/${ASSET}" | sha256sum -c - -tar -xzf "${tmp}/${ASSET}" -C "${tmp}" gitleaks - -# Scan exactly the committed content (no .git, no untracked scratch). -mkdir -p "${tmp}/tree" -git archive HEAD | tar -x -C "${tmp}/tree" - -config=".gitleaks.toml" -if [ -n "${GITLEAKS_PRIVATE_CONFIG:-}" ]; then - config="${tmp}/private.toml" - printf '%s' "${GITLEAKS_PRIVATE_CONFIG}" > "${config}" - echo "Scanning with the private org ruleset (redacted)." -else - echo "Private ruleset unavailable (fork PR?) — using the committed generic baseline." -fi - -"${tmp}/gitleaks" detect --no-git --no-banner --redact \ - --source "${tmp}/tree" --config "${config}" -echo "Secret scan passed: no leaks found." diff --git a/security/scrubbed-values.mdx b/security/scrubbed-values.mdx index 606705d..22d9985 100644 --- a/security/scrubbed-values.mdx +++ b/security/scrubbed-values.mdx @@ -28,7 +28,7 @@ The public `docs` repo runs a fail-closed [gitleaks](https://github.com/gitleaks The rules come in two layers, so the *list of what counts as sensitive stays private*: -- A committed, generic baseline (`.gitleaks.toml`) — gitleaks' built-in credential rules plus the placeholder allowlist above. It contains nothing org-specific, so it is safe to keep in this public repo. Fork PRs (which cannot read repository secrets) are scanned with this baseline. +- A committed, generic baseline (`.gitleaks.toml.example`) — gitleaks' built-in credential rules plus the placeholder allowlist above. It contains nothing org-specific, so it is safe to keep in this public repo. CI does not use it directly; it is the fallback for fork PRs that cannot read the secret. - The full org-specific ruleset lives **only** in a private repo and reaches this repo's CI as an encrypted secret. The scan runs with `--redact`, so neither the patterns nor any match ever appear in public logs. The same private ruleset is consumed by the [Docs Sync routine](/automation/scheduled-routines/claude-code-routines), which scrubs drafted content before it is ever committed here — the CI gate is the backstop for anything authored by hand. From f16ac0adb3aa009714df50cc59a6a11d751386bc Mon Sep 17 00:00:00 2001 From: JacobPEvans <20714140+JacobPEvans-personal@users.noreply.github.com> Date: Sun, 31 May 2026 14:23:02 -0400 Subject: [PATCH 3/3] refactor(security): scan via gitleaks-action with the org secret ruleset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switches the gate from the raw gitleaks Docker image to gitleaks-action@v2, now that the dryvist org provides GITLEAKS_LICENSE_KEY. The ruleset is injected from the org-wide GITLEAKS_PRIVATE_CONFIG secret (never committed — the sensitive list stays private), materialized to a config file at run time. The action runs gitleaks with --redact; PR comments, SARIF artifact, and job summary are disabled so the rule taxonomy / file locations are not exposed on this public repo. Removes the now-unused .gitleaks.toml.example. Updates the security doc to match. Assisted-by: Claude:claude-opus-4-8 --- .github/workflows/secret-scan.yml | 38 ++++++++++++++++++------------- .gitleaks.toml.example | 25 -------------------- security/scrubbed-values.mdx | 9 +++----- 3 files changed, 25 insertions(+), 47 deletions(-) delete mode 100644 .gitleaks.toml.example diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml index 31f9ce1..b0e490e 100644 --- a/.github/workflows/secret-scan.yml +++ b/.github/workflows/secret-scan.yml @@ -1,13 +1,15 @@ # Secret-scan gate for the public docs site. # -# Blocks any sensitive value from reaching docs.jacobpevans.com. Runs the full -# private org ruleset injected from the GITLEAKS_PRIVATE_CONFIG secret (same-repo -# PRs incl. drafts, and push to main); fork PRs that cannot read the secret fall -# back to the committed generic .gitleaks.toml.example. Fail-closed: any finding -# fails the job. See security/scrubbed-values.mdx. +# Blocks sensitive values from reaching docs.jacobpevans.com. Runs gitleaks-action +# with the org-wide private ruleset (the dryvist GITLEAKS_PRIVATE_CONFIG secret, +# injected at run time) and the org GITLEAKS_LICENSE_KEY. The action runs gitleaks +# with --redact, so matched values never appear in logs. # -# gitleaks runs from its official OSS Docker image — the gitleaks GitHub Action -# requires a paid license for organization accounts; the image does not. +# The ruleset lives ONLY in the org secret — never committed — so the list of what +# counts as sensitive stays private. Public-output features (PR comments, SARIF +# artifact, job summary) are disabled: on a public repo they would expose the rule +# taxonomy / file locations on a catch. A finding fails the job; investigate from a +# trusted checkout with the private config. # # Make this a required check via branch protection on `main` after merge. name: Secret Scan @@ -24,6 +26,7 @@ concurrency: permissions: contents: read + pull-requests: read jobs: gitleaks: @@ -31,16 +34,19 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - - name: Select ruleset (private secret, else committed example) + - name: Materialize the org ruleset into a config file env: GITLEAKS_PRIVATE_CONFIG: ${{ secrets.GITLEAKS_PRIVATE_CONFIG }} - run: printf '%s' "${GITLEAKS_PRIVATE_CONFIG:-$(cat .gitleaks.toml.example)}" > "$RUNNER_TEMP/gitleaks.toml" + run: printf '%s' "$GITLEAKS_PRIVATE_CONFIG" > "$RUNNER_TEMP/gitleaks.toml" - - name: Scan for secrets and sensitive values - run: > - docker run --rm - -v "$PWD:/repo:ro" - -v "$RUNNER_TEMP/gitleaks.toml:/cfg.toml:ro" - ghcr.io/gitleaks/gitleaks:v8.30.1 - detect --source /repo --no-git --config /cfg.toml --no-banner --redact + - uses: gitleaks/gitleaks-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE_KEY }} + GITLEAKS_CONFIG: ${{ runner.temp }}/gitleaks.toml + GITLEAKS_ENABLE_UPLOAD_ARTIFACT: "false" + GITLEAKS_ENABLE_COMMENTS: "false" + GITLEAKS_ENABLE_SUMMARY: "false" diff --git a/.gitleaks.toml.example b/.gitleaks.toml.example deleted file mode 100644 index f753441..0000000 --- a/.gitleaks.toml.example +++ /dev/null @@ -1,25 +0,0 @@ -# .gitleaks.toml.example — TEMPLATE / fork fallback (NOT the active config). -# -# CI does not use this file directly. .github/workflows/secret-scan.yml runs the -# full org ruleset injected from the private GITLEAKS_PRIVATE_CONFIG secret; this -# committed example is the fallback only when that secret is unavailable (e.g. -# fork PRs). It is intentionally generic — gitleaks' built-in credential rules -# plus approved documentation placeholders — and contains nothing org-specific. -# The real, org-specific ruleset lives privately in dryvist/docs-starlight. See -# security/scrubbed-values.mdx. - -title = "docs public secret-scan baseline (example / fork fallback)" - -[extend] -useDefault = true - -[allowlist] -description = "Skip VCS metadata; never flag approved documentation placeholders." -paths = ['''(^|/)\.git/'''] -regexes = [ - '''192\.168\.0\.[0-9]{1,3}''', - '''(192\.0\.2|198\.51\.100|203\.0\.113)\.[0-9]{1,3}''', - '''2001:db8:''', - '''example\.(com|org|local|test)''', - '''(your-token-here||)''', -] diff --git a/security/scrubbed-values.mdx b/security/scrubbed-values.mdx index 22d9985..e9d94d7 100644 --- a/security/scrubbed-values.mdx +++ b/security/scrubbed-values.mdx @@ -24,14 +24,11 @@ Never write a real value even in a "wrong" example — the example becomes commi ## Enforcement: the secret-scan gate -The public `docs` repo runs a fail-closed [gitleaks](https://github.com/gitleaks/gitleaks) check (`.github/workflows/secret-scan.yml`) on every PR and push to `main`. A finding fails the job, so a sensitive value cannot merge. +The public `docs` repo runs a fail-closed [gitleaks](https://github.com/gitleaks/gitleaks) check (`.github/workflows/secret-scan.yml`) on every PR and push to `main`, via [`gitleaks-action`](https://github.com/gitleaks/gitleaks-action). A finding fails the job, so a sensitive value cannot merge. -The rules come in two layers, so the *list of what counts as sensitive stays private*: +So the *list of what counts as sensitive stays private*, the rule set is **never committed**. It lives only as the `dryvist` org Actions secret `GITLEAKS_PRIVATE_CONFIG`, injected into CI at run time. The scan runs with `--redact`, and the public-output features (PR comments, SARIF artifact, job summary) are disabled, so neither the patterns nor any match ever surface publicly. `useDefault` covers generic credentials; org-specific rules cover the values that must never appear here. -- A committed, generic baseline (`.gitleaks.toml.example`) — gitleaks' built-in credential rules plus the placeholder allowlist above. It contains nothing org-specific, so it is safe to keep in this public repo. CI does not use it directly; it is the fallback for fork PRs that cannot read the secret. -- The full org-specific ruleset lives **only** in a private repo and reaches this repo's CI as an encrypted secret. The scan runs with `--redact`, so neither the patterns nor any match ever appear in public logs. - -The same private ruleset is consumed by the [Docs Sync routine](/automation/scheduled-routines/claude-code-routines), which scrubs drafted content before it is ever committed here — the CI gate is the backstop for anything authored by hand. +The same gate runs on the draft PRs opened by the [Docs Sync routine](/automation/scheduled-routines/claude-code-routines), so machine-authored content is held to the same bar as anything authored by hand. ## Portable path references