Skip to content

feat(action): squabble consumer action — pinned sha256 + attestation - #128

Merged
hyperpolymath merged 1 commit into
mainfrom
feat/squabble-consumer-action
Oct 9, 2026
Merged

hyperpolymath merged 1 commit into
mainfrom
feat/squabble-consumer-action

Conversation

@hyperpolymath

@hyperpolymath hyperpolymath commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

Summary

Adds .github/actions/squabble, a composite action other workflows can use to install the squabble v0.1.0 release binary and, optionally, run it. It refuses any download whose sha256 is not the pinned digest, or whose build attestation does not trace to this repo's release.yml at v0.1.0 and its source commit. Nothing is executed until both checks pass.

This is the prerequisite for running squabble verify-satisfied from CI (the central board and the semantic-audit-check placeholders). Those still wait on the read-only GitHub App, because verify-satisfied needs Administration: read, which github.token cannot hold.

Changes

  • .github/actions/squabble/install.sh: download into a fresh dir → sha256 must equal 6cbeb457…026c → gh attestation verify --format json with --repo, --signer-workflow …/release.yml, --source-ref refs/tags/v0.1.0, --source-digest b854d17a…, --predicate-type https://slsa.dev/provenance/v1, --deny-self-hosted-runners. The JSON is then read back: it must be a non-empty array in which every result names the asset with the pinned digest. Only then: chmod, --version must be squabble 0.1.0, PATH and path output. JSON is used because the plain-text output has been seen empty with exit 0 off a TTY.
  • .github/actions/squabble/run.sh: args one per line, literal (no globbing or splitting), blank lines dropped. Exit code captured with || rc=$? and written as exit-code.
  • .github/actions/squabble/action.yml: KYAML (D280). Install always uses github.token; run mode uses the token input, so an App token can be passed for verify-satisfied.
  • tests/squabble_action_test.sh: 27 cases across five sections; see Testing.
  • .github/workflows/squabble-action.yml: block YAML, because this repo's linter greps ^permissions: and its governance pin predates 13b872c1. It has no uses: at all (git checkout in a run step, like lock-sync-gate.yml).
  • .github/workflows/actions.lock: '.github/workflows/squabble-action.yml': [].
  • .github/actions/squabble/README.adoc, CHANGELOG.adoc.

📌 New pins

  • Head SHA: 2edc515d3e23e69d017910d8e9146f5fd0e1ecc0
  • Asset squabble-x86_64-linux-musl sha256: 6cbeb4577d83ccf2dea3405a3afb0d34b7fee422af73f2113d413ea7edba026c (matches the release's own SHA256SUMS)
  • Source commit: b854d17abc0dce296719a349ebe856ff945cf03e (v0.1.0^{commit})
  • Signer workflow: hyperpolymath/cicd-squabbler/.github/workflows/release.yml, source ref refs/tags/v0.1.0
  • actions.lock: '.github/workflows/squabble-action.yml': []. No action uses: SHAs are added or changed anywhere.

RSR Quality Checklist

Required

  • Tests pass (just test or equivalent): just test not run, because this PR changes no Rust code. The new suite tests/squabble_action_test.sh ran 27/27, and the repo's workflow and lock gates pass (see Testing).
  • Code is formatted: kyaml-format.sh --check action.yml rc=0; the shell follows the repo's existing style.
  • Linter is clean (no new warnings or errors): shellcheck -x rc=0 on all three scripts; actionlint rc=0 on the new workflow. Repo-wide actionlint findings are all in pre-existing workflows.
  • No banned language patterns: bash, jq, yq and KYAML only.
  • No unsafe blocks without // SAFETY: comments: not applicable, no Rust touched.
  • No banned functions.
  • SPDX license headers present on all new/modified source files: MPL-2.0 on the scripts, action.yml and workflow; CC-BY-SA-4.0 on README.adoc.
  • No secrets, credentials, or .env files included.

As Applicable

  • STATE descriptile: not applicable; no descriptile is edited (A2ML is retired estate-wide and is not written to).
  • ECOSYSTEM descriptile: not applicable, for the same reason.
  • META descriptile: not applicable, for the same reason.
  • Documentation updated for user-facing changes: .github/actions/squabble/README.adoc covers usage, what is checked, the pins, how to bump, and what is not yet tested.
  • TOPOLOGY.md updated: not applicable, no architecture change.
  • CHANGELOG updated: Unreleased → Added.
  • New dependencies reviewed for license compatibility: not applicable, no new dependencies. The action uses gh, jq and yq, which are preinstalled on ubuntu-latest.
  • ABI/FFI changes validated: not applicable.

Testing

Run locally on head 2edc515:

  • bash tests/squabble_action_test.sh → 27 passed, 0 failed, 27 of 27 planned cases ran. The five sections:
    • 0. action.yml contract: action.yml runs exactly install.sh and then run.sh (only when args is set), with exactly the tested env and no uses:. Four planted mutants are each refused, naming the mismatch: another script, an extra uses: step, github.token swapped into run mode, the run step's if: removed. A further case checks that both scripts are executable.
    • 1. JSON check, offline: the real shape and two good results pass. Refused: empty array, wrong digest, asset absent, one bad result among good ones, an object, an empty file.
    • 2. run.sh against a stand-in binary: blank lines dropped, a line with *, $HOME and backticks passed literally, empty args, non-zero exit passed through and recorded, missing binary → rc 1 with nothing recorded.
    • 3. Real asset vs. one-byte tampered copy: sha256 pass and refuse; attestation pass and refuse (HTTP 404 for the tampered digest).
    • 4. install.sh end to end: with gh release download shimmed to serve the tampered copy, it is refused and writes nothing to GITHUB_PATH or GITHUB_OUTPUT. The real download installs and records both.
  • Mutation check: each mutant was syntax-checked, run on a scratch copy, and killed by exactly its own cases.
    • verify_digest mismatch → return 0 turns only "tampered copy: sha256" red. The end-to-end tampered install is still refused by the attestation.
    • Dropping length > 0 turns only "empty array" red.
    • Keeping blank lines in run.sh turns the split and empty-args cases red.
  • Repo gates:
    • scripts/check-lock-sync.sh rc=0; gh actions-lock --no-fix (v0.1.6) rc=0.
    • tests/workflows/validate_workflows_test.sh passed with 0 errors. Its 4 warnings are the pre-existing missing npm-bun-blocker.yml and ts-blocker.yml.
    • scripts/check-root-shape.sh rc=0.
    • The workflow-linter.yml SPDX and ^permissions: checks pass; the new workflow adds 0 lines to its unpinned-uses: list.
    • standards/.githooks/docstring-scan.sh --worktree --check: 24/24 functions documented.
  • scripts/validate-template.sh fails on main already, for files this PR does not touch: npm-bun-blocker.yml, ts-blocker.yml and CONTRIBUTING.adoc.

CI on this head (2edc515)

  • Squabble Consumer Action (run 37924918914): green on ubuntu-latest. The suite ran 27 of 27 planned cases. The real install was verified by sha256 and attestation (installed squabble 0.1.0 … attestation verified), and squabble resolved on PATH in a later step. Run mode returned exit-code 0 for --version, and 2 with outcome failure for --no-such-flag.
  • Required context scan / gitleaks: success. It is the only required context. Effective rules on main are deletion, non_fast_forward and required_status_checks.
  • lint-workflows (Workflow Security Linter, step "Check SHA-Pinned Actions"): red, and pre-existing. It is red on main at b854d17 too, failing on the same list of 31 tag-pinned refs plus 5 actions.lock lines. squabble-action.yml adds no line to that list, because it contains no uses:.
  • Codeac: green; it reports "1 errors and 3 warnings", the same counts as on main at b854d17.
  • 🟡 CHECK: Standards pipeline: startup_failure, which is pre-existing. 27 of its 30 runs since 2026-09-22 ended this way. A startup failure creates no check-run, so it is absent from this PR's rollup. It is logged for the owner in the findings inbox, not fixed here.

Not covered here: GitHub's own evaluation of action.yml, meaning composite inputs and outputs, the run step's if:, and whether exit-code is readable when the run step fails. This repo has an actions.lock, and gh actions-lock --no-fix refuses local-path actions ("workflow uses local path actions which are not supported"), so the workflow cannot call uses: ./.github/actions/squabble. Instead it runs the two scripts exactly as the composite's steps do, and section 0 pins action.yml to that wiring. GitHub's evaluation is first exercised by a caller that pins hyperpolymath/cicd-squabbler/.github/actions/squabble@<merged main SHA>, which the lockfile does support. That caller must assert exit-code on a failing run.

Screenshots

Not applicable.

🤖 Generated with Claude Code

https://claude.ai/code/session_015bTuGfwCcvjrmNFejydTML

Add .github/actions/squabble, a composite action that installs the
squabble v0.1.0 release binary for other workflows and optionally runs it.

install.sh downloads the asset into a fresh directory and refuses it
unless (1) its sha256 equals the pinned digest and (2) gh attestation
verify --format json accepts it for this repo's release.yml at
refs/tags/v0.1.0, source commit b854d17, SLSA provenance v1, no
self-hosted runner, and the JSON names the asset with that digest.
Only then is it made executable, version-checked and put on PATH.
run.sh passes args one per line, literally, and records exit-code.

tests/squabble_action_test.sh (27 cases) checks action.yml runs exactly
the tested scripts and env (4 planted mutants refused), the JSON check
offline, run.sh against a stand-in binary, digest and attestation
against the real asset and a one-byte tampered copy, and install.sh end
to end with a shimmed tampered download (refused, PATH untouched).

The new squabble-action.yml workflow runs the suite and then the two
scripts as the composite does. It has no uses: at all: actions.lock
does not support local-path actions (gh actions-lock --no-fix refuses
`uses: ./`), so GitHub's evaluation of action.yml itself is first
exercised by a caller pinning a merged main SHA.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015bTuGfwCcvjrmNFejydTML
@coderabbitai

coderabbitai Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: ee5a28e5-1c8b-4aa3-8d96-53e99a246b8c

📥 Commits

Reviewing files that changed from the base of the PR and between b854d17 and 2edc515.


⛔ Files ignored due to path filters (1)
  • .github/workflows/actions.lock is excluded by !**/*.lock

📒 Files selected for processing (7)
  • .github/actions/squabble/README.adoc
  • .github/actions/squabble/action.yml
  • .github/actions/squabble/install.sh
  • .github/actions/squabble/run.sh
  • .github/workflows/squabble-action.yml
  • CHANGELOG.adoc
  • tests/squabble_action_test.sh

 __________________________________________________________________________
< Mirror, mirror on the wall, who's the best AI code reviewer of them all? >
 --------------------------------------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR


  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

sonarqubecloud Bot commented Oct 9, 2026

Copy link
Copy Markdown

@hyperpolymath
hyperpolymath merged commit 651ec38 into main Oct 9, 2026
53 of 56 checks passed
@hyperpolymath
hyperpolymath deleted the feat/squabble-consumer-action branch October 9, 2026 11:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant