From 250f806be8555785275e548a9671733b35b2f7c4 Mon Sep 17 00:00:00 2001 From: Matee ullah Malik Date: Thu, 2 Jul 2026 19:15:59 +0000 Subject: [PATCH] ci(release): pass tag message via env to prevent shell injection The 'Check deploy intent' step interpolated the multi-line tag message directly into a bash script via ${{ steps.tag_info.outputs.tag_message }}. When the tag body contains lines that look like shell commands (e.g. the literal token 'deadcode' from a merge-commit trailer), those lines are executed as bash. That is exactly what happened on v2.6.0-testnet and v2.6.0-rc1: the release job aborted with exit code 127 ("deadcode: command not found") before any artifacts were uploaded, leaving the GitHub Release with zero downloadable assets and sn-manager unable to roll the testnet fleet forward. Fix: expose the tag message through the step's `env:` block and read it as a shell variable inside the script. This is the GHA-recommended way to consume untrusted multi-line values. Verified locally with yaml.safe_load; runtime behavior is a strict subset of the previous logic (single grep, same output key). Root cause captured for future readers in the inline comment. Signed-off-by: Matee ullah Malik --- .github/workflows/build&release.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build&release.yml b/.github/workflows/build&release.yml index ba8665fc..1a8c14c0 100644 --- a/.github/workflows/build&release.yml +++ b/.github/workflows/build&release.yml @@ -74,13 +74,19 @@ jobs: - name: Check deploy intent id: deploy_check + env: + TAG_MSG: ${{ steps.tag_info.outputs.tag_message }} run: | - TAG_MSG="${{ steps.tag_info.outputs.tag_message }}" - if echo "$TAG_MSG" | grep -qi '\[no-deploy\]'; then - echo "draft=true" >> $GITHUB_OUTPUT + # NOTE: TAG_MSG is passed via env: (not ${{ }} inlined into the script) + # because the tag/commit message is multi-line and may contain shell + # metacharacters or command names (e.g. "deadcode"). Inlining it would + # let those lines execute as bash and abort the release job with exit + # 127. See build&release.yml history / v2.6.0-testnet post-mortem. + if printf '%s' "$TAG_MSG" | grep -qi '\[no-deploy\]'; then + echo "draft=true" >> "$GITHUB_OUTPUT" echo "⚠️ [no-deploy] detected — release will be created as DRAFT" else - echo "draft=false" >> $GITHUB_OUTPUT + echo "draft=false" >> "$GITHUB_OUTPUT" fi - name: Setup Go and dependencies