-
Notifications
You must be signed in to change notification settings - Fork 425
fix: compiler validates dangerous shell expansion in safe-outputs.steps; fix copilot-pr-nlp-analysis prompt #29123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
614264b
2521e22
d1fea34
bc5de8e
aaefd9f
83ddde4
16b5df4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,194 @@ | ||||||||||||||||||||
| // This file validates safe-outputs.steps run scripts for dangerous shell expansion | ||||||||||||||||||||
| // patterns that would be blocked at runtime by the safe-outputs security harness. | ||||||||||||||||||||
| // | ||||||||||||||||||||
| // # Shell Expansion Security in Safe-Outputs Steps | ||||||||||||||||||||
| // | ||||||||||||||||||||
| // The safe-outputs security harness blocks shell scripts that contain dangerous | ||||||||||||||||||||
| // bash expansion patterns. This validator detects those patterns at compile time | ||||||||||||||||||||
| // so workflow authors receive a clear error during `gh aw compile` rather than | ||||||||||||||||||||
| // a confusing runtime failure. | ||||||||||||||||||||
| // | ||||||||||||||||||||
| // # Blocked Patterns | ||||||||||||||||||||
| // | ||||||||||||||||||||
| // The following bash constructs are rejected: | ||||||||||||||||||||
| // - ${var@operator}: bash 4.4+ parameter transformation (e.g. ${foo@P}, ${bar@U}) | ||||||||||||||||||||
| // - ${!var}: bash indirect expansion | ||||||||||||||||||||
| // - $(command): command substitution | ||||||||||||||||||||
| // - `command`: backtick command substitution | ||||||||||||||||||||
| // | ||||||||||||||||||||
| // GitHub Actions template expressions (${{ ... }}) are explicitly allowed and are | ||||||||||||||||||||
| // excluded from the checks. | ||||||||||||||||||||
| // | ||||||||||||||||||||
| // # When to Add Validation Here | ||||||||||||||||||||
| // | ||||||||||||||||||||
| // Add validation to this file when: | ||||||||||||||||||||
| // - A new dangerous shell expansion variant must be detected in safe-outputs run scripts | ||||||||||||||||||||
| // - The safe-outputs harness blocks a new class of shell pattern at runtime | ||||||||||||||||||||
| // | ||||||||||||||||||||
| // For general safe-outputs validation, see safe_outputs_validation.go. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| package workflow | ||||||||||||||||||||
|
|
||||||||||||||||||||
| import ( | ||||||||||||||||||||
| "fmt" | ||||||||||||||||||||
| "regexp" | ||||||||||||||||||||
| "strings" | ||||||||||||||||||||
| ) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| var safeOutputsStepsShellExpansionLog = newValidationLogger("safe_outputs_steps_shell_expansion") | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // shellExpansionPattern matches dangerous bash expansion constructs inside a run: script. | ||||||||||||||||||||
| // | ||||||||||||||||||||
| // Captured groups by name: | ||||||||||||||||||||
| // - "paramTransform": ${var@operator} — bash 4.4+ parameter transformation | ||||||||||||||||||||
| // - "indirectExpand": ${!var} — bash indirect expansion | ||||||||||||||||||||
| // - "commandSubst": $(...) — command substitution (any $( sequence) | ||||||||||||||||||||
| // - "backtick": `...` — backtick command substitution | ||||||||||||||||||||
| // | ||||||||||||||||||||
| // After matching, callers must exclude false-positives: | ||||||||||||||||||||
| // - "commandSubst" matches starting with "$((" (arithmetic expansion) are ignored. | ||||||||||||||||||||
| // - "commandSubst" matches starting with "$({" or "$({{" that form "${{" are ignored | ||||||||||||||||||||
| // because "${{" is not a valid shell construct (GitHub Actions uses "${{ }}"). | ||||||||||||||||||||
|
||||||||||||||||||||
| // - "commandSubst" matches starting with "$({" or "$({{" that form "${{" are ignored | |
| // because "${{" is not a valid shell construct (GitHub Actions uses "${{ }}"). | |
| // - GitHub Actions expressions use "${{ ... }}", not "$(...)", so they do not | |
| // match the "commandSubst" pattern. |
Copilot
AI
Apr 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The remediation text says "Avoid shell variable expansion in safe-outputs run scripts", but this validator explicitly allows simple "$VAR" and "${VAR}" expansions (and tests cover those as allowed). This wording is misleading—please narrow it to the specific blocked constructs (command substitution/backticks/indirect expansion/parameter transformation) or describe the safe alternative without implying all variable expansion is forbidden.
| "Avoid shell variable expansion in safe-outputs run scripts. "+ | |
| "Write URL values and other dynamic content to files in /tmp/gh-aw/agent/ "+ | |
| "during the agent turn, then read the file contents in the safe-outputs step "+ | |
| "without using shell expansion (e.g. with 'cat' or a script argument)", | |
| "Avoid command substitution, backticks, indirect expansion, and parameter "+ | |
| "transformation in safe-outputs run scripts. Write URL values and other "+ | |
| "dynamic content to files in /tmp/gh-aw/agent/ during the agent turn, then "+ | |
| "read the file contents in the safe-outputs step (e.g. with 'cat' or by "+ | |
| "passing a script argument)", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Phase 5 explicitly says not to store URLs in shell variables or use command substitution (
$(...)), but Phase 6 immediately usesSENTIMENT_DIST_URL=$(cat ...)(both a shell variable and command substitution). If the intent is "don’t do this inside safe-outputs.steps run scripts" but it’s OK in the agent bash turn, please clarify the wording in Phase 5 (or adjust Phase 6 to avoid$()/shell vars by reading the files directly in Python) to remove the internal contradiction.See below for a potential fix: