fix(ci): pass PR head ref as data, scope contents:write to publish job (PMS-500) #372

Merged
Claude-Run merged 1 commit from fix/PMS-500-harden-create-release-injection into main 2026-06-26 10:39:10 +02:00
Member

The create-release workflow interpolated attacker-controlled github.event.* values (the PR head ref and merged flag) directly into bash and Nushell command text, allowing shell/script injection in a PR-triggered job that held a contents:write token.

Pass those values through the env: mapping and reference them as $HEAD_REF / env.HEAD_REF so they are treated as data, not code. Split the workflow into a read-only gate job and a create-release publish job so contents:write is granted only to the step that actually publishes. The gate validates the head ref against a strict ^release/v[0-9.]+ pattern in a data context before any release runs, and always succeeds so the repo status badge stays green on non-release merges.

#PMS-500

The create-release workflow interpolated attacker-controlled github.event.* values (the PR head ref and merged flag) directly into bash and Nushell command text, allowing shell/script injection in a PR-triggered job that held a contents:write token. Pass those values through the env: mapping and reference them as $HEAD_REF / $env.HEAD_REF so they are treated as data, not code. Split the workflow into a read-only gate job and a create-release publish job so contents:write is granted only to the step that actually publishes. The gate validates the head ref against a strict ^release/v[0-9.]+$ pattern in a data context before any release runs, and always succeeds so the repo status badge stays green on non-release merges. #PMS-500
fix(ci): pass PR head ref as data, scope contents:write to publish job (PMS-500)
All checks were successful
E2E / Playwright against staging (pull_request) Successful in 24s
Check / fmt + clippy + build + tests (pull_request) Successful in 1m21s
Integration / integration tests (pull_request) Successful in 7m10s
Create release / Gate (release-branch merges only) (pull_request) Successful in 1s
Create release / Create release from merged PR (pull_request) Has been skipped
7cdf124b1d
The create-release workflow interpolated attacker-controlled github.event.* values (the PR head ref and merged flag) directly into bash and Nushell command text, allowing shell/script injection in a PR-triggered job that held a contents:write token.

Pass those values through the env: mapping and reference them as $HEAD_REF / $env.HEAD_REF so they are treated as data, not code. Split the workflow into a read-only gate job and a create-release publish job so contents:write is granted only to the step that actually publishes. The gate validates the head ref against a strict ^release/v[0-9.]+$ pattern in a data context before any release runs, and always succeeds so the repo status badge stays green on non-release merges.

#PMS-500
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-server!372
No description provided.