Skip to content

fix(recipes): replace quoted heredocs with direct env-var assignment (skwaq#469)#4245

Closed
rysweet wants to merge 1 commit intomainfrom
fix/issue-469-heredoc-direct-assignment
Closed

fix(recipes): replace quoted heredocs with direct env-var assignment (skwaq#469)#4245
rysweet wants to merge 1 commit intomainfrom
fix/issue-469-heredoc-direct-assignment

Conversation

@rysweet
Copy link
Copy Markdown
Owner

@rysweet rysweet commented Apr 5, 2026

Problem

The recipe runner 0.3.4 does inline substitution inside quoted heredocs (<<'EOF'). If a template variable value (e.g. final_requirements from step-02c) contains the heredoc terminator string on its own line, the heredoc terminates early. When the remaining content after the early termination has an odd number of single quotes, bash raises:

/bin/bash: -c: line N: unexpected EOF while looking for matching \'

This is the exact failure in skwaq issue #469, which blocked every benchmark and PR workflow going through default-workflow/smart-orchestrator.

Root Cause Verified

recipe-runner-rs recipe.yaml \
  --set 'final_requirements=normal content
EOFREQS
it\'s content'
# → /bin/bash: -c: line 5: unexpected EOF while looking for matching `'

The bug chain:

  1. 0.3.4 binary inlines {{final_requirements}} directly into quoted heredoc body
  2. If value contains EOFREQS on its own line, heredoc terminates early
  3. Remaining content after fake terminator is parsed as bash commands
  4. If that content has an odd number of single quotes → syntax error

Fix

Replace quoted-heredoc captures with direct env-var assignment:

# Before (vulnerable):
ISSUE_REQS=$(cat <<'EOFREQS'
{{final_requirements}}
EOFREQS
)

# After (safe):
ISSUE_REQS={{final_requirements}}
# render_shell expands to: ISSUE_REQS="$RECIPE_VAR_final_requirements"

Security: bash does NOT re-execute $() or backtick substitutions when expanding a variable reference — so values containing $(evil) are safe.

Scope

Fixed in 4 steps:

  • step-03-create-issue{{task_description}}, {{final_requirements}}
  • step-03b-extract-issue-number{{issue_creation}}, {{task_description}}
  • step-16-create-draft-pr{{task_description}}, {{design_spec}}
  • step-19d-verification-gate{{philosophy_check}}, {{patterns_check}}

Validation

Tested with recipe-runner-rs 0.3.4: values containing heredoc terminators + single quotes now succeed with all content preserved.

…(skwaq#469)

The recipe runner 0.3.4 does INLINE substitution for quoted heredocs
(<<'EOF'). If a template variable value (e.g. final_requirements from
step-02c) contains the heredoc terminator string on its own line, the
heredoc terminates early. When the content remaining after the early
termination has an odd number of single quotes, bash raises:

  /bin/bash: -c: line N: unexpected EOF while looking for matching \'

This is the exact failure in skwaq issue #469, which blocked every
benchmark and PR workflow that went through default-workflow/smart-
orchestrator.

Root cause verification:
  recipe-runner-rs with value containing 'EOFREQS\nit\'s content'
  → /bin/bash: -c: line 5: unexpected EOF while looking for matching `'

Fix: replace all four affected quoted-heredoc captures in
  - step-03-create-issue   ({{task_description}}, {{final_requirements}})
  - step-03b-extract-issue-number  ({{issue_creation}}, {{task_description}})
  - step-16-create-draft-pr  ({{task_description}}, {{design_spec}})
  - step-19d-verification-gate  ({{philosophy_check}}, {{patterns_check}})

with direct env-var assignment:

  TASK_DESC={{task_description}}  →  TASK_DESC="$RECIPE_VAR_task_description"

Security: bash does NOT re-execute $() or backtick substitutions when
expanding a variable reference, so values containing $(evil) are safe.
The env-var approach is the canonical injection-safe pattern for the
0.3.4+ runner (matching unquoted-heredoc usage elsewhere in this file).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 5, 2026

Repo Guardian - Passed

All files in this PR have been reviewed and no ephemeral content was found.

File Status
amplifier-bundle/recipes/default-workflow.yaml ✅ Durable — project recipe/toolchain configuration

No violations detected.

Generated by Repo Guardian for issue #4245 ·

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 5, 2026

PR Triage Report

Risk: Medium | Priority: High | Type: Bugfix / Shell injection prevention

Summary

Replaces quoted-heredoc captures with direct env-var assignment in 4 recipe steps (step-03, step-03b, step-16, step-19d) to fix heredoc terminator injection that caused unexpected EOF while looking for matching ' failures (skwaq#469).

Assessment

Dimension Score Notes
Risk Medium Changes 4 core workflow steps in default-workflow.yaml; no Python/test changes
Priority High Fixes blocking failure (#469) that affected all benchmark and PR workflows
Scope Focused 1 file, 30 additions / 46 deletions — simplification
CI ✅ Green Validate Code, plugin tests, skill/agent drift, Root Hygiene all pass
Tests ⚠️ None added Validated manually with recipe-runner-rs 0.3.4; no regression tests included
Security Related PRs: #4228, #4236 All three address the same heredoc quoting issue with different approaches

Key Finding: Overlapping PRs

Three open PRs address the same quoting issue with different strategies:

Recommendation: If this approach is preferred, #4228 and #4236 should be closed as superseded before merging. The direct env-var approach is technically superior (immune to terminator injection by design), but the lack of regression tests is a gap given the blocking nature of the original issue.

Action Required

Generated by PR Triage Agent ·

rysweet pushed a commit that referenced this pull request Apr 16, 2026
- Replace fragile nested heredocs (<<EOFCOUNTS wrapping <<PY) with process
  substitution for parsing scope counts
- Replace <<EOF commit message heredoc with printf to avoid delimiter
  collisions when $COMMIT_TITLE contains EOF-like strings
- Add PRECOMMIT_DIR resolution with AMPLIHACK_HOME fallback so pre-commit
  scripts are found regardless of worktree layout
- Add clarifying comments on EOFTASKDESC heredoc quoting rationale
- Update test regex to accommodate quoted script paths

Closes #4304, closes #4309, closes #4321, closes #4245, closes #4341

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rysweet pushed a commit that referenced this pull request Apr 17, 2026
- Replace fragile nested heredocs (<<EOFCOUNTS wrapping <<PY) with process
  substitution for parsing scope counts
- Replace <<EOF commit message heredoc with printf to avoid delimiter
  collisions when $COMMIT_TITLE contains EOF-like strings
- Add PRECOMMIT_DIR resolution with AMPLIHACK_HOME fallback so pre-commit
  scripts are found regardless of worktree layout
- Add clarifying comments on EOFTASKDESC heredoc quoting rationale
- Update test regex to accommodate quoted script paths

Closes #4304, closes #4309, closes #4321, closes #4245, closes #4341

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rysweet added a commit that referenced this pull request Apr 17, 2026
- Replace fragile nested heredocs (<<EOFCOUNTS wrapping <<PY) with process
  substitution for parsing scope counts
- Replace <<EOF commit message heredoc with printf to avoid delimiter
  collisions when $COMMIT_TITLE contains EOF-like strings
- Add PRECOMMIT_DIR resolution with AMPLIHACK_HOME fallback so pre-commit
  scripts are found regardless of worktree layout
- Add clarifying comments on EOFTASKDESC heredoc quoting rationale
- Update test regex to accommodate quoted script paths

Closes #4304, closes #4309, closes #4321, closes #4245, closes #4341

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant