From 6e15b4a25c35df21e343231303b4df7b5384bd09 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Thu, 5 Mar 2026 01:17:14 -0800 Subject: [PATCH 1/3] Fix expression injection in rubric-check action MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move ${{ inputs.* }} from run: blocks to env: blocks so values are set as environment variables before bash executes. Prevents shell injection via crafted input values on user-submitted PRs. Resolves CodeQL alerts 1–4 (actions/code-injection). --- actions/rubric-check/action.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/actions/rubric-check/action.yml b/actions/rubric-check/action.yml index b093260..869dab0 100644 --- a/actions/rubric-check/action.yml +++ b/actions/rubric-check/action.yml @@ -26,18 +26,23 @@ runs: steps: - name: Verify binary exists shell: bash + env: + INPUT_CLI_BINARY: ${{ inputs.cli-binary }} run: | - if [ ! -x "${{ inputs.cli-binary }}" ]; then - echo "::error::CLI binary not found or not executable: ${{ inputs.cli-binary }}" + if [ ! -x "$INPUT_CLI_BINARY" ]; then + echo "::error::CLI binary not found or not executable: $INPUT_CLI_BINARY" exit 1 fi - name: Score rubric id: score shell: bash + env: + INPUT_CLI_BINARY: ${{ inputs.cli-binary }} + INPUT_PROFILE: ${{ inputs.profile }} run: | - BINARY="${{ inputs.cli-binary }}" - PROFILE="${{ inputs.profile }}" + BINARY="$INPUT_CLI_BINARY" + PROFILE="$INPUT_PROFILE" PASSED=0 FAILED=0 TOTAL=0 From 5f1b134919bd2cf48681da630dd92306a7ec1b2a Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Thu, 5 Mar 2026 01:17:17 -0800 Subject: [PATCH 2/3] Fix expression injection in surface-compat action MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move ${{ inputs.* }} from run: blocks to env: blocks so values are set as environment variables before bash executes. Prevents shell injection via crafted input values on user-submitted PRs. Resolves CodeQL alerts 5–10 (actions/code-injection). --- actions/surface-compat/action.yml | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/actions/surface-compat/action.yml b/actions/surface-compat/action.yml index d0da717..c1fdda6 100644 --- a/actions/surface-compat/action.yml +++ b/actions/surface-compat/action.yml @@ -22,21 +22,26 @@ runs: steps: - name: Verify inputs shell: bash + env: + INPUT_CLI_BINARY: ${{ inputs.cli-binary }} + INPUT_BASELINE: ${{ inputs.baseline }} run: | - if [ ! -x "${{ inputs.cli-binary }}" ]; then - echo "::error::CLI binary not found: ${{ inputs.cli-binary }}" + if [ ! -x "$INPUT_CLI_BINARY" ]; then + echo "::error::CLI binary not found: $INPUT_CLI_BINARY" exit 1 fi - if [ ! -f "${{ inputs.baseline }}" ]; then - echo "::error::Baseline file not found: ${{ inputs.baseline }}" + if [ ! -f "$INPUT_BASELINE" ]; then + echo "::error::Baseline file not found: $INPUT_BASELINE" exit 1 fi - name: Generate current surface shell: bash + env: + INPUT_CLI_BINARY: ${{ inputs.cli-binary }} run: | # Walk the CLI command tree and produce surface snapshot - BINARY="${{ inputs.cli-binary }}" + BINARY="$INPUT_CLI_BINARY" ROOT_NAME=$(basename "$BINARY") walk_commands() { @@ -72,8 +77,10 @@ runs: - name: Diff surfaces id: diff shell: bash + env: + INPUT_BASELINE: ${{ inputs.baseline }} run: | - BASELINE="${{ inputs.baseline }}" + BASELINE="$INPUT_BASELINE" CURRENT="/tmp/surface-current.txt" # comm requires sorted input — sort both with consistent locale From 02aee6e211e58e4157f194d1048cbc309520ed79 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Thu, 5 Mar 2026 01:39:27 -0800 Subject: [PATCH 3/3] Replace workflow commands with stderr for user-controlled values Workflow commands (::error::, ::warning::) are vulnerable to newline injection when they include user-controlled values. Replace with plain stderr messages to eliminate the command injection vector. --- actions/rubric-check/action.yml | 2 +- actions/surface-compat/action.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/actions/rubric-check/action.yml b/actions/rubric-check/action.yml index 869dab0..47a541c 100644 --- a/actions/rubric-check/action.yml +++ b/actions/rubric-check/action.yml @@ -30,7 +30,7 @@ runs: INPUT_CLI_BINARY: ${{ inputs.cli-binary }} run: | if [ ! -x "$INPUT_CLI_BINARY" ]; then - echo "::error::CLI binary not found or not executable: $INPUT_CLI_BINARY" + echo "ERROR: CLI binary not found or not executable: $INPUT_CLI_BINARY" >&2 exit 1 fi diff --git a/actions/surface-compat/action.yml b/actions/surface-compat/action.yml index c1fdda6..51d3bb8 100644 --- a/actions/surface-compat/action.yml +++ b/actions/surface-compat/action.yml @@ -27,11 +27,11 @@ runs: INPUT_BASELINE: ${{ inputs.baseline }} run: | if [ ! -x "$INPUT_CLI_BINARY" ]; then - echo "::error::CLI binary not found: $INPUT_CLI_BINARY" + echo "ERROR: CLI binary not found: $INPUT_CLI_BINARY" >&2 exit 1 fi if [ ! -f "$INPUT_BASELINE" ]; then - echo "::error::Baseline file not found: $INPUT_BASELINE" + echo "ERROR: Baseline file not found: $INPUT_BASELINE" >&2 exit 1 fi @@ -53,7 +53,7 @@ runs: local json json=$("$BINARY" "${args[@]}" --help --agent 2>/dev/null) || { - echo "::warning::--help --agent failed for: $cmd_path" + echo "WARNING: --help --agent failed for: $cmd_path" >&2 return 0 }