Commit 807e61c
authored
fix(git): use env vars for safe commit message handling (#6)
## Summary
Fixed shell expansion issues in `git-commit` workflow by adopting the
GitHub Actions security pattern for handling user-provided content.
## Problem
Commit messages containing special characters were being corrupted:
- ❌ Backticks (`` ` ``) were stripped or executed as command
substitution
- ❌ Dollar signs (`$`) triggered variable expansion
- ❌ Quotes could break shell parsing
Example of broken message:
```
fix: handle `backticks` and $variables
```
Would become:
```
fix: handle and
```
## Background: Variable Syntax Migration
This fix builds on commit c234599 which migrated all workflow variables
from `${...}` to `{{...}}` syntax:
- **Previous syntax**: `${inputs.commit_message}` (ambiguous with shell
variables)
- **New syntax**: `{{inputs.commit_message}}` (clear separation from
shell)
- **Benefits**: Matches Jinja2 standard, eliminates shell expansion
ambiguity
- **Scope**: Complete migration of 32 template workflows, 25 test
workflows, all docs
However, even with `{{...}}` syntax, the variable content was still
vulnerable to shell expansion when resolved into command strings.
## Root Cause
The `select_message` block passed commit message content directly in
shell command strings with double quotes:
```yaml
command: |
printf '%s' "{{inputs.commit_message}}" > "$SCRATCH/message.txt"
```
**Execution flow:**
1. Workflow engine resolves `{{inputs.commit_message}}` → `"fix: handle
\`backticks\` here"`
2. String inserted into command: `printf '%s' "fix: handle \`backticks\`
here" > ...`
3. Shell executes backticks as command substitution → content stripped
The `{{...}}` syntax change separated workflow vars from shell vars
syntactically, but didn't prevent shell expansion of the **content**
itself.
## Solution
Adopted the **GitHub Actions security pattern**: Pass sensitive content
via environment variables instead of inline shell substitution.
**Before:**
```yaml
command: |
if [ -n "{{inputs.commit_message}}" ]; then
printf '%s' "{{inputs.commit_message}}" > "$SCRATCH/message.txt"
else
printf '%s' "{{blocks.llm_generate_message.outputs.response}}" > "$SCRATCH/message.txt"
fi
```
**After:**
```yaml
env:
MANUAL_MESSAGE: "{{inputs.commit_message}}"
LLM_MESSAGE: "{{blocks.llm_generate_message.outputs.response}}"
command: |
# Use environment variables to safely pass content (GitHub Actions pattern)
# This prevents shell expansion of special chars (backticks, $, quotes)
if [ -n "$MANUAL_MESSAGE" ]; then
printf '%s' "$MANUAL_MESSAGE" > "$SCRATCH/message.txt"
else
printf '%s' "$LLM_MESSAGE" > "$SCRATCH/message.txt"
fi
```
## How It Works
1. **Workflow engine** resolves `{{...}}` variables at YAML level (from
commit c234599)
2. **Python** sets environment variables through the OS (no shell
interpretation)
3. **Shell** reads `$VAR` from environment safely without expanding
content
This completes the security model:
- ✅ **Syntax level**: `{{...}}` (workflow) vs `${...}` (shell) - clear
separation
- ✅ **Content level**: Environment variables prevent shell expansion of
user content
## How Industry Standards Handle This
**GitHub Actions:**
```yaml
# UNSAFE: Content in command string
run: echo "${{ github.event.issue.title }}"
# SAFE: Content via environment variable
env:
TITLE: ${{ github.event.issue.title }}
run: echo "$TITLE"
```
**GitLab CI:** Uses same pattern with `variables:` block
**Argo Workflows:** Uses `env:` fields for parameter passing
This is the industry-standard security practice for CI/CD systems.
## Testing
Tested with commit message containing all special characters:
```
fix(git): use env vars for safe message handling
Changed `select_message` block with `backticks`, $variables, and user's quotes.
Pattern: set `$MESSAGE` via env, read $VAR safely.
```
**Result**: All special characters preserved correctly in the final
commit! ✅
Both commits in this PR were created using the fixed workflow,
demonstrating it works correctly.
## Changes
- Modified `src/workflows_mcp/templates/git/git-commit.yaml`:
- Added `env` field to `select_message` block with `MANUAL_MESSAGE` and
`LLM_MESSAGE`
- Moved message content from command string to environment variables
- Updated shell command to reference `$MANUAL_MESSAGE` and
`$LLM_MESSAGE` instead of inline substitution
- Added comments explaining the GitHub Actions security pattern
## Benefits
- ✅ Prevents shell expansion of backticks, dollar signs, and quotes
- ✅ Matches industry-standard CI/CD security practices (GitHub Actions,
GitLab CI)
- ✅ Uses existing Shell executor capabilities (no new executors needed)
- ✅ Maintains the file-based commit approach (`git commit -F`)
- ✅ Completes the security model started by commit c234599
## References
- Variable syntax migration: commit c234599
- GitHub Actions Security: [Preventing script injection
attacks](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable)
- Shell executor env support:
`src/workflows_mcp/engine/executors_core.py` lines 276-27992 files changed
Lines changed: 2188 additions & 2161 deletions
File tree
- src/workflows_mcp
- engine
- templates
- ci
- files
- git
- node
- python
- tdd
- testing
- tools
- catalog
- core
- providers
- tests
- snapshots
- workflows
- core
- block-status
- composition
- conditionals
- dag-execution
- file-operations
- state-management
- variable-resolution
- integration
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
133 | 133 | | |
134 | 134 | | |
135 | 135 | | |
136 | | - | |
| 136 | + | |
137 | 137 | | |
138 | 138 | | |
139 | | - | |
| 139 | + | |
140 | 140 | | |
141 | 141 | | |
142 | 142 | | |
| |||
147 | 147 | | |
148 | 148 | | |
149 | 149 | | |
150 | | - | |
151 | | - | |
152 | | - | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
153 | 153 | | |
154 | 154 | | |
155 | 155 | | |
| |||
167 | 167 | | |
168 | 168 | | |
169 | 169 | | |
170 | | - | |
171 | | - | |
172 | | - | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
173 | 173 | | |
174 | 174 | | |
175 | 175 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
650 | 650 | | |
651 | 651 | | |
652 | 652 | | |
653 | | - | |
| 653 | + | |
654 | 654 | | |
655 | 655 | | |
656 | 656 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
85 | 85 | | |
86 | 86 | | |
87 | 87 | | |
88 | | - | |
| 88 | + | |
89 | 89 | | |
90 | 90 | | |
91 | 91 | | |
| |||
106 | 106 | | |
107 | 107 | | |
108 | 108 | | |
109 | | - | |
| 109 | + | |
110 | 110 | | |
111 | 111 | | |
112 | 112 | | |
| |||
124 | 124 | | |
125 | 125 | | |
126 | 126 | | |
127 | | - | |
| 127 | + | |
128 | 128 | | |
129 | 129 | | |
130 | 130 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
27 | | - | |
28 | | - | |
29 | | - | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
| |||
53 | 53 | | |
54 | 54 | | |
55 | 55 | | |
56 | | - | |
| 56 | + | |
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
| |||
72 | 72 | | |
73 | 73 | | |
74 | 74 | | |
75 | | - | |
76 | | - | |
| 75 | + | |
| 76 | + | |
77 | 77 | | |
78 | 78 | | |
79 | 79 | | |
| |||
86 | 86 | | |
87 | 87 | | |
88 | 88 | | |
89 | | - | |
| 89 | + | |
90 | 90 | | |
91 | 91 | | |
92 | 92 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
16 | | - | |
| 16 | + | |
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
43 | | - | |
| 43 | + | |
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
| |||
293 | 293 | | |
294 | 294 | | |
295 | 295 | | |
296 | | - | |
| 296 | + | |
297 | 297 | | |
298 | 298 | | |
299 | 299 | | |
300 | 300 | | |
301 | 301 | | |
302 | 302 | | |
303 | | - | |
| 303 | + | |
304 | 304 | | |
305 | 305 | | |
306 | 306 | | |
| |||
321 | 321 | | |
322 | 322 | | |
323 | 323 | | |
324 | | - | |
| 324 | + | |
325 | 325 | | |
326 | 326 | | |
327 | 327 | | |
| |||
407 | 407 | | |
408 | 408 | | |
409 | 409 | | |
410 | | - | |
| 410 | + | |
411 | 411 | | |
412 | 412 | | |
413 | 413 | | |
414 | 414 | | |
415 | 415 | | |
416 | 416 | | |
417 | | - | |
| 417 | + | |
418 | 418 | | |
419 | 419 | | |
420 | 420 | | |
421 | | - | |
| 421 | + | |
422 | 422 | | |
423 | 423 | | |
424 | 424 | | |
| |||
466 | 466 | | |
467 | 467 | | |
468 | 468 | | |
469 | | - | |
| 469 | + | |
470 | 470 | | |
471 | 471 | | |
472 | 472 | | |
473 | 473 | | |
474 | | - | |
| 474 | + | |
475 | 475 | | |
476 | 476 | | |
477 | 477 | | |
478 | 478 | | |
479 | | - | |
| 479 | + | |
480 | 480 | | |
481 | 481 | | |
482 | 482 | | |
| |||
602 | 602 | | |
603 | 603 | | |
604 | 604 | | |
605 | | - | |
606 | | - | |
| 605 | + | |
| 606 | + | |
607 | 607 | | |
608 | 608 | | |
609 | 609 | | |
| |||
614 | 614 | | |
615 | 615 | | |
616 | 616 | | |
617 | | - | |
| 617 | + | |
618 | 618 | | |
619 | 619 | | |
620 | 620 | | |
621 | | - | |
622 | | - | |
| 621 | + | |
| 622 | + | |
623 | 623 | | |
624 | 624 | | |
625 | 625 | | |
626 | 626 | | |
627 | | - | |
| 627 | + | |
628 | 628 | | |
629 | 629 | | |
630 | 630 | | |
631 | 631 | | |
632 | | - | |
633 | | - | |
| 632 | + | |
| 633 | + | |
634 | 634 | | |
635 | 635 | | |
636 | 636 | | |
637 | | - | |
| 637 | + | |
638 | 638 | | |
639 | | - | |
640 | | - | |
641 | | - | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
642 | 642 | | |
643 | 643 | | |
644 | 644 | | |
645 | 645 | | |
646 | 646 | | |
647 | 647 | | |
648 | | - | |
| 648 | + | |
649 | 649 | | |
650 | 650 | | |
651 | 651 | | |
| |||
654 | 654 | | |
655 | 655 | | |
656 | 656 | | |
657 | | - | |
| 657 | + | |
658 | 658 | | |
659 | 659 | | |
660 | | - | |
| 660 | + | |
661 | 661 | | |
662 | 662 | | |
663 | 663 | | |
664 | 664 | | |
665 | 665 | | |
666 | 666 | | |
667 | 667 | | |
668 | | - | |
| 668 | + | |
669 | 669 | | |
670 | 670 | | |
671 | 671 | | |
| |||
0 commit comments