Skip to content

fix(security): harden run-steps against script injection; fix release gating#44

Draft
peschee wants to merge 2 commits into
mainfrom
fix/harden-run-step-injection
Draft

fix(security): harden run-steps against script injection; fix release gating#44
peschee wants to merge 2 commits into
mainfrom
fix/harden-run-step-injection

Conversation

@peschee

@peschee peschee commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Hardens every reusable workflow and composite action against shell script injection, and fixes two release-workflow correctness bugs found during the review.

The core change is mechanical and repo-wide: every github.*, inputs.*, secrets.*, and vars.* value that was interpolated directly into a run: shell body is now passed via a step-level env: block and referenced as a quoted shell variable. This is the standard mitigation for GitHub Actions expression injection.

Security

  • Script injection: all untrusted ${{ … }} moved out of run: bodies into env: (quoted refs) across every workflow + composite action.
  • Secrets off command lines: Nexus credentials (curl --user), macOS keychain password, skopeo --src/--dest-creds, and the Jira auth token are no longer interpolated into command strings.
  • add-helm-repositories: removed eval of a built-up command string; now uses a bash argument array.
  • setup-npm-nexus-access: removed the step that printed the generated .npmrc (auth token) to the build log.

Fixed

  • Release announce job was effectively never running: its condition was just inputs.send_announcement while it needs: several jobs that are skipped on a normal release, so it got skipped too. Now gated with always() && … && !contains(needs.*.result, 'failure'/'cancelled').
  • Release run summary referenced non-existent uppercase parse outputs (rendered blank); now uses the correct lowercase outputs.

Verification

  • actionlint clean across the tree (only the pre-existing macduff self-hosted-runner label warning).
  • Injection scan confirms no untrusted inputs.*/secrets.*/github.event.* remains in any run: body. Remaining ${{ }} occurrences are safe (with:/env: values, the announce message: hardened inside its action, and a fixed-literal ternary in shared-maven-build.yml).

Notes / not in this PR

  • This repo has no release/tag process — consumers ride @main, and package.json's 1.0.0 maps to no tag. The CHANGELOG entry is under [Unreleased]. Cutting an actual v1.0.0 (so this lands in a real release) is a separate decision.
  • Scope was deliberately limited to injection/secrets + the announce gating. Other review findings (missing permissions: blocks, concurrency groups, unpinned third-party actions, the orphaned shared-* vs legacy duplication, the dead non-dot github/ dir, README link errors) are not addressed here.

peschee added 2 commits June 15, 2026 15:47
… gating

Move every github.*, inputs.*, secrets.* and vars.* value that was
interpolated directly into a `run:` shell body into a step-level `env:`
block, referenced as a quoted shell variable. This closes script-injection
holes across all reusable workflows and composite actions and keeps
secrets (Nexus, macOS keychain, skopeo, Jira) off command lines.

Also:
- add-helm-repositories: drop `eval`, build a bash argument array instead
- setup-npm-nexus-access: stop printing the generated .npmrc auth token
- release announce job: add always() so it is not skipped when optional
  upstream jobs are skipped; suppress only on failure/cancellation
- release run summary: reference the correct lowercase parse outputs
- CHANGELOG: document the above under [Unreleased]

Verified with actionlint (clean) and an injection scan over all run: bodies.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant