ci: prevent command injection in release workflow - #99
Conversation
Pass workflow_dispatch inputs to shell scripts through env instead of
${{ }} substitution, and validate the npm dist-tag alongside the version
in the first step so a malformed tag fails before install or publish.
@standard-server/aws-lambda
@standard-server/core
@standard-server/fastify
@standard-server/fetch
@standard-server/node
@standard-server/peer
@standard-server/shared
commit: |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Merging this PR will not alter performance
Comparing Footnotes
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
Reviewed the single-file change to .github/workflows/release.yaml (initial review) — a CI security hardening fix.
- Env-var migration for dispatch inputs —
versionandtagnow reach scripts only throughenv:(VERSION,TAG_INPUT); no${{ }}interpolation remains in anyrun:body, which removes the injection vector that reached the npm-publishing job. - Up-front version and tag validation — the new first step validates
versionagainst the semver regex, rejects alatestpreid, resolves the dist-tag, and validates it against^[a-z][a-z0-9._-]*$before checkout, install, bump, or publish. - Boolean skip flags —
skip_bump/skip_changelogconditions changed to!inputs.skip_bump/!inputs.skip_changelog, which preserves behavior for typed boolean inputs including when omitted. - Publish step — uses the validated
--tag="$TAG"and$NO_GIT_CHECKS, both supplied viaenv.
Verified that the validated tag cannot inject extra $GITHUB_OUTPUT lines (the regex excludes = and newlines), that steps.version.outputs.tag can only carry a validated value, and that the dist-tag fallback ${TAG_INPUT:-${PREID:-latest}} is equivalent to the prior || chain and always non-empty. The one remaining raw interpolation of inputs.version is run-name, which is display-only and not an execution context.
DeepSeek Flash (default — pick a model for stronger reviews) | 𝕏

The release workflow substituted its
workflow_dispatchinputs (version,tag) directly into shell scripts, so a crafted value could run arbitrary commands in the job that publishes to npm with provenance (id-token: write). The version check didn't help, because it ran after the substituted line had already executed. Inputs now reach the scripts only as environment variables, and both the version and the dist-tag are validated in the first step, before anything is installed, bumped or published.Fixes
versionortaginputs no longer execute: no${{ }}expression remains inside anyrun:scriptx --registry=…) now fail the run immediately instead of reachingpnpm publisha-z,0-9,.,_or-Testing
act, with bump/changelog/publish commands replaced by argument printers. Valid inputs (stable, prerelease, explicit tag, every skip flag on/off) produce identical commands; payloads that executed on the original are rejected in the first step$NO_GIT_CHECKS, so an empty value adds no argument