fix(ci): shell-free release candidate that keeps the tag out of script text - #59
Merged
Merged
Conversation
…t text The hseshadr/ci fleet scan reports shell-step (x2) and candidate-order on release-candidate.yml: fleet policy allows only checkout, Dagger, upload. Keep the tag-injection hardening inside the pinned dagger-for-github step instead of run: steps. The tag reaches the step only as the TAG env var, and args hold only double-quoted variables, so the action's bash expands it as one inert word. Dagger already rejects a requested tag that differs from the verified candidate's tag, so the shell regex guard is dropped. Tests now expand the real args in bash with hostile tags and assert that each arrives as one literal argument and runs nothing. publish.yml is intentionally unchanged: its lineage step is a real control that fleet policy cannot yet express (see hseshadr/ci). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015oBArfm762nN1r4F4Fst5a
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.
Claim touched: a dispatched release tag can never run code on the runner, and the release candidate follows the fleet's shell-free shape (checkout, Dagger, upload).
Why
The hseshadr/ci fleet scan (
scan_fleet(include_central=True), 2026-09-25) reports five findings onmain@ 57df579. This PR fixes the three onrelease-candidate.yml:The tag-injection hardening (7b3ab4d) was right that
--tag=${{ inputs.tag }}indagger-for-githubargswas injectable. Its fix moved the call intorun:steps, and fleet policy forbids those. The same property holds without shell steps.What
dagger/dagger-for-githubstep.env: TAG: ${{ inputs.tag }}, andargs: release-candidate --tag="$TAG" --commit-sha="$GITHUB_SHA" .... The action pastesargsinto its own bash script, so bash expands"$TAG"as a single literal word. The value is never parsed as code, and no${{ }}expression appears inargs.vX.Y.Zregex shell step is dropped.release_candidatein Dagger already rejects a requested tag that differs from the verified candidate's tag (_require_requested_tag, run throughEXPECTED_TAGinside the container, not shell text).tests/test_workflow_security.pyused to assert the shell shape: a guard step, an install-only action, and arun:release call. It now asserts the policy shape and norun:steps. The bash regex tests are replaced by a stronger test: it expands the real workflowargsin bash, the way the action does, with 8 hostile tags ($(touch pwned), backticks,;, a newline, a quote breakout, an extra flag). It asserts that each tag reaches Dagger as exactly one argument and that no file is created.This touches only
release-candidate.yml,tests/test_workflow_security.pyandCHANGELOG.md. The bump PR #58 touchesdagger.json,.dagger/src/edgeproc_core/main.pyand.dagger/tests/test_public_contracts.py, so the two do not overlap (git merge-treeis clean). They can merge in either order.Not changed:
publish.yml(2 findings, intentionally left)This step is a real control. It blocks a
workflow_dispatchon a tag namedmainfrom getting past thehead_branchgate. Fleet policy has no compliant way to express it, and the only Dagger identity the policy allows there is the candidate's own code. Removing the step would reopen the hole, so the policy has to change. That is tracked in hseshadr/ci#49.Evidence
At index 0 diff: 'None' != 'release-candidate')uv run poe gateexit 0: 361 passed, coverage 99.31%ci.fleet_policy.validate_workflow)shell-stepx2 andcandidate-order. New file: no findings--tag=$TAG(unquoted)--tag=${{ inputs.tag }}(the pre-0.5.0 bug)run:stepNot verified live: a real
workflow_dispatchrun. That needs a release tag, so it happens at the next release.🤖 Generated with Claude Code
https://claude.ai/code/session_015oBArfm762nN1r4F4Fst5a