Skip to content

Security: Environment protection on the release jobs (PyPI / HACS / c4z) #44

Description

@caidurbin

What to build

Add GitHub deployment-environment protection to the three publish jobs so a compromised or modified publish workflow can't release unattended. A PyPI Trusted Publisher must be treated like a credential — anyone who can modify the publish workflow can publish — so gate each release job behind an environment with required reviewers and/or tag/branch restrictions.

Acceptance criteria

  • release-pypi.yml, release-hacs.yml, release-c4z.yml publish jobs each run under a protected environment:
  • Each environment has required reviewers and/or tag/branch restrictions
  • A release still succeeds end-to-end through the protected environment

⚠️ Do not check criterion 1 on the YAML change alone. Adding an environment: key provides zero protection until protection rules exist on that environment. GitHub auto-creates an environment on first reference with no rules, and the job runs immediately, ungated — no error, no deadlock. The key is security-inert without rules; the control is only real once rules are configured.

Blocked by

None - can start immediately


This was generated by AI during triage.

Implementation notes — sequencing & delegation (why this stays ready-for-human)

The ordering hazard. Because GitHub auto-creates a bare, unprotected environment on first reference, merging environment: <name> before the protection rules exist opens an ungated-release window: any tag pushed in the gap publishes with no gate — including the irreversible PyPI publish. That is precisely the exposure this issue exists to close. So the naive "add the YAML first, configure Settings later" split is unsafe and must not be done.

Name-coupling hazard. The environment name in YAML must match the configured environment exactly. On a mismatch (agent picks pypi, human later creates release-pypi with reviewers) GitHub silently keeps pointing the workflow at the unprotected auto-created environment — no error, ever. So the name (and the design question behind it — one shared release environment vs. three per-lane environments so reviewers can be scoped per package) must be decided by whoever owns the rules, first.

Why not fully delegable. Criterion 3 ("release succeeds end-to-end through the protected environment") can't be verified by an AFK agent without a human approver and a real (irreversible) release. Only two pieces are genuinely human-only: deciding who the required reviewers are (a policy call) and being a reviewer identity.

Delegation options, best first:

  1. Keep whole (recommended). Do it in one human sitting in Settings > Environments: create the environments, set rules (required reviewers and/or tag restrictions — v* / hacs-v* / c4-v*), then add the matching environment: keys to the three publish jobs. The YAML is a ~3-line diff (publish in release-pypi.yml, release in release-hacs.yml, build-and-release in release-c4z.yml) — not worth splitting out.
  2. If splitting, reverse the order. Human creates environments + rules first (fixing the names); then a ready-for-agent follow-up adds the matching environment: keys, gated on this issue (not run in parallel). No coupling hazard, no ungated window.
  3. Delegate the richer slice. GitHub environments and tag-restriction policies are creatable via gh api (PUT /repos/{owner}/{repo}/environments/{name}), so an agent with administration:write could create the environments, apply tag restrictions, and wire the YAML in one PR — leaving the human only "designate required reviewers + approve the first real release." This carves at the actual human/automation joint. (Tag restriction alone is a meaningful control, and criterion 2 accepts "and/or".)

Environment protection rules (required reviewers, wait timers, branch/tag policies) are free on public repos, so there is no licensing blocker to any of the above.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions