Skip to content

[P0] Command Execution Timeout Missing - DoS Vulnerability #102

Description

@jeremyeder

Summary

The CommandFix.apply() method calls subprocess.run() without a timeout, creating a DoS vulnerability where malicious or buggy commands can hang indefinitely. This bypasses the project's security guardrails (all other subprocess calls use safe_subprocess_run() with 120s timeout).

Impact

  • Malicious commands can hang indefinitely (e.g., sleep 999999)
  • Blocks entire assessment process
  • Resource exhaustion on CI/CD systems
  • Inconsistent with project's established security patterns

Location

  • File: src/agentready/models/fix.py
  • Lines: 165-172
  • Function: CommandFix.apply()

Current Code

subprocess.run(
    cmd_list,
    cwd=cwd,
    check=True,
    capture_output=True,
    text=True,
    # Security: Never use shell=True - explicitly removed
)

Solution

Replace direct subprocess.run() call with project's safe_subprocess_run() wrapper:

from ..utils.subprocess_utils import safe_subprocess_run, SUBPROCESS_TIMEOUT

try:
    result = safe_subprocess_run(
        cmd_list,
        cwd=cwd,
        check=True,
        capture_output=True,
        text=True,
        timeout=SUBPROCESS_TIMEOUT,  # 120 seconds
    )

    return FixResult(
        success=True,
        message=f"Command executed successfully: {' '.join(cmd_list)}",
        details=result.stdout if result.stdout else None,
    )
except subprocess.TimeoutExpired as e:
    return FixResult(
        success=False,
        message=f"Command timed out after {SUBPROCESS_TIMEOUT}s: {' '.join(cmd_list)}",
        details=f"Timeout limit: {SUBPROCESS_TIMEOUT}s. Command may be hanging or taking too long.",
    )
except subprocess.CalledProcessError as e:
    return FixResult(
        success=False,
        message=f"Command failed with exit code {e.returncode}: {' '.join(cmd_list)}",
        details=e.stderr if e.stderr else str(e),
    )

Testing

# 1. Run unit tests
pytest tests/unit/test_fix.py -v

# 2. Manual timeout test (should complete in ~120s, not hang forever)
python -c "
from agentready.models import CommandFix, Repository
from pathlib import Path
import time

fix = CommandFix.from_dict({
    'attribute_id': 'test',
    'priority': 1,
    'description': 'Timeout test',
    'command': 'sleep 300',
    'auto_apply': False
})

repo = Repository(path=Path.cwd())
start = time.time()
result = fix.apply(repo)
duration = time.time() - start

print(f'Duration: {duration:.1f}s')
assert duration < 130, 'Should timeout around 120s'
assert not result.success
"

Acceptance Criteria

  • Import safe_subprocess_run added to fix.py
  • Direct subprocess.run() call removed
  • Timeout exception handling added with user-friendly messages
  • Unit test for timeout behavior added
  • Manual timeout test passes (completes in ~120s)
  • Regular commands still work (e.g., echo "test")
  • All existing tests pass

References

  • Project pattern: src/agentready/utils/subprocess_utils.py (SUBPROCESS_TIMEOUT = 120)
  • All other subprocess calls use safe_subprocess_run()
  • Full remediation plan: .plans/code-review-remediation-plan.md

Labels: security, bug, P0, good-first-issue
Milestone: v1.24.0

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions