Repository navigation
ci: validate published scanner on representative consumer snapshots #65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d8ab4bc
768f6d3
0f0500f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| # SPDX-License-Identifier: MPL-2.0 | ||
| # Read-only corpus audits, NOT consumer deployment or consumer PR validation. | ||
| # Execute only pinned suite scripts; never execute code from a subject repo. | ||
| name: Secret scanner consumer canaries | ||
| on: | ||
| pull_request: | ||
| paths: | ||
| - '.github/workflows/secret-scanner-consumer-canaries.yml' | ||
| - 'config/gitleaks/consumer-canaries.json' | ||
| permissions: | ||
| contents: read | ||
| concurrency: | ||
| group: scanner-consumer-canaries-${{ github.ref }} | ||
| cancel-in-progress: true | ||
| jobs: | ||
| plan: | ||
| runs-on: ubuntu-24.04 | ||
| timeout-minutes: 5 | ||
| outputs: | ||
| subjects: ${{ steps.plan.outputs.subjects }} | ||
| suite_sha: ${{ steps.plan.outputs.suite_sha }} | ||
| steps: | ||
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| persist-credentials: false | ||
| - name: Validate bounded immutable manifest and published suite pin | ||
| id: plan | ||
| shell: bash | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| run: | | ||
| set -euo pipefail | ||
| manifest=config/gitleaks/consumer-canaries.json | ||
| jq -e ' | ||
| .schema_version == 1 and | ||
| (.suite_sha | test("^[0-9a-f]{40}$")) and | ||
| (.subjects | length >= 1 and length <= 5) and | ||
| all(.subjects[]; | ||
| (.repository | test("^hyperpolymath/[A-Za-z0-9_.-]+$")) and | ||
| (.sha | test("^[0-9a-f]{40}$"))) | ||
| ' "$manifest" >/dev/null | ||
| sha=$(jq -r .suite_sha "$manifest") | ||
| bash scripts/verify-published-scanner-pin.sh "$sha" | ||
| echo "suite_sha=$sha" >> "$GITHUB_OUTPUT" | ||
| echo "subjects=$(jq -c .subjects "$manifest")" >> "$GITHUB_OUTPUT" | ||
| audit: | ||
| needs: plan | ||
| name: Audit ${{ matrix.subject.repository }} | ||
| runs-on: ubuntu-24.04 | ||
| timeout-minutes: 15 | ||
| strategy: | ||
| fail-fast: false | ||
| max-parallel: 3 | ||
| matrix: | ||
| subject: ${{ fromJSON(needs.plan.outputs.subjects) }} | ||
| steps: | ||
| - name: Check out immutable published subject (read only) | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| repository: ${{ matrix.subject.repository }} | ||
| ref: ${{ matrix.subject.sha }} | ||
| path: subject | ||
| fetch-depth: 0 | ||
| persist-credentials: false | ||
| - name: Check out published scanner implementation outside subject | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| repository: hyperpolymath/cicd-suite | ||
| ref: ${{ needs.plan.outputs.suite_sha }} | ||
| path: suite | ||
| persist-credentials: false | ||
| - name: Verify both checkouts and install pinned scanner | ||
| shell: bash | ||
| env: | ||
| SUBJECT_SHA: ${{ matrix.subject.sha }} | ||
| SUITE_SHA: ${{ needs.plan.outputs.suite_sha }} | ||
| run: | | ||
| set -euo pipefail | ||
| [[ $(git -C subject rev-parse HEAD) == "$SUBJECT_SHA" ]] || exit 2 | ||
| [[ $(git -C suite rev-parse HEAD) == "$SUITE_SHA" ]] || exit 2 | ||
| bash suite/actions/secret-scan-check/install.sh "$RUNNER_TEMP/canary-gitleaks" | ||
| - name: Audit full history, tree and supplemental populations | ||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
|
||
| shell: bash | ||
| env: | ||
| SUBJECT: ${{ github.workspace }}/subject | ||
| GITLEAKS_BIN: ${{ runner.temp }}/canary-gitleaks/gitleaks | ||
| ESTATE_BASELINE: ${{ github.workspace }}/suite/config/gitleaks/estate-baseline.toml | ||
| SCAN_MODE: full | ||
| SCAN_TIMEOUT_SECONDS: '180' | ||
| run: | | ||
| set -euo pipefail | ||
| # This is an offline audit of a published consumer default-branch | ||
| # snapshot, not a scan of the cicd-suite PR base/head. Clear that | ||
| # unrelated caller event context for this child process only. | ||
| # Scanner controls and all finding/infrastructure failures enforce. | ||
| env -u GITHUB_EVENT_NAME -u PR_BASE -u PR_HEAD \ | ||
| bash suite/actions/secret-scan-check/check.sh | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| { | ||
| "schema_version": 1, | ||
| "suite_sha": "41f8a325cf5ceae0f8ce213f991c86b95c987f9b", | ||
| "subjects": [ | ||
| { | ||
| "repository": "hyperpolymath/unified-dataset-vocab", | ||
| "sha": "12acc7881e12ffb52c9146dea7f6d43fce0c13e3", | ||
| "profile": "documentation and vocabulary", | ||
| "default_branch": "main" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Verify that each subject SHA belongs to its declared default branch. If a manifest edit pins a commit from another branch, the plan accepts it and the audit runs. The result would then be labelled as a default-branch snapshot without testing that snapshot. Check ancestry against
|
||
| }, | ||
| { | ||
| "repository": "hyperpolymath/idrisiser", | ||
| "sha": "c630c024c9be7852a42a6699f2394d10af0ee71b", | ||
| "profile": "Rust and shell scaffolding", | ||
| "default_branch": "main" | ||
| }, | ||
| { | ||
| "repository": "hyperpolymath/gitbot-fleet", | ||
| "sha": "83e35bcfd04d43960d635fec5397da8dfa344268", | ||
| "profile": "larger Rust estate tooling with local Gitleaks exceptions", | ||
| "default_branch": "main" | ||
| } | ||
| ] | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| #!/usr/bin/env bash | ||
| # SPDX-License-Identifier: MPL-2.0 | ||
| # Exercise the exact compatibility-job shell parsed from the workflow YAML. | ||
| set -euo pipefail | ||
| root=$(cd "$(dirname "$0")/.." && pwd) | ||
| command -v yq >/dev/null || { echo 'mikefarah yq v4 is required' >&2; exit 2; } | ||
| tmp=$(mktemp -d) | ||
| trap 'rm -rf "$tmp"' EXIT | ||
| program=$(yq -r '.jobs["supplemental-results"].steps[0].run' "$root/.github/workflows/secret-scanner-reusable.yml") | ||
| [[ -n $program && $program != null ]] || { echo 'receipt validator missing' >&2; exit 2; } | ||
| printf '%s\n' "$program" > "$tmp/validate.sh" | ||
| sha=0123456789abcdef0123456789abcdef01234567 | ||
| assert_receipt() { | ||
| local want=$1 label=$2 detector=$3 upstream=$4 actual=${5-$sha} expected=${6-$sha} rc=0 | ||
| DETECTOR_STATUS=$detector UPSTREAM_RESULT=$upstream SUBJECT_SHA=$actual EXPECTED_SHA=$expected \ | ||
| bash "$tmp/validate.sh" >"$tmp/output" 2>&1 || rc=$? | ||
| [[ $rc == "$want" ]] || { cat "$tmp/output"; echo "FAIL $label: wanted $want got $rc"; exit 1; } | ||
| echo "ok: $LANGUAGE $label" | ||
| } | ||
| for language in rust shell; do | ||
| export LANGUAGE=$language | ||
| assert_receipt 0 'completed passing detector' 0 success | ||
| assert_receipt 0 'passing detector despite another failed scan component' 0 failure | ||
| assert_receipt 1 'finding stays red' 1 failure | ||
| assert_receipt 2 'tool error stays non-passing' 2 failure | ||
| assert_receipt 2 'timeout stays non-passing' 124 failure | ||
| assert_receipt 2 'missing receipt is not green' '' success | ||
| assert_receipt 2 'cancelled upstream is not green' 0 cancelled | ||
| assert_receipt 2 'skipped upstream is not green' 0 skipped | ||
| assert_receipt 2 'wrong subject is not green' 0 success ffffffffffffffffffffffffffffffffffffffff | ||
| assert_receipt 2 'missing expected SHA is not green' 0 success "$sha" '' | ||
| done | ||
| LANGUAGE=unknown assert_receipt 2 'unknown detector is refused' 0 success |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Script executed:
Repository: hyperpolymath/cicd-suite
Length of output: 3198
Script executed:
Repository: hyperpolymath/cicd-suite
Length of output: 17340
Web query:
actions/checkout 3d3c42e5aac5ba805825da76410c181273ba90b1 v7.0.1 git-source-provider.ts fetchDepth 0 refSpecForAllHistoryResult:
Restrict the history audit to the pinned commit.
fetch-depth: 0fetches all branch and tag refs. The history pass then gives Gitleaks--all, so a commit on an unrelated ref can add findings or trigger the timeout without changing either manifest pin.Keep the checkout non-shallow, but retain only one ref at the pinned commit before the scan.
Suggested fix
[[ $(git -C subject rev-parse HEAD) == "$SUBJECT_SHA" ]] || exit 2 [[ $(git -C suite rev-parse HEAD) == "$SUITE_SHA" ]] || exit 2 + - name: Restrict subject history to the pinned commit + shell: bash + env: + SUBJECT_SHA: ${{ matrix.subject.sha }} + run: | + set -euo pipefail + git -C subject update-ref refs/heads/audit "$SUBJECT_SHA" + while IFS= read -r ref; do + [[ $ref == refs/heads/audit ]] || + git -C subject update-ref --no-deref -d "$ref" + done < <(git -C subject for-each-ref --format='%(refname)') bash suite/actions/secret-scan-check/install.sh "$RUNNER_TEMP/canary-gitleaks"