Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions src/agentready/models/fix.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,25 +140,39 @@ class CommandFix(Fix):
repository_path: Path

def apply(self, dry_run: bool = False) -> bool:
"""Execute the command."""
"""Execute the command.

Security: Uses shlex.split() to safely parse commands without shell=True,
preventing command injection attacks.
"""
if dry_run:
return True

import shlex
import subprocess

cwd = self.working_dir or self.repository_path

try:
# Security: Parse command safely without shell interpretation
# This prevents injection via shell metacharacters (;, |, &&, etc.)
cmd_list = shlex.split(self.command)

# Validate that we have a command to run
if not cmd_list:
return False

subprocess.run(
self.command,
shell=True,
cmd_list,
cwd=cwd,
check=True,
capture_output=True,
text=True,
# Security: Never use shell=True - explicitly removed
)
return True
except subprocess.CalledProcessError:
except (subprocess.CalledProcessError, ValueError):
# ValueError can be raised by shlex.split() on malformed input
Comment on lines +174 to +175

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Handle missing commands without raising

Replacing shell=True with shlex.split() means subprocess.run now raises FileNotFoundError when the executable is missing, but apply only catches CalledProcessError/ValueError. A mistyped or unavailable command will therefore bubble an exception and crash the remediation flow instead of returning False as before, violating the method’s contract and previous behavior.

Useful? React with 👍 / 👎.

return False

def preview(self) -> str:
Expand Down
Loading