Skip to content
Merged
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
6 changes: 6 additions & 0 deletions .github/workflows/language-gates-self-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ on:
- "scripts/verify-suite-checkout.sh"
- "scripts/deonboard-workflow.sh"
- "tests/gate-action-owners.sh"
- "tests/inspect-pr-ci.sh"
- "scripts/inspect-pr-ci.sh"
- ".github/workflows/rust-ci.yml"
- ".github/workflows/creusot-gate.yml"
- ".github/workflows/spark-gate.yml"
Expand All @@ -41,6 +43,8 @@ on:
- "scripts/verify-suite-checkout.sh"
- "scripts/deonboard-workflow.sh"
- "tests/gate-action-owners.sh"
- "tests/inspect-pr-ci.sh"
- "scripts/inspect-pr-ci.sh"
- ".github/workflows/rust-ci.yml"
- ".github/workflows/creusot-gate.yml"
- ".github/workflows/spark-gate.yml"
Expand Down Expand Up @@ -92,6 +96,8 @@ jobs:
rc=0; EXPECTED=0000000000000000000000000000000000000000 SUITE_DIR=. bash scripts/verify-suite-checkout.sh || rc=$?
[ "$rc" -eq 2 ] || { echo "verifier accepted a wrong SHA (rc=$rc)"; exit 1; }
EXPECTED="$(git rev-parse HEAD)" SUITE_DIR=. bash scripts/verify-suite-checkout.sh
- name: PR evidence collector refuses invisible-green and denied-probe assumptions
run: bash tests/inspect-pr-ci.sh
- name: Rust gate
run: bash actions/rust-ci-check/test.sh
- name: Creusot gate
Expand Down
28 changes: 28 additions & 0 deletions .github/workflows/secret-scanner-legacy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Canary safety: retained for Rust/shell coverage comparison; not scan / gitleaks.
# SPDX-License-Identifier: MPL-2.0
# Prevention workflow - scans for hardcoded secrets before they reach main.
#
# Calls the estate's shared secret scanner (gitleaks + rust-secrets +
# shell-secrets). The job key MUST stay `scan`: the estate-wide
# Secret-Scan-Floor ruleset requires the check context `scan / gitleaks`.
#
# No `secrets:` line, deliberately: the reusable references no secrets
# (gitleaks runs as a checksum-verified binary, not gitleaks-action), so
# `secrets: inherit` would only hand it every repo and org secret (CWE-250).
name: Legacy secret scanner (coverage comparison)

on:
pull_request:
push:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
legacy:
uses: hyperpolymath/standards/.github/workflows/secret-scanner-reusable.yml@74d2f66f575246cf6e313ae7775f44df6e097ff2
82 changes: 82 additions & 0 deletions .github/workflows/secret-scanner-reusable.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# SPDX-License-Identifier: MPL-2.0
# Single required context remains scan / gitleaks in direct callers.
name: Bounded secret scanner (reusable)
on:
workflow_call:
inputs:
fast-pr:
description: Security-owner-approved introduced-commit PR scan; non-PR runs always scan full history
type: boolean
default: false
permissions:
contents: read
jobs:
gitleaks:
runs-on: ubuntu-24.04
timeout-minutes: 15
permissions:
contents: read
defaults:
run:
shell: bash
steps:
- name: Check out subject and complete ancestry
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
path: subject
fetch-depth: 0
persist-credentials: false
- name: Resolve exact suite pin from the direct scan caller
id: suite
env:
CALLER_REF: ${{ github.workflow_ref }}
CALLER_REPO: ${{ github.repository }}
run: |
set -euo pipefail
file="${CALLER_REF#"$CALLER_REPO"/}"; file="${file%@*}"
case "$file" in .github/workflows/*.yml|.github/workflows/*.yaml) ;; *) exit 2 ;; esac
ref=$(yq -r '.jobs.scan.uses' "subject/$file")
Comment thread
hyperpolymath marked this conversation as resolved.
Resolved
prefix=hyperpolymath/cicd-suite/.github/workflows/secret-scanner-reusable.yml@
[[ $ref == "$prefix"* ]] || { echo '::error::requires a direct scan caller'; exit 2; }
sha=${ref#"$prefix"}
[[ $sha =~ ^[0-9a-f]{40}$ ]] || { echo '::error::caller must pin a full SHA'; exit 2; }
echo "sha=$sha" >> "$GITHUB_OUTPUT"
- name: Check out pinned scanner implementation outside subject
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: hyperpolymath/cicd-suite
ref: ${{ steps.suite.outputs.sha }}
path: suite
persist-credentials: false
- name: Verify pinned checkout and install checksum-pinned Gitleaks
env:
EXPECTED: ${{ steps.suite.outputs.sha }}
run: |
set -euo pipefail
[[ $(git -C suite rev-parse HEAD) == "$EXPECTED" ]] || exit 2
bash suite/actions/secret-scan-check/install.sh "$RUNNER_TEMP/suite-gitleaks"
- name: Run real detector controls before trusting the scan
env:
GITLEAKS_BIN: ${{ runner.temp }}/suite-gitleaks/gitleaks
run: bash suite/actions/secret-scan-check/control.sh
- name: Scan subject (bounded, redacted, both populations)
env:
GITLEAKS_BIN: ${{ runner.temp }}/suite-gitleaks/gitleaks
SUBJECT: ${{ github.workspace }}/subject
ESTATE_BASELINE: ${{ github.workspace }}/suite/config/gitleaks/estate-baseline.toml
FAST_PR: ${{ inputs.fast-pr }}
EVENT: ${{ github.event_name }}
PR_BASE: ${{ github.event.pull_request.base.sha }}
PR_HEAD: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail
export SCAN_MODE=full SCAN_TIMEOUT_SECONDS=180
if [[ "$EVENT" == pull_request && "$FAST_PR" == true ]]; then
export SCAN_MODE=pr-range HEAD_SHA="$PR_HEAD"
# A PR may be behind the base tip: scan from their common ancestor,
# not from the final file diff and not from a guessed depth.
export BASE_SHA
BASE_SHA=$(git -C "$SUBJECT" merge-base "$PR_BASE" "$PR_HEAD")
fi
bash suite/actions/secret-scan-check/check.sh
36 changes: 36 additions & 0 deletions .github/workflows/secret-scanner-self-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# SPDX-License-Identifier: MPL-2.0
name: Bounded secret scanner controls
on:
push:
pull_request:
permissions:
contents: read
concurrency:
group: secret-controls-${{ github.ref }}
cancel-in-progress: true
jobs:
controls:
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Shell and YAML syntax
shell: bash
run: |
set -euo pipefail
for f in actions/secret-scan-check/*.sh; do bash -n "$f"; done
yq '.' .github/workflows/secret-scanner-reusable.yml >/dev/null
yq '.' templates/secret-scanner-caller.yml >/dev/null
yq '.' actions/secret-scan-check/action.yml >/dev/null
- name: Shell/control-flow fixtures and vacuous-scanner mutant
run: bash actions/secret-scan-check/test.sh
- name: Install the pinned real scanner
run: bash actions/secret-scan-check/install.sh "$RUNNER_TEMP/suite-gitleaks"
- name: Repeat fixtures against the real detector (not a stand-in)
env:
GITLEAKS_BIN: ${{ runner.temp }}/suite-gitleaks/gitleaks
run: bash actions/secret-scan-check/test.sh
- name: Evidence collector controls
run: bash tests/inspect-pr-ci.sh
30 changes: 13 additions & 17 deletions .github/workflows/secret-scanner.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
# SPDX-License-Identifier: MPL-2.0
# Prevention workflow - scans for hardcoded secrets before they reach main.
#
# Calls the estate's shared secret scanner (gitleaks + rust-secrets +
# shell-secrets). The job key MUST stay `scan`: the estate-wide
# Secret-Scan-Floor ruleset requires the check context `scan / gitleaks`.
#
# No `secrets:` line, deliberately: the reusable references no secrets
# (gitleaks runs as a checksum-verified binary, not gitleaks-action), so
# `secrets: inherit` would only hand it every repo and org secret (CWE-250).
# Keep scan / gitleaks: required by the estate Secret-Scan-Floor ruleset.
name: Secret Scanner

on:
pull_request:
push:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

schedule:
- cron: '23 3 * * 2'
workflow_dispatch:
permissions:
contents: read

concurrency:
# Scheduled/full audits and PR cancellation do not cancel one another.
group: secrets-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: true
jobs:
scan:
uses: hyperpolymath/standards/.github/workflows/secret-scanner-reusable.yml@74d2f66f575246cf6e313ae7775f44df6e097ff2
uses: hyperpolymath/cicd-suite/.github/workflows/secret-scanner-reusable.yml@50dc27f47e39f7ac046596c071ed4ea80b4989e0
with:
# Keep false until an initial full audit passes and the security owner
# accepts introduced-commit PR coverage. All non-PR runs remain full.
fast-pr: false
50 changes: 47 additions & 3 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,10 @@ them is a rename to `+*-advisory+`, not teeth.
longer runs them.*

* `+semantic-audit+` is pure `+echo+` with "`Placeholder for actual
invocation`"; its real body is `+cicd-squabbler audit --semantic+`, and
*`+cicd-squabbler+` does not exist yet*. It cannot be implemented, only
parked.
invocation`". `+cicd-squabbler+` now exists, but is a gate diagnosis
and legitimate-remediation engine, not evidence of the imagined
`+audit --semantic+` interface. Keep this stub out of enforcement; do not
confuse pipeline diagnosis with semantic verification.
* `+prat+` claims to validate "`PRAT testing`", but *PRAT is defined
nowhere in the estate* — a search of every repository turns up only
further copies of the workflow that calls it. Its current check passes
Expand Down Expand Up @@ -389,3 +390,46 @@ this repository's own CI can witness them; both are now guarded here.
=== Licence

Code MPL-2.0, prose CC-BY-SA-4.0. See link:LICENSE[`+LICENSE+`].

=== Merge-blocker diagnosis and secret-scan latency

`+bash scripts/inspect-pr-ci.sh OWNER/REPO PR_NUMBER+` collects read-only
JSON evidence: PR checks, effective branch rules, classic protection,
head-SHA workflow runs and jobs, including failed workflows with zero jobs.
Requires `+gh+` and `+jq+`; exit 2 means collection did not complete, including
invalid arguments, missing `+gh+` or `+jq+`, and denied API probes. Exit 0
means collection succeeded, *not* that CI passed.
The report is not merge authorisation, a full check-history export, or an
implementation of cicd-squabbler. Use squabbler for attribution and repair.
No logs, artifacts, secrets, policy edits, reruns or merges are requested.

See link:docs/CI-TRIAGE-2026-10-30.adoc[the sampled estate diagnosis] and
link:docs/SECRET-SCAN-PERFORMANCE.adoc[the secret-scan latency investigation
and implementation handoff]. No replacement scanner has been deployed.

=== Bounded secret scanner (canary, not estate-deployed)

`+.github/workflows/secret-scanner-reusable.yml+` and
`+actions/secret-scan-check/+` provide a checksum-pinned Gitleaks 8.30.1
implementation with bounded download/scan deadlines, redacted logs,
real detector controls using the effective configuration, and separate
working-tree/history passes. The vendored estate baseline records its
upstream SHA; no renamed standards reusable is needed at runtime.

The direct caller template preserves `+scan / gitleaks+`. Full history
remains the default. `+fast-pr: true+` explicitly opts PRs into scanning
all introduced commits, not merely the final diff; non-PR runs remain full.
PR scans use the base commit's configuration and extensions; extensions must
stay within that tree. A changed `+.gitleaksignore+` fails the scan. PR scans
require Python 3.11 or newer to validate configuration paths.
A full ancestry checkout is still required: this version reduces scanning
work, not checkout history size. No speed claim is established without
runner measurements. The job deadline is 15 minutes; each main scan is
bounded to 180 seconds plus termination grace. Oversized audits fail visibly
rather than wait forever or silently pass.

Do not bulk-replace the old three-job standards bundle yet: this scanner
is not claimed to reproduce its specialised Rust/shell heuristic checks.
Keep those protections until parity controls or an owner-approved migration
exist. The real-binary CI controls and a caller canary must pass before
production rollout; see link:docs/SECRET-SCANNER-ROLLOUT.adoc[rollout status].
35 changes: 35 additions & 0 deletions actions/secret-scan-check/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# SPDX-License-Identifier: MPL-2.0
name: Bounded secret scan
description: Redacted tree and history scan with pinned Gitleaks, live controls and explicit infrastructure failure
inputs:
path:
description: Complete subject repository checkout (no shallow history)
default: '.'
mode:
description: full (default) or security-owner-approved pr-range
default: full
base-sha:
description: Verified ancestor commit for pr-range
default: ''
head-sha:
description: Expected checked-out head commit for pr-range
default: ''
runs:
using: composite
steps:
- name: Install and execute the bounded scanner
shell: bash
env:
ACTION_DIR: ${{ github.action_path }}
SUBJECT: ${{ inputs.path }}
SCAN_MODE: ${{ inputs.mode }}
BASE_SHA: ${{ inputs.base-sha }}
HEAD_SHA: ${{ inputs.head-sha }}
PR_BASE: ${{ github.event.pull_request.base.sha }}
PR_HEAD: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail
bash "$ACTION_DIR/install.sh" "$RUNNER_TEMP/suite-gitleaks"
export GITLEAKS_BIN="$RUNNER_TEMP/suite-gitleaks/gitleaks"
export ESTATE_BASELINE="$ACTION_DIR/../../config/gitleaks/estate-baseline.toml"
bash "$ACTION_DIR/check.sh"
Loading
Loading