Skip to content

Attest the tagged workbook on the draft release - #225

Merged
ryanduguid merged 3 commits into
mainfrom
ryan/ozzit-release-attestation
Oct 19, 2026
Merged

ryanduguid merged 3 commits into
mainfrom
ryan/ozzit-release-attestation

Conversation

@ryanduguid

Copy link
Copy Markdown
Owner

What this changes

Adds a manual Attest release workflow. It signs build provenance for the tagged ozzit.xlsx and adds the bundle, ozzit-X.Y.Z.intoto.jsonl, to the draft release before it is published. RELEASING.md places the step.

gh workflow run attest-release.yml --ref vX.Y.Z
  checks   required_checks.py: verify.yml verify-gates succeeded on main for the tag commit
  attest   tag is a verified, signed, annotated vX.Y.Z on this commit
           exactly one draft release holding SHA256SUMS, ozzit.xlsx and provenance.json, digests checked
           prepare_release_bundle.py verify + the draft workbook equals git show <tag>:ozzit.xlsx
           actions/attest on the tagged workbook, then gh attestation verify (API and bundle)
           upload to the verified release ID only if the draft, its assets and the tag are unchanged

Why

OpenSSF Scorecard scores Ozzit 0/10 on Signed-Releases, because releases ship a custom provenance.json that Scorecard does not recognise. A Sigstore *.intoto.jsonl bundle on the release is what it counts. The workbook is built and gated with native Excel on Windows, so CI cannot rebuild it. Instead this attests the tracked workbook at the signed tag, after checking that the draft's copy is byte-identical. No computed number, rate or threshold changes.

Verification

python -m ruff check .                                  # All checks passed!
python -m mypy --config-file mypy.ini                    # Success: no issues found in 34 source files
python -m unittest discover -s tools/tests               # Ran 679 tests, OK (skipped=1)
actionlint .github/workflows/attest-release.yml          # clean

tools/tests/test_repository_policy.py gains tests for the new workflow's policy: its trigger, permissions, verification steps, upload guards and the RELEASING.md step. Rust gates are unaffected: no file under tools/ozzit changes.

Final review

  • Reviewed head commit: 92bb2cb
  • Review type (self-review or independent): a Calvino review of the original commits, then an independent agent review of the rebased branch from a separate session (not a human review)
  • Review record and scope: the full workflow, the RELEASING.md step and the policy tests, after rebasing onto Port the workbook tools to a Rust binary and run CI through it #224 (Rust workbook tools)
  • Findings and disposition: Calvino requested changes and raised 5 findings. All were accepted and fixed in 60dc4a2 ("Bind the attestation upload to the verified draft and signed tag name"), and one hypothesis was left open in that review. The rebased review found one more issue, fixed in 92bb2cb: both jobs ran on ubuntu-latest, which GitHub is moving to Ubuntu 26, so they now pin ubuntu-24.04, as release-policy's release jobs do.
  • Provider results and coverage gaps: CI had not run when this was written
  • Remaining risks and unrun checks: the workflow has never run on GitHub, so the signing and upload paths are unproven until the next release's draft. The checks job uses release-policy a601f1d, the commit Ozzit's VirusTotal workflow already pins. Door: two-way; the workflow only runs when dispatched by hand. Blast radius: the next release's draft.

Checklist

  • No client, taxpayer, employee or payroll data, and no credentials, tokens, tenant IDs or .env contents, appear in the diff, the commit messages or this description.
  • The change is single-purpose.
  • New or changed behaviour has a test that fails without the change.
  • Documentation, README and any docstring that describes the changed rule were updated together with it.
  • Any new dependency is justified in the description, or there is none.

OpenSSF Scorecard gives Signed-Releases 0/10 because releases carry only the
custom provenance.json. attest-release.yml runs on the signed tag while the
release is a draft. It requires a verified annotated tag on the run's commit,
a successful verify-gates push run on main for that commit (release-policy's
required_checks.py at the pin the VirusTotal workflow uses), exactly the three
bundle assets with matching API digests, a passing prepare_release_bundle.py
verify and a draft workbook byte-identical to the tagged one. It then attests
the tagged ozzit.xlsx with actions/attest v4.2.2, verifies the attestation
through the API and the bundle, and uploads ozzit-X.Y.Z.intoto.jsonl.

The bundle sits outside SHA256SUMS, as RELEASING.md now states. An immutable
release accepts no asset after publication, so v3.5.0 and earlier stay without
one.
From the final review of the attestation workflow:

- The signed tag's own name must equal the dispatched ref, so another ref
  pointing at a signed object cannot stand in for a different version.
- The draft's ID, tag name and three assets (ID, digest, size, state) are
  recorded when verified. Before upload the draft must still match and the
  tag must still name the verified object; the bundle is then uploaded to
  that release ID instead of resolving the tag again. After upload the three
  assets must be unchanged and the bundle the only addition.
- RELEASING.md makes immutable releases a prerequisite, since only immutable
  publication stops an upload racing a maintainer's publish; describes the
  required-check run as a trusted push or workflow_dispatch run of main;
  drops an incorrect checksum-cycle rationale; and gives explicit paths in
  the verifier's commands.
Release jobs elsewhere pin ubuntu-24.04 (release-policy #183) so the label's
move to Ubuntu 26 cannot change the runner mid-release.
@ryanduguid
ryanduguid enabled auto-merge (squash) October 19, 2026 23:50
@ryanduguid
ryanduguid merged commit 6e06926 into main Oct 19, 2026
19 checks passed
@ryanduguid
ryanduguid deleted the ryan/ozzit-release-attestation branch October 19, 2026 23:52
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