From fe0d2b9acf05842ab03da14b03cc5f0d5b944984 Mon Sep 17 00:00:00 2001 From: rubydusa Date: Thu, 30 Apr 2026 13:10:55 -0400 Subject: [PATCH] ci: pass inputs to run blocks via env: instead of templating GitHub Actions templates ${{ inputs.X }} substitutions before the shell parses, so quoting in the script does not protect against values containing ", $, backticks, or newlines. Convert every input substitution inside run: blocks to the env: + quoted-shell-variable pattern across all four workflows. Behavior is unchanged for well-formed inputs; misbehaved inputs now fail closed in the shell rather than escaping out of templating. Closes #42 --- .github/workflows/_ci.yml | 24 +++++++--- .github/workflows/_deploy-testnet.yml | 22 ++++++--- .github/workflows/_foundry-cicd.yml | 66 ++++++++++++++++++--------- .github/workflows/_upgrade-safety.yml | 4 +- 4 files changed, 80 insertions(+), 36 deletions(-) diff --git a/.github/workflows/_ci.yml b/.github/workflows/_ci.yml index 145b2b4..6760953 100644 --- a/.github/workflows/_ci.yml +++ b/.github/workflows/_ci.yml @@ -88,8 +88,10 @@ jobs: - name: Install dependencies if: inputs.package-manager != 'none' + env: + PACKAGE_MANAGER: ${{ inputs.package-manager }} run: | - case "${{ inputs.package-manager }}" in + case "$PACKAGE_MANAGER" in npm) npm ci ;; yarn) yarn --frozen-lockfile ;; pnpm) corepack enable && pnpm install --frozen-lockfile ;; @@ -108,7 +110,8 @@ jobs: - name: Run tests env: RPC_URL: ${{ secrets.RPC_URL }} - run: forge test -${{ inputs.test-verbosity }} + TEST_VERBOSITY: ${{ inputs.test-verbosity }} + run: forge test "-$TEST_VERBOSITY" slither: name: Slither Analysis @@ -132,8 +135,10 @@ jobs: - name: Install dependencies if: inputs.package-manager != 'none' + env: + PACKAGE_MANAGER: ${{ inputs.package-manager }} run: | - case "${{ inputs.package-manager }}" in + case "$PACKAGE_MANAGER" in npm) npm ci ;; yarn) yarn --frozen-lockfile ;; pnpm) corepack enable && pnpm install --frozen-lockfile ;; @@ -179,8 +184,10 @@ jobs: - name: Install dependencies if: inputs.package-manager != 'none' + env: + PACKAGE_MANAGER: ${{ inputs.package-manager }} run: | - case "${{ inputs.package-manager }}" in + case "$PACKAGE_MANAGER" in npm) npm ci ;; yarn) yarn --frozen-lockfile ;; pnpm) corepack enable && pnpm install --frozen-lockfile ;; @@ -192,9 +199,10 @@ jobs: - name: Run coverage env: RPC_URL: ${{ secrets.RPC_URL }} + COVERAGE_EXCLUDE_PATHS: ${{ inputs.coverage-exclude-paths }} run: | - if [[ -n "${{ inputs.coverage-exclude-paths }}" ]]; then - forge coverage --no-match-path "${{ inputs.coverage-exclude-paths }}" 2>&1 | tee coverage-raw.txt + if [[ -n "$COVERAGE_EXCLUDE_PATHS" ]]; then + forge coverage --no-match-path "$COVERAGE_EXCLUDE_PATHS" 2>&1 | tee coverage-raw.txt else forge coverage 2>&1 | tee coverage-raw.txt fi @@ -243,8 +251,10 @@ jobs: - name: Install dependencies if: inputs.package-manager != 'none' + env: + PACKAGE_MANAGER: ${{ inputs.package-manager }} run: | - case "${{ inputs.package-manager }}" in + case "$PACKAGE_MANAGER" in npm) npm ci ;; yarn) yarn --frozen-lockfile ;; pnpm) corepack enable && pnpm install --frozen-lockfile ;; diff --git a/.github/workflows/_deploy-testnet.yml b/.github/workflows/_deploy-testnet.yml index 4299ec6..859884d 100644 --- a/.github/workflows/_deploy-testnet.yml +++ b/.github/workflows/_deploy-testnet.yml @@ -80,8 +80,10 @@ jobs: - name: Install dependencies if: inputs.package-manager != 'none' + env: + PACKAGE_MANAGER: ${{ inputs.package-manager }} run: | - case "${{ inputs.package-manager }}" in + case "$PACKAGE_MANAGER" in npm) npm ci ;; yarn) yarn --frozen-lockfile ;; pnpm) corepack enable && pnpm install --frozen-lockfile ;; @@ -89,27 +91,33 @@ jobs: - name: Read network config id: network + env: + NETWORK_INDEX: ${{ inputs.network-index }} + NETWORK_CONFIG_PATH: ${{ inputs.network-config-path }} run: | - BLOCKSCOUT_URL=$(jq -r '.testnets[${{ inputs.network-index }}].blockscout_url' ${{ inputs.network-config-path }}) - NETWORK_NAME=$(jq -r '.testnets[${{ inputs.network-index }}].name' ${{ inputs.network-config-path }}) - echo "blockscout_url=$BLOCKSCOUT_URL" >> $GITHUB_OUTPUT - echo "network_name=$NETWORK_NAME" >> $GITHUB_OUTPUT + BLOCKSCOUT_URL=$(jq -r ".testnets[$NETWORK_INDEX].blockscout_url" "$NETWORK_CONFIG_PATH") + NETWORK_NAME=$(jq -r ".testnets[$NETWORK_INDEX].name" "$NETWORK_CONFIG_PATH") + echo "blockscout_url=$BLOCKSCOUT_URL" >> "$GITHUB_OUTPUT" + echo "network_name=$NETWORK_NAME" >> "$GITHUB_OUTPUT" - name: Deploy contracts env: PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} RPC_URL: ${{ secrets.RPC_URL }} DEPLOY_ENV_VARS: ${{ secrets.DEPLOY_ENV_VARS }} + DEPLOY_SCRIPT: ${{ inputs.deploy-script }} run: | source .etherform/scripts/deploy/prepare-env.sh - forge script ${{ inputs.deploy-script }} \ + forge script "$DEPLOY_SCRIPT" \ --rpc-url "$RPC_URL" \ --broadcast \ --slow \ -vvvv - name: Wait for indexing - run: sleep ${{ inputs.indexing-wait }} + env: + INDEXING_WAIT: ${{ inputs.indexing-wait }} + run: sleep "$INDEXING_WAIT" - name: Parse deployment addresses id: parse diff --git a/.github/workflows/_foundry-cicd.yml b/.github/workflows/_foundry-cicd.yml index e88d73c..b7312fa 100644 --- a/.github/workflows/_foundry-cicd.yml +++ b/.github/workflows/_foundry-cicd.yml @@ -146,13 +146,17 @@ jobs: - name: Build paths filter id: paths + env: + CONTRACT_PATHS: ${{ inputs.contract-paths }} run: | # Convert newline-separated paths to YAML list format - YAML_PATHS=$(echo "${{ inputs.contract-paths }}" | sed '/^$/d' | sed 's/^/ - /') - echo "filter<> $GITHUB_OUTPUT - echo "contracts:" >> $GITHUB_OUTPUT - echo "$YAML_PATHS" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT + YAML_PATHS=$(echo "$CONTRACT_PATHS" | sed '/^$/d' | sed 's/^/ - /') + { + echo "filter<> "$GITHUB_OUTPUT" - name: Check for contract changes id: filter @@ -162,16 +166,19 @@ jobs: - name: Decide whether to run id: decide + env: + SKIP_IF_NO_CHANGES: ${{ inputs.skip-if-no-changes }} + CONTRACTS_CHANGED: ${{ steps.filter.outputs.contracts }} run: | - if [[ "${{ inputs.skip-if-no-changes }}" == "false" ]]; then + if [[ "$SKIP_IF_NO_CHANGES" == "false" ]]; then echo "Change detection disabled, workflow will run" - echo "should-run=true" >> $GITHUB_OUTPUT - elif [[ "${{ steps.filter.outputs.contracts }}" == "true" ]]; then + echo "should-run=true" >> "$GITHUB_OUTPUT" + elif [[ "$CONTRACTS_CHANGED" == "true" ]]; then echo "Contract changes detected, workflow will run" - echo "should-run=true" >> $GITHUB_OUTPUT + echo "should-run=true" >> "$GITHUB_OUTPUT" else echo "No contract changes detected, skipping workflow" - echo "should-run=false" >> $GITHUB_OUTPUT + echo "should-run=false" >> "$GITHUB_OUTPUT" fi ci: @@ -197,8 +204,10 @@ jobs: - name: Install dependencies if: inputs.package-manager != 'none' + env: + PACKAGE_MANAGER: ${{ inputs.package-manager }} run: | - case "${{ inputs.package-manager }}" in + case "$PACKAGE_MANAGER" in npm) npm ci ;; yarn) yarn --frozen-lockfile ;; pnpm) corepack enable && pnpm install --frozen-lockfile ;; @@ -217,7 +226,8 @@ jobs: - name: Run tests env: RPC_URL: ${{ secrets.RPC_URL }} - run: forge test -${{ inputs.test-verbosity }} + TEST_VERBOSITY: ${{ inputs.test-verbosity }} + run: forge test "-$TEST_VERBOSITY" slither: name: Slither Analysis @@ -244,8 +254,10 @@ jobs: - name: Install dependencies if: inputs.package-manager != 'none' + env: + PACKAGE_MANAGER: ${{ inputs.package-manager }} run: | - case "${{ inputs.package-manager }}" in + case "$PACKAGE_MANAGER" in npm) npm ci ;; yarn) yarn --frozen-lockfile ;; pnpm) corepack enable && pnpm install --frozen-lockfile ;; @@ -295,8 +307,10 @@ jobs: - name: Install dependencies if: inputs.package-manager != 'none' + env: + PACKAGE_MANAGER: ${{ inputs.package-manager }} run: | - case "${{ inputs.package-manager }}" in + case "$PACKAGE_MANAGER" in npm) npm ci ;; yarn) yarn --frozen-lockfile ;; pnpm) corepack enable && pnpm install --frozen-lockfile ;; @@ -308,9 +322,10 @@ jobs: - name: Run coverage env: RPC_URL: ${{ secrets.RPC_URL }} + COVERAGE_EXCLUDE_PATHS: ${{ inputs.coverage-exclude-paths }} run: | - if [[ -n "${{ inputs.coverage-exclude-paths }}" ]]; then - forge coverage --no-match-path "${{ inputs.coverage-exclude-paths }}" 2>&1 | tee coverage-raw.txt + if [[ -n "$COVERAGE_EXCLUDE_PATHS" ]]; then + forge coverage --no-match-path "$COVERAGE_EXCLUDE_PATHS" 2>&1 | tee coverage-raw.txt else forge coverage 2>&1 | tee coverage-raw.txt fi @@ -362,8 +377,10 @@ jobs: - name: Install dependencies if: inputs.package-manager != 'none' + env: + PACKAGE_MANAGER: ${{ inputs.package-manager }} run: | - case "${{ inputs.package-manager }}" in + case "$PACKAGE_MANAGER" in npm) npm ci ;; yarn) yarn --frozen-lockfile ;; pnpm) corepack enable && pnpm install --frozen-lockfile ;; @@ -415,8 +432,10 @@ jobs: - name: Install dependencies if: inputs.package-manager != 'none' + env: + PACKAGE_MANAGER: ${{ inputs.package-manager }} run: | - case "${{ inputs.package-manager }}" in + case "$PACKAGE_MANAGER" in npm) npm ci ;; yarn) yarn --frozen-lockfile ;; pnpm) corepack enable && pnpm install --frozen-lockfile ;; @@ -478,8 +497,10 @@ jobs: - name: Install dependencies if: inputs.package-manager != 'none' + env: + PACKAGE_MANAGER: ${{ inputs.package-manager }} run: | - case "${{ inputs.package-manager }}" in + case "$PACKAGE_MANAGER" in npm) npm ci ;; yarn) yarn --frozen-lockfile ;; pnpm) corepack enable && pnpm install --frozen-lockfile ;; @@ -490,9 +511,10 @@ jobs: PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} RPC_URL: ${{ secrets.RPC_URL }} DEPLOY_ENV_VARS: ${{ secrets.DEPLOY_ENV_VARS }} + DEPLOY_SCRIPT: ${{ inputs.deploy-script }} run: | source .etherform/scripts/deploy/prepare-env.sh - forge script "${{ inputs.deploy-script }}" \ + forge script "$DEPLOY_SCRIPT" \ --rpc-url "$RPC_URL" \ --private-key "$PRIVATE_KEY" \ --broadcast \ @@ -500,7 +522,9 @@ jobs: -vvvv - name: Wait for indexing - run: sleep ${{ inputs.indexing-wait }} + env: + INDEXING_WAIT: ${{ inputs.indexing-wait }} + run: sleep "$INDEXING_WAIT" - name: Parse deployment addresses id: parse diff --git a/.github/workflows/_upgrade-safety.yml b/.github/workflows/_upgrade-safety.yml index 4fc21dc..3bb8d0f 100644 --- a/.github/workflows/_upgrade-safety.yml +++ b/.github/workflows/_upgrade-safety.yml @@ -63,8 +63,10 @@ jobs: - name: Install dependencies if: inputs.package-manager != 'none' + env: + PACKAGE_MANAGER: ${{ inputs.package-manager }} run: | - case "${{ inputs.package-manager }}" in + case "$PACKAGE_MANAGER" in npm) npm ci ;; yarn) yarn --frozen-lockfile ;; pnpm) corepack enable && pnpm install --frozen-lockfile ;;