-
Notifications
You must be signed in to change notification settings - Fork 49
Description
Bug Description
The template injection validator incorrectly flags expressions in env: blocks as unsafe, even though using environment variables is the documented safe pattern. This happens because:
- The validator regex expects
env:to appear beforerun:in the YAML - The YAML serializer outputs
run:beforeenv:for custom job steps - The regex then captures
env:blocks as part ofrun:blocks, flagging safe code as unsafe
Reproduction Steps
- Create a workflow with a custom job that uses step outputs in env blocks:
---
name: Test Workflow
on: workflow_dispatch
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Build
id: build
uses: docker/build-push-action@v5
with:
context: ./
- name: Sign image
env:
DIGEST: ${{ steps.build.outputs.digest }}
run: |
echo "Signing image with digest: $DIGEST"
---
Test prompt-
Run
gh aw compile -
Observe the error:
✗ error: template injection vulnerabilities detected in compiled workflow
steps.*.outputs context (1 occurrence(s)):
- ${{ steps.build.outputs.digest }}
in: DIGEST: ${{ steps.build.outputs.digest }}
Expected Behavior
The workflow should compile successfully because the expression is used in an env: block, which is the documented safe pattern.
Actual Behavior
The compiler flags the expression as unsafe, even though it's in an env: block.
Root Cause Analysis
1. Validator Regex Issue
In pkg/workflow/template_injection_validation.go:69, the regex for multi-line run blocks:
runBlockRegex = regexp.MustCompile(`(?m)^\s+run:\s*\|\s*\n((?:[ \t]+.+\n?)+?)\s*(?:^[ \t]*-\s|\z)|^\s+run:\s*(.+)$`)This regex captures content until it finds:
- A line starting with
-(next step) - End of string
But env: lines at the same indentation don't match either pattern, so they get included in the capture.
2. YAML Serialization Order Issue
The compiled YAML has different key ordering:
For AI agent steps (correct order - validation passes):
- env:
DRY_RUN: ${{ github.event.inputs.dry_run }}
name: Check dry run mode
run: |
...For custom job steps (incorrect order - validation fails):
- name: Sign image
run: |
echo "$DIGEST"
env:
DIGEST: ${{ steps.build.outputs.digest }}3. Test Case Confirms the Issue
In pkg/workflow/template_injection_validation_test.go:382-394, the safe test case has env: BEFORE run::
env:
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"And the test passes. But when run: comes before env: (as in the custom jobs serialization), the validation fails.
Suggested Fixes
Either:
-
Fix the YAML serializer to output
env:beforerun:for custom job steps (consistent with AI agent steps) -
Fix the regex to properly detect YAML step boundaries by stopping at any YAML key at the same indentation level (
env:,if:,name:,with:, etc.), not just-markers -
Use proper YAML parsing instead of regex to extract run block content
Environment
- gh-aw version: v0.37.3
- OS: Linux