fix(signing): require active tag rulesets for release tags in the verifier

Merged
QN
1d ago
+393-45

User description

Summary

Part of #1206. Step 1 of 2: make the release-tag verifier independent of github.ref_protected.

Both release workflows require github.ref_protected == true on the v* tag push. GitHub's variables reference says that is true when rulesets apply to the ref, but community reports (discussion 142985, discussion 66588) describe it staying false for tag pushes. If that is the case on the real tag push, both verifier jobs are skipped and nothing publishes. Because release-tags-immutable has no bypass actor, the pushed tag could then not be deleted or moved, so the version would be unusable. No v* tag was created to probe this.

What changes

  • verifyRemoteTag now reads the repository's active tag rulesets (GET /rulesets?targets=tag, then one detail per active repository ruleset) and requires that creation, update, deletion and non_fast_forward are all covered for refs/tags/<tag>. Coverage may be split across rulesets.
  • It fails closed on: no ruleset, a disabled or evaluate-only ruleset, a non-tag target, an inherited ruleset whose rules cannot be read, a different include pattern, an excluded ref, unknown pattern syntax (a pattern outside a safe subset never matches an include and always counts as an exclude), a failed list request, and a list of 100 or more entries that may be truncated.
  • The check runs after the signature and protected-main ancestry checks and before the final tag ref re-read.
  • refPatternMatches is exported and tested directly.

Evidence

  • Live, read-only and unauthenticated: the real signed tag v1.29.2 passes the full verifyRemoteTag path, including the new ruleset coverage for refs/tags/v1.29.2. This also shows the check works with a low-privilege caller, which is what the workflow token is.
  • verifyRemoteTag (44) and signing (72) tests pass locally, including 11 new rejection cases, a split-ruleset acceptance case and the pattern table.

Known limit

The API hides bypass_actors from callers without admin rights, so this check cannot prove that no actor can bypass release-tags-immutable. That stays a setting a maintainer verifies with an authenticated read (done: no bypass actors on ruleset 26812769). It is documented in docs/VERIFIED-SIGNING.md.

Next step

The workflows pin the verifier to 147470682b62, so they do not run this code yet. After this merges, a second small PR moves the pin in docker.yml, tauri-build.yml, scripts/release-security-gate.mjs and the gate test to the resulting protected-main commit and drops the github.ref_protected condition. Until then the workflows keep their current conditions.

Release boundary

No tag, GitHub Release, registry push or Vercel mutation. This PR does not authorize v1.29.3.

Summary by Sourcery

Harden release-tag verification by validating complete active ruleset protection directly through the GitHub rulesets API.

Bug Fixes:

  • Require release-tag verification to confirm that active repository tag rulesets fully protect the pushed tag, rather than relying on unreliable protected-ref status.

Enhancements:

  • Fail closed when tag ruleset coverage is missing, inactive, unsupported, excluded, unreadable, or potentially truncated, while allowing protection to be distributed across multiple rulesets.

Documentation:

  • Document active tag ruleset verification and the limitation that bypass-actor validation requires an authenticated maintainer check.

Tests:

  • Add coverage for ruleset protection scenarios, safe ref-pattern matching, split ruleset coverage, and fail-closed rejection cases.

Chores:

  • Update the changelog and README test-count metrics.

Summary by cubic

Makes release-tag verification depend on active tag rulesets read directly from the rulesets API rather than github.ref_protected, which community reports say stays false for tag pushes. The verifier now requires creation, update, deletion and non-fast-forward coverage for the pushed ref and fails closed on disabled, evaluate-only, mis-targeted, excluded, or unreadable rulesets.

  • Coverage may be split across several active repository rulesets.
  • bypass_actors remains visible only to admins, so the no-bypass guarantee stays a maintainer's authenticated-read check, documented rather than enforced by the verifier.
  • Pattern syntax outside a supported safe subset, including double stars, is treated as unknown and never matches, so it cannot be used to grant or deny coverage. Matching uses an iterative glob so no pattern text reaches a regular expression.

Rollout

  • Workflows still pin the verifier to the prior commit, so release behavior is unchanged; a follow-up moves the pin and drops the github.ref_protected condition.

CodeAnt-AI Description

Require active tag rulesets for release verification

What Changed

  • Release-tag verification now checks active rulesets for the pushed tag instead of relying on GitHub’s protected-ref status.
  • Verification requires creation, update, deletion, and non-fast-forward protection; coverage can be split across rulesets. Missing, unreadable, or unsupported protection fails verification.

Impact

✅ Rejects release tags without full protection
✅ Accepts protection split across active rulesets
✅ Verifies tag protection when GitHub’s protected-ref status is unreliable

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Summary by CodeRabbit

  • Bug Fixes
    • Release tag verification now checks active repository rulesets to confirm the pushed tag is covered by rules for creation, updates, deletion, and non-fast-forward changes. Verification also requires the ruleset information to be fully retrieved.
  • Documentation
    • Updated the verified-signing policy to describe the tag ruleset checks and relevant visibility limitations.
    • Refreshed the README’s reported test count.