resolve_sast_vulnerability workflow fails with shell operators like | or 2>/dev/null
Problem to solve
As a user of workflow resolve_sast_vulnerability/v1, I want to run the workflow on a LangSmith dataset with multiple examples to test the feature on a wide variety of vulnerabilities.
When the workflow is invoked, it performs multiple steps like gather_context , execute_fix and commit_and_open_mr, among others. During the execute_fix step, the agent tries to run certain commands like:
find . -name "*.js" -type f | head -20 # or
find / -name "bootstrap.js" -type f 2>/dev/null
However, these commands are:
-
Using disallowed operators like
|defined in duo_workflow_service/tools/command.py#L10 -
Using certain args like
2>/dev/nullthat do not work on Golangos/execexecutor:package main import ( "fmt" "os/exec" ) func main() { cmd := exec.Command("find", "/", "-name", `"bootstrap.js"`, "-type", "f", "2>/dev/null") output, err := cmd.Output() if err != nil { fmt.Println("Error:", err) return } fmt.Println(string(output)) } //Command output: Error: exit status 1Reference: https://go.dev/play/p/7GeerG-HIlF
Which results in the agent throwing multiple unknown primary or operator errors during runtime:
DEBUG runner/runner.go:235 Action result {"correlation_id": "01K7NZ3GJJXPZGR5P8NC5G9S27", "workflow_id": "39", "result": "Error running tool: exit status 1. Result: find: 2>/dev/null: unknown primary or operator\n"}
As a result, the agent is unable to finish the workflow and therefore it hangs indefinitely until a timeout occurs.
Proposal
Modify duo_workflow_service/tools/command.py to automatically detect shell operators and wrap commands in a shell when needed:
async def _arun(
self,
program: str,
args: Optional[str] = None,
) -> str:
args = args or ""
# Existing validation checks...
for disallowed_operator in _DISALLOWED_OPERATORS:
if disallowed_operator in program or disallowed_operator in args:
return f"'{disallowed_operator}' operators are not supported..."
for disallowed_command in _DISALLOWED_COMMANDS:
if program.startswith(disallowed_command):
return f"{disallowed_command} commands are not supported..."
# NEW: Detect shell operators
shell_operators = ['>', '', '&>', '>>']
needs_shell = any(op in args for op in shell_operators)
if needs_shell:
# Execute via shell to handle operators
full_command = f"{program} {args}"
return await _execute_action(
self.metadata,
contract_pb2.Action(
runCommand=contract_pb2.RunCommandAction(
program="sh",
arguments=["-c", full_command],
flags=[],
)
),
)
else:
# Direct execution (existing behavior)
return await _execute_action(
self.metadata,
contract_pb2.Action(
runCommand=contract_pb2.RunCommandAction(
program=program,
arguments=args.split(),
flags=[],
)
),
)
Further details
Benefits:
- Agents can use standard shell redirection without errors
- Commands without shell operators continue to execute directly (more secure)
- No breaking changes to existing functionality
- Minimal code modification