fix: harden workflow command argument execution - #4
Conversation
Reviewer's GuideIntroduces a hardened command argument preparation layer that preserves quoting/escaping, normalizes legacy stdout redirection into managed capture, updates DAG and tool runners to use this new API, and simplifies tool validation to generic executable checks while expanding test coverage for argument parsing, redirection, and validation edge cases. Sequence diagram for hardened workflow command executionsequenceDiagram
participant Runner as WorkflowRunner
participant Prep as CommandPreparation
participant Process as DirectProcess
participant Artifact as OutputArtifact
Runner->>Prep: prepareCommandString(raw, domain, inputPath, outputFile)
Prep->>Prep: splitCommandArgs(raw)
Prep->>Prep: stripOutputRedirect(args)
Prep-->>Runner: commandInvocation(Args, CaptureStdout)
Runner->>Process: exec.CommandContext(command, Args)
alt CaptureStdout
Process-->>Artifact: stdout
end
Process-->>Runner: exit status and stderr
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Hey - I've found 2 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="internal/pipeline/command_args.go" line_range="147-154" />
<code_context>
+ }
+
+ if quote == '"' {
+ switch r {
+ case '"':
+ quote = 0
+ case '\\':
+ escaped = true
+ default:
+ current.WriteRune(r)
+ }
+ tokenStarted = true
+ continue
</code_context>
<issue_to_address>
**issue (bug_risk):** Backslashes inside double-quoted arguments are always consumed, so an explicit command body such as `bash -c "printf '%s\\n' hi"` is passed to bash without the backslash and prints the wrong result. This violates the stated requirement that quoted command bodies remain intact.
**Triggers:** When a quoted workflow argument, especially a `bash -c` body, contains a backslash intended for the invoked program.
**Suggested fix:** Preserve backslashes inside double quotes except when they escape a supported quoting character, or implement the documented escaping rules explicitly.
</issue_to_address>
### Comment 2
<location path="internal/pipeline/pipeline.go" line_range="360-365" />
<code_context>
- if !strings.Contains(tool.Args[0], "-w") {
- return fmt.Errorf("gobuster requires a wordlist (-w)")
- }
+ command := strings.TrimSpace(tool.Command)
+ if command == "" {
+ return fmt.Errorf("tool command is empty")
+ }
+ if _, err := exec.LookPath(command); err != nil {
+ return fmt.Errorf("command not found: %s (install it or check PATH)", command)
}
-
</code_context>
<issue_to_address>
**issue (bug_risk):** `validateTool` performs `LookPath` on the trimmed command but callers still execute `tool.Command` unchanged, so a command configured with surrounding whitespace passes validation and then fails at `exec.CommandContext` with an executable-not-found error.
**Triggers:** When a workflow tool command contains leading or trailing whitespace.
**Suggested fix:** Either reject whitespace-bearing command names or assign the trimmed command back to the invocation used by `exec.CommandContext`.
```suggestion
command := strings.TrimSpace(tool.Command)
if command == "" {
return fmt.Errorf("tool command is empty")
}
if command != tool.Command {
return fmt.Errorf("tool command contains surrounding whitespace")
}
if _, err := exec.LookPath(command); err != nil {
return fmt.Errorf("command not found: %s (install it or check PATH)", command)
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| switch r { | ||
| case '"': | ||
| quote = 0 | ||
| case '\\': | ||
| escaped = true | ||
| default: | ||
| current.WriteRune(r) | ||
| } |
There was a problem hiding this comment.
issue (bug_risk): Backslashes inside double-quoted arguments are always consumed, so an explicit command body such as bash -c "printf '%s\\n' hi" is passed to bash without the backslash and prints the wrong result. This violates the stated requirement that quoted command bodies remain intact.
Triggers: When a quoted workflow argument, especially a bash -c body, contains a backslash intended for the invoked program.
Suggested fix: Preserve backslashes inside double quotes except when they escape a supported quoting character, or implement the documented escaping rules explicitly.
| command := strings.TrimSpace(tool.Command) | ||
| if command == "" { | ||
| return fmt.Errorf("tool command is empty") | ||
| } | ||
| if _, err := exec.LookPath(command); err != nil { | ||
| return fmt.Errorf("command not found: %s (install it or check PATH)", command) |
There was a problem hiding this comment.
issue (bug_risk): validateTool performs LookPath on the trimmed command but callers still execute tool.Command unchanged, so a command configured with surrounding whitespace passes validation and then fails at exec.CommandContext with an executable-not-found error.
Triggers: When a workflow tool command contains leading or trailing whitespace.
Suggested fix: Either reject whitespace-bearing command names or assign the trimmed command back to the invocation used by exec.CommandContext.
| command := strings.TrimSpace(tool.Command) | |
| if command == "" { | |
| return fmt.Errorf("tool command is empty") | |
| } | |
| if _, err := exec.LookPath(command); err != nil { | |
| return fmt.Errorf("command not found: %s (install it or check PATH)", command) | |
| command := strings.TrimSpace(tool.Command) | |
| if command == "" { | |
| return fmt.Errorf("tool command is empty") | |
| } | |
| if command != tool.Command { | |
| return fmt.Errorf("tool command contains surrounding whitespace") | |
| } | |
| if _, err := exec.LookPath(command); err != nil { | |
| return fmt.Errorf("command not found: %s (install it or check PATH)", command) |
Summary
Fix the first runtime-hardening set after the DAG v3 / Mermaid work:
strings.Fields.> {{output}}/1> {{output}}syntax into Termaid-managed stdout capture without invoking a shell.Args[0]panic hazard and false rejections such as valid Nuclei defaults.Safety / execution semantics
exec.CommandContext.bash -c "..."arguments remain supported because their quoted command body is now kept intact.> filenamedestinations are rejected; managed legacy redirection must point at{{output}}/$(output).Tests
Added coverage for quoted arguments, escaped spaces, empty quoted arguments,
bash -ccommand bodies, malformed quoting, legacy and compact stdout redirects, tool-owned output paths, stdout-, arbitrary redirect rejection, nil tool configs, and executables with empty argument lists.Summary by Sourcery
Harden workflow command preparation and validation while retaining direct, shell-free execution semantics.
Bug Fixes:
Enhancements:
Tests: