Skip to content

Make reusable-workflow publication a protected executable release boundary #19

Description

@forkwright

Finding

This repository is executable fleet infrastructure, but main is not currently a protected release boundary. The only source check runs on pull requests, downloads its validator without integrity verification, and performs syntax lint only. A direct push or a semantically unsafe reusable change can therefore become code executed in callers—with their granted token/secrets—without a protected, executable publication receipt.

Evidence

  • Re-querying GET /repos/forkwright/.github/branches/main/protection immediately before filing on 2026-07-21 returned HTTP 404, “Branch not protected.”
  • .github/workflows/actionlint.yml:5-8 triggers only on pull requests touching workflow files; it has no push/main verification path.
  • Lines 26-32 stream an actionlint release archive from curl directly into tar without a checksum, signature, or pinned artifact digest.
  • The only assertion at lines 33-34 is actionlint syntax over YAML. There is no fixture caller proving workflow_call contracts, emitted check names, permissions/secrets behavior, or adversarial cases such as the branch-prefix bypass in Authenticate release automation; never waive required gates by branch name #18.
  • README.md:18-62 instructs consumers to execute mutable @main; Kanon #2565 documents the resulting fleet-wide promotion/reproducibility boundary.
  • The README's manually typed input/pin/rollout tables are already stale (for example a nonexistent gate-attestation runner input and no hybrid-gate inventory), so documentation cannot serve as the release manifest.

Why this matters

A merge or direct push here changes privileged CI code across consumers without a consumer commit. Syntax validity does not prove security or behavior, and the validator download itself is outside the pinning policy the repository exists to centralize. One mistaken or compromised write can fan out through every caller before a controlled rollback or pin wave exists.

Required mechanism

  • Treat protection of main as an explicit operator-owned prerequisite: require reviewed pull requests and the publication checks; do not mutate repository authority silently.
  • Install actionlint from a pinned, checksum/signature-verified artifact or another reproducible package source.
  • Add executable fixture callers for each reusable contract, including least-permission/secret behavior, emitted check names, failure propagation, Dependabot/release automation, non-tip attestations, and spoofed automation identity.
  • Publish one release receipt naming the exact source commit, resolved action commits, test results, and reusable input/output schemas.
  • Have Kanon #2565 promote only that verified commit through a derived immutable pin wave; never make @main the release channel.
  • Generate README workflow/input/action/consumer inventory from parsed YAML plus the Kanon registry and reject drift.

Refs #18, forkwright/kanon#2293, forkwright/kanon#2565

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggithub_actionsPull requests that update GitHub Actions code

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions