fix(security): enforce the privileged-event guard for caller-supplied refs - #62
Merged
Conversation
…vents
private-static.yml and cross-platform-smoke.yml are the only reusables that
let the caller choose the checked-out commit. That is safe on pull_request
and unsafe on pull_request_target or workflow_run, where the job holds the
caller's write token and secrets. The rule lived only in a risk note and in
three dismissed code-scanning alerts, so nothing enforced it.
A reusable inherits the caller's github context, so github.event_name is the
calling run's event. Both workflows now open with a fail-closed guard that
refuses a non-empty checkout_ref on a privileged event, with no opt-out input
and env indirection instead of ${{ }} in run:.
check_privileged_ref_guard.py discovers every workflow exposing checkout_ref,
asserts the guard is the first step, and executes the extracted guard body
across the privileged/safe event matrix.
Signed-off-by: Danil Silantyev <danilsilantyevwork@gmail.com>
.github/rulesets/branch-main.json declares allowed_merge_methods: ["merge"] and the live settings agree (allow_squash_merge and allow_rebase_merge are both false), matching the estate-wide atomic-commit policy. README, SECURITY, AGENTS, .claude/CLAUDE.md, docs/08-governance-rulesets.md and the nddev-change-flow / nddev-release-flow skills all claimed squash-only, which pointed every agent at a merge method the repository rejects. Signed-off-by: Danil Silantyev <danilsilantyevwork@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two changes, both closing gaps between what this repository claims and what it enforces.
checkout_refhad no enforced safety rule.private-static.ymlandcross-platform-smoke.ymlare the only reusables that let the caller pick the checked-out commit. That is correct onpull_request(read-only token, no secrets) and unsafe onpull_request_target/workflow_run, where the job holds the caller's write token and secrets. The rule existed only as a sentence in the catalog risk notes and in three dismissed code-scanning alerts (chore: ignore local .serena/ Serena MCP config #1–fix(release-supply-chain): migrate SBOM attestation to actions/attest #3) that resurrected as fix(workflows): enforce private-free action boundary #4–feat(release-supply-chain)!: close release artifact boundary #6 when the files were next edited — a triage loop, not a control.Eight files described a merge method the repository rejects. Corrected to match the declared ruleset and live settings.
Type of change
Threat-model note
A reusable workflow inherits the caller's
githubcontext, sogithub.event_nameis the event that triggered the calling run (contexts reference). Both workflows now open with a guard step that fails closed when a privileged event (pull_request_target,workflow_run,issue_comment,issues,discussion,discussion_comment) supplies a non-emptycheckout_ref.Properties, chosen to avoid the usual ways such a control fails open:
CALLER_EVENT/CHECKOUT_REF, no${{ }}insiderun:;shell: bash— identical behaviour on the Linux/macOS/Windows matrix, and no dependency onpython3being on PATH beforesetup-python;checkout_refstill legitimately checks out the base repository.This reduces attack surface and introduces none: the guard only refuses combinations that were already documented as unsupported.
Permissions diff
None. No job's
permissions:block changes; top-level stayspermissions: {}in both files.New enforcement
scripts/check_privileged_ref_guard.py, wired intovalidate_allasprivileged-ref-guard, discovers every workflow exposingcheckout_ref, asserts the guard is the first step of every checking-out job, and then executes the extracted guard body against the full privileged/safe event matrix. A future reusable that addscheckout_refwithout the guard fails the gate. Verified it is not vacuous: a stub guard that always exits 0 is reported as a failure.Docs correction
.github/rulesets/branch-main.jsondeclares"allowed_merge_methods": ["merge"], and the live settings agree —allow_squash_merge: false,allow_rebase_merge: false,allow_merge_commit: true, matching the estate-wide atomic-commit policy.README.md,SECURITY.md,AGENTS.md,.claude/CLAUDE.md,docs/08-governance-rulesets.md, and thenddev-change-flow/nddev-release-flowskills all said squash-only. Corrected in the authored sources and remirrored viasync_skills.py.Checklist
permissions(top-level{}, per-job minimal)timeout-minutespresent on changed jobspersist-credentials: falseunchanged${{ }}insiderun:actionlintpasses on both changed files (exit=0)zizmor --persona regular --min-severity low— "No findings to report"catalog/capabilities.ymlrisk notes +docs/02-private-free.mdupdatedCHANGELOG.mdupdated under[Unreleased]-S) and signed off (-s), Conventional Commitspython3 scripts/validate_all.py→ OK (all 19 checks)Tier impact
Both workflows are
unverifiedincatalog/runtime-coverage.yml, so noproven_digestre-proof is required.