From 8c213d28ce34846d44f825afdab026309405cd44 Mon Sep 17 00:00:00 2001 From: leonard-tesnov Date: Mon, 14 Sep 2026 15:51:53 +0300 Subject: [PATCH] Pass release version and notes to shell via env, not interpolation The publish job interpolated ${{ steps.version_check.outputs.version }} and ${{ steps.notes.outputs.notes }} directly into `run:` bodies. Actions substitutes those textually before the shell runs, so the values are parsed as shell source rather than read as data. The version comes from lib/gocardless_pro/version.rb and the notes from CHANGELOG.md. Bind both through step-level `env:` so the shell reads them as data, and reject a version that is not plain semver at the point it is read. Passing the notes via `env:` also preserves their line breaks, which the previous inline assignment did not. Ref: SEC-11868 Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3f28e20..c25ce11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,6 +60,11 @@ jobs: GH_TOKEN: ${{ github.token }} run: | VERSION=$(ruby -r ./lib/gocardless_pro/version.rb -e 'puts GoCardlessPro::VERSION') + # Guard against a crafted version string reaching later shell commands. + if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then + echo "Refusing to release: '$VERSION' is not a valid semantic version" + exit 1 + fi echo "version=$VERSION" >> $GITHUB_OUTPUT if gh release view "v$VERSION" >/dev/null 2>&1; then echo "already_published=true" >> $GITHUB_OUTPUT @@ -80,8 +85,9 @@ jobs: - name: Extract changelog notes for this version id: notes if: steps.version_check.outputs.already_published == 'false' + env: + VERSION: ${{ steps.version_check.outputs.version }} run: | - VERSION="${{ steps.version_check.outputs.version }}" NOTES="" if [ -f CHANGELOG.md ]; then NOTES=$(awk -v ver="$VERSION" ' @@ -100,9 +106,10 @@ jobs: if: steps.version_check.outputs.already_published == 'false' env: GH_TOKEN: ${{ github.token }} + VERSION: ${{ steps.version_check.outputs.version }} + NOTES: ${{ steps.notes.outputs.notes }} run: | - NOTES="${{ steps.notes.outputs.notes }}" - TAG="v${{ steps.version_check.outputs.version }}" + TAG="v${VERSION}" if [ -n "$(echo "$NOTES" | tr -d '[:space:]')" ]; then gh release create "$TAG" --title "$TAG" --notes "$NOTES" else