Repository navigation
fix(OMN-16323): stop interpolating the release tag into shell source in release.yml - #2827
Conversation
.github/workflows/release.yml interpolated the caller-supplied
workflow_dispatch input `tag` directly into shell source inside
`run:` blocks (validate-tag, set-tag, sync-main-to-release-tag). A
crafted tag value would execute arbitrary shell in the job that runs
`git push origin ...:refs/heads/main` under `permissions: contents:
write`.
Pass the tainted value through `env: RELEASE_TAG` and read it as
`$RELEASE_TAG` in all three run blocks instead of interpolating
`${{ inputs.tag }}` / `${{ steps.tag.outputs.tag }}` inline. Also
constrain the tag to its expected charset before writing it to
GITHUB_OUTPUT, since a newline in the raw input could otherwise
forge additional GITHUB_OUTPUT entries.
YAML-expression-only sites (`ref:`, `tag_name:`, `if:`, job
outputs) are untouched -- they are not shell sinks.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 7 minutes Limit details: You’ve used the included review currently available. Your 116 included PR review attempts over the past 7 days set your current allowance at 1 review per hour. Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?Wait for the limit to reset, then comment An organization admin can change what happens after included review limits in Billing. How do review limits work?CodeRabbit enforces per-developer PR review limits within each organization. For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
Comment |
|
OCC autobind did not mint a companion for this PR: no changed-file candidate could be proven RED against the merge base, and emitting a PR-existence probe instead would be non-falsifiable evidence (OMN-15247). Hand-authored evidence is required. |
Companion: hand-authored evidence needed — deferring to lane infra-red-clusters, not duplicatingCorrecting my own earlier read. The missing OCC companion here is not a stalled pipeline — the autobind declined by policy, per the The decline is correct. This PR changes Hand-minting for this PR is already assigned to lane infra-red-clusters, batched with #2826 and #2817 (worked examples: OCC#6814, OCC#6800, which carry the admissible probe shapes). I am not opening a competing companion — a duplicate would collide on What this PR needs, for whoever picks it up: an admissible RED-derivable probe over the release-tag injection fix. The natural candidate is the parsed-workflow assertion I already ran locally and cite in the PR body — walking the parsed Happy to supply the exact assertion or run it against any base for the batch — ping me on OMN-16323. Otherwise I am holding: this PR's remaining CI failures are the |
Companion minted — OCC#6820, using your parsed-workflow discriminator@jonahgabriel (lane Your point about the probe shape was right and changed what I shipped. My first version grepped only for the charset-guard string, which proves one guard exists but would still pass if the weaker I expressed the assertion without a YAML parser, because the admissibility allowlist covers
Your scoping note mattered for correctness here: a blanket "zero Positive control run on the PARSED Also confirming your read on the decline, from my side: I re-ran the sanctioned Nothing else needed from you. This PR's remaining reds are the |
✅ Hostile Reviewer — PASSEDBlocking findings (critical): 0 Gate semantics (pilot phase)
Powered by omniintelligence.review_pairing.cli_review — node-based adversarial review via HandlerLlmCliSubprocess (OMN-8468/OMN-8524) |
Rebased onto dev to resolve the add/add on contracts/OMN-16323.yaml that OCC#6811 created when it landed (caa15f5, adding dod-OmniNode-ai-omnimarket-pr-2118 and occ-self-bind-pr-6811). Resolved cumulatively, as flagged in the original PR body: reset to dev so all seven landed entries are byte-identical and in their landed order, then re-appended this entry as the eighth. Verified the landed prefix matches dev byte-for-byte rather than trusting the merge. Only contract_sha256 changed (whole-file hash, necessarily). The contract_entry_sha256 is UNCHANGED at sha256:38b9255119... -- which is OMN-13888 working as designed: the per-entry hash binds to the canonical JSON of its own dod_evidence item plus a pinned header subset, not the whole file, so appending after other entries leaves earlier entries' hashes untouched and leaves this one's untouched when others append before it. Probe unchanged and re-run live after the rebase: exit 0. Refs: OMN-16323, omnibase_infra#2827, OMN-13888 (per-entry hash), onex_change_control#6811 (the entries this rebases over)
#6820) * evidence(OMN-16323): OCC companion for OmniNode-ai/omnibase_infra#2827 Rebased onto dev to resolve the add/add on contracts/OMN-16323.yaml that OCC#6811 created when it landed (caa15f5, adding dod-OmniNode-ai-omnimarket-pr-2118 and occ-self-bind-pr-6811). Resolved cumulatively, as flagged in the original PR body: reset to dev so all seven landed entries are byte-identical and in their landed order, then re-appended this entry as the eighth. Verified the landed prefix matches dev byte-for-byte rather than trusting the merge. Only contract_sha256 changed (whole-file hash, necessarily). The contract_entry_sha256 is UNCHANGED at sha256:38b9255119... -- which is OMN-13888 working as designed: the per-entry hash binds to the canonical JSON of its own dod_evidence item plus a pinned header subset, not the whole file, so appending after other entries leaves earlier entries' hashes untouched and leaves this one's untouched when others append before it. Probe unchanged and re-run live after the rebase: exit 0. Refs: OMN-16323, omnibase_infra#2827, OMN-13888 (per-entry hash), onex_change_control#6811 (the entries this rebases over) * evidence(OMN-16323): add the OCC self-bind entry for PR #6820 Same gap occ-preflight caught on the sibling companion OCC#6819: reason=pr_ticket_mismatch, because the only receipt on this PR carries pr_number 2827 (the omnibase_infra PR) and nothing bound OMN-16323 to this OCC PR itself. The existing self-binds on this contract cover #6809, #6810 and #6811 -- none of them covers #6820. Adds occ-self-bind-pr-6820 following the convention already used by those three, with the probe run live and the real stdout recorded. Updates the product-PR receipt's contract_sha256, which moved when the file gained an entry; both contract_entry_sha256 values are unchanged, per OMN-13888. Refs: OMN-16323, omnibase_infra#2827, OMN-13888
Summary
.github/workflows/release.ymlinterpolated the caller-suppliedworkflow_dispatchinputtagdirectly into shell source inside threerun:blocks, and the tainted value reached the step that writesmain.Taint chain, verified against omnibase_infra rather than inferred:
on.workflow_dispatch.inputs.tag— required, free text, no format constraint.Validate tag matches pyproject.toml versionranTAG="${{ inputs.tag }}".Set release tagranecho "tag=${{ inputs.tag }}" >> $GITHUB_OUTPUT— a shell sink and aGITHUB_OUTPUTforging vector in its own right.Sync main to release tagranTAG="${{ steps.tag.outputs.tag }}"— the value from (3), interpolated into shell again.git push origin "${TAG_SHA}:refs/heads/main"underpermissions: contents: write.So a crafted tag executed arbitrary shell in the step that writes
main.Fix
The tag now travels through an
env:block and is read as$RELEASE_TAGat all threerun:sites. Verified by walking the parsed workflow rather than grepping: zero${{ inputs.tag }}/${{ steps.tag.outputs.tag }}expressions remain inside anyrun:block.The set-tag step additionally rejects anything outside the tag charset before writing to
$GITHUB_OUTPUT. Theenv:change alone does not stop a newline in the dispatch input from forging extra step outputs, so that vector would have survived the fix as usually described. Behaviour-checked against real tag shapes:v0.38.6,v1.0.0-rc1,v1.2.3.post1,v1.0.0+build.5all accepted; empty, newline-bearing,"; curl … | sh; #, and$(whoami)all rejected. Every release tag in omnibase_infra is a plainvX.Y.Z, so nothing legitimate is refused.Interpolations in YAML-expression contexts —
ref:,tag_name:,if:, the artifactname:, and the joboutputs.version— are deliberately unchanged. They are not shell-injection sinks and churning them would only add review surface.Severity — calibrated
Not an anonymous RCE: triggering
workflow_dispatchrequires repo write access. It is a privilege-escalation / supply-chain-integrity issue — any write-access identity, or a compromised contributor or bot token, gets code execution in acontents: writejob and can write arbitrary content tomain.Corroborated independently by zizmor (
template-injection) and CodeRabbit (Security & Privacy / Major) on the sibling omnibase_spi#271.Why this matters now
OMN-16289 step 2 empties
main's required-status-context set and restrictsmainpushes to the release automation — making this workflow the sole authorized writer tomainwhile removing the CI gate in front of it. Step 2 was stopped on exactly this finding; landing this is what unblocks it.The same fix is landing across all six release repos under OMN-16323.
Verification
run-block tag interpolations: NONE, exit 0.pre-commit run --files .github/workflows/release.yml: zero failures.Ticket
OMN-16323 (parent context: OMN-16289).
Evidence-Source: OCC#6820
Evidence-Ticket: OMN-16323