Skip to content

Compile fails on v0.78.3+: auto-injected "Build checkout manifest" step embeds workflow_dispatch input expressions directly into shell, tripping template-injection linter #38150

Description

@corygehr

Summary

When a workflow's checkout.repository is set to a workflow_dispatch input expression (e.g. ${{ github.event.inputs.trigger_ref }}), the compiler-emitted "Build checkout manifest for safe-outputs handlers" step embeds that expression directly into a shell run: block. Starting with v0.78.3, the template-injection linter flags this as unsafe and gh aw compile fails.

The unsafe pattern is generated by gh-aw itself — there is no source-side workaround.

Affected versions

Version Behavior
v0.78.0 – v0.78.2 ✅ Compiles clean (manifest step not yet generated)
v0.78.3 ❌ Compile fails — manifest step + tightened linter shipped together
v0.79.0, v0.79.1 ❌ Same failure

Reproduction

Minimal workflow:

---
on:
  workflow_dispatch:
    inputs:
      trigger_ref:
        type: string
        required: true
engine: copilot
timeout-minutes: 5
checkout:
  - repository: ${{ github.event.inputs.trigger_ref }}
    current: true
safe-outputs:
  noop:
    report-as-issue: false
---
# Repro
Do nothing.

gh aw compile produces:

✗ template injection vulnerabilities detected in compiled workflow
  repo='${{ github.event.inputs.trigger_ref }}'

Root cause

pkg/workflow/checkout_step_generator.go around line 122:

for _, e := range entries {
    // Both repo and path are static at compile time. Use shell-quoted literals.
    fmt.Fprintf(&sb, "          repo=%s\n", shellSingleQuote(e.repository))

The comment assumes e.repository is always a static literal, but it's the raw string from frontmatter — which legitimately holds a ${{ … }} expression for dispatch-time targeting. shellSingleQuote wraps the expression in single quotes and the linter then sees an unsafe substitution inside run:.

Suggested directions (non-exhaustive)

  • Detect ${{ in e.repository and emit the value via job-level env: referenced as $REPO_x in shell (the same pattern the codemod uses for user-authored steps), or
  • Skip manifest emission for entries whose repository is dynamic and have the MCP server fall back to live gh api resolution at runtime, or
  • Resolve default_branch in a downstream step that uses env: indirection instead of inline expression substitution.

Workarounds

None known on the consumer side — the unsafe substring is purely framework-generated. --strict=false does not bypass the template-injection linter. Affected users must remain on v0.78.2 or earlier, which means they cannot pick up the cross-org owner-derivation fix from #37976 that ships in v0.79.x.

Impact

This blocks adoption of v0.78.3+ for any workflow that uses dispatch-time trigger_ref patterns to target arbitrary repositories — a common pattern for centralized-ops architectures.

Metadata

Metadata

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions