agents/graphs/plan_generation: Add test for path traversal via @path hint in _generate_plan #10358

Open
opened 2026-04-18 09:09:44 +00:00 by HAL9000 · 0 comments
Owner

Metadata

  • Commit: agents/graphs/plan_generation: add test for path traversal via @path hint in _generate_plan
  • Branch: test/plan-generation-path-traversal-hint

Background and Context

The PlanGenerationGraph._generate_plan() method extracts a file path from user prompts using an @path hint pattern. The regex PATH_HINT_RE = re.compile(r"@(?P<path>[\w./-]+)") permits ../ sequences, meaning a prompt like "Fix @../../etc/passwd" could cause the agent to read arbitrary files outside the project directory and include their contents in LLM prompts. This TDD issue establishes the failing test that must pass once the security fix is implemented.

Expected Behavior

PlanGenerationGraph._generate_plan() must reject any @path hint that resolves outside the project/workspace directory. Prompts containing path traversal sequences (e.g., @../../etc/passwd, @~/.ssh/id_rsa) must raise a ValueError, PermissionError, or a custom SecurityError rather than reading the file.

Acceptance Criteria

  • A test test_plan_generation_rejects_path_traversal_in_prompt exists in the agents/graphs/plan_generation test module
  • The test is decorated with @tdd_issue, @tdd_issue_1, and @tdd_expected_fail
  • The test constructs a PlanGenerationGraph with a FakeListLLM and a state whose prompt contains @../../etc/passwd
  • The test asserts that calling graph._generate_plan(state) raises ValueError, PermissionError, or SecurityError
  • The test fails (as expected) against the current unpatched codebase
  • The test passes once the path-traversal fix is applied (see linked Bug issue)

Subtasks

  • Locate the correct test file for agents/graphs/plan_generation
  • Write the failing test test_plan_generation_rejects_path_traversal_in_prompt with the scenario described below
  • Confirm the test fails on the current HEAD (expected-fail decorator)
  • Open the companion Bug issue and link it as a dependency

Definition of Done

This issue is closed when:

  1. The failing test is merged to the relevant feature/fix branch
  2. The companion Bug issue fix causes the test to pass
  3. CI confirms the test transitions from expected-fail to passing

Failing Scenario

@tdd_issue
@tdd_issue_1
@tdd_expected_fail
def test_plan_generation_rejects_path_traversal_in_prompt():
    """Path hints with traversal sequences must be rejected."""
    from pathlib import Path
    from unittest.mock import MagicMock, patch
    from langchain_community.llms import FakeListLLM
    from cleveragents.agents.graphs.plan_generation import PlanGenerationGraph
    from cleveragents.domain.models.core import Plan, Project

    mock_llm = FakeListLLM(responses=["requirements", "generated code", "PASS"])
    graph = PlanGenerationGraph(llm=mock_llm)

    # Simulate a prompt with a path traversal hint
    plan = MagicMock(spec=Plan)
    plan.prompt = "Fix the config @../../etc/passwd"
    plan.id = 1
    project = MagicMock(spec=Project)

    state = {
        "project": project,
        "plan": plan,
        "contexts": [],
        "context_summary": "",
        "context_dependencies": {},
        "context_relevance": {},
        "context_analysis_error": None,
        "actor_name": None,
        "actor_options": {},
        "actor_graph_descriptor": None,
        "actor_initial_context": {},
        "prompt": "Fix the config @../../etc/passwd",
        "analyzed_requirements": {"description": "fix", "files_to_modify": [], "operation": "create"},
        "generated_changes": [],
        "validation_result": {},
        "retry_count": 0,
        "error": None,
    }

    # Should raise ValueError or SecurityError for path traversal
    with pytest.raises((ValueError, PermissionError, SecurityError)):
        graph._generate_plan(state)

Root Cause

In src/cleveragents/agents/graphs/plan_generation.py, the _generate_plan() method extracts a path from the user prompt using _extract_path_from_prompt() and then reads the file without validating it stays within the project directory:

explicit_path = self._extract_path_from_prompt(state.get("prompt", ""))
# ...
existing_path = Path(file_path)
if existing_path.exists():
    if existing_path.is_dir():
        # ...
    else:
        operation_type = OperationType.MODIFY
        try:
            original_content = existing_path.read_text()  # BUG: reads arbitrary files!
        except Exception:
            original_content = None

The PATH_HINT_RE = re.compile(r"@(?P<path>[\w./-]+)") regex allows ../ sequences, enabling path traversal. A prompt like "Fix @../../etc/passwd" would cause the agent to read /etc/passwd and include its contents in the LLM prompt.

Expected Fix

Validate that the resolved path is within the project/workspace directory:

def _extract_path_from_prompt(self, prompt: str) -> str | None:
    match = PATH_HINT_RE.search(prompt or "")
    if not match:
        return None
    path = match.group("path")
    # Reject paths with traversal sequences
    resolved = Path(path).resolve()
    # Must be validated against project root before use
    return path

Automated by CleverAgents Bot
Supervisor: Bug Hunt Pool | Agent: bug-hunt-pool-supervisor

## Metadata - **Commit:** `agents/graphs/plan_generation: add test for path traversal via @path hint in _generate_plan` - **Branch:** `test/plan-generation-path-traversal-hint` ## Background and Context The `PlanGenerationGraph._generate_plan()` method extracts a file path from user prompts using an `@path` hint pattern. The regex `PATH_HINT_RE = re.compile(r"@(?P<path>[\w./-]+)")` permits `../` sequences, meaning a prompt like `"Fix @../../etc/passwd"` could cause the agent to read arbitrary files outside the project directory and include their contents in LLM prompts. This TDD issue establishes the failing test that must pass once the security fix is implemented. ## Expected Behavior `PlanGenerationGraph._generate_plan()` must reject any `@path` hint that resolves outside the project/workspace directory. Prompts containing path traversal sequences (e.g., `@../../etc/passwd`, `@~/.ssh/id_rsa`) must raise a `ValueError`, `PermissionError`, or a custom `SecurityError` rather than reading the file. ## Acceptance Criteria - [ ] A test `test_plan_generation_rejects_path_traversal_in_prompt` exists in the `agents/graphs/plan_generation` test module - [ ] The test is decorated with `@tdd_issue`, `@tdd_issue_1`, and `@tdd_expected_fail` - [ ] The test constructs a `PlanGenerationGraph` with a `FakeListLLM` and a state whose `prompt` contains `@../../etc/passwd` - [ ] The test asserts that calling `graph._generate_plan(state)` raises `ValueError`, `PermissionError`, or `SecurityError` - [ ] The test **fails** (as expected) against the current unpatched codebase - [ ] The test **passes** once the path-traversal fix is applied (see linked Bug issue) ## Subtasks - [ ] Locate the correct test file for `agents/graphs/plan_generation` - [ ] Write the failing test `test_plan_generation_rejects_path_traversal_in_prompt` with the scenario described below - [ ] Confirm the test fails on the current HEAD (expected-fail decorator) - [ ] Open the companion Bug issue and link it as a dependency ## Definition of Done This issue is closed when: 1. The failing test is merged to the relevant feature/fix branch 2. The companion Bug issue fix causes the test to pass 3. CI confirms the test transitions from expected-fail to passing --- ## Failing Scenario ```python @tdd_issue @tdd_issue_1 @tdd_expected_fail def test_plan_generation_rejects_path_traversal_in_prompt(): """Path hints with traversal sequences must be rejected.""" from pathlib import Path from unittest.mock import MagicMock, patch from langchain_community.llms import FakeListLLM from cleveragents.agents.graphs.plan_generation import PlanGenerationGraph from cleveragents.domain.models.core import Plan, Project mock_llm = FakeListLLM(responses=["requirements", "generated code", "PASS"]) graph = PlanGenerationGraph(llm=mock_llm) # Simulate a prompt with a path traversal hint plan = MagicMock(spec=Plan) plan.prompt = "Fix the config @../../etc/passwd" plan.id = 1 project = MagicMock(spec=Project) state = { "project": project, "plan": plan, "contexts": [], "context_summary": "", "context_dependencies": {}, "context_relevance": {}, "context_analysis_error": None, "actor_name": None, "actor_options": {}, "actor_graph_descriptor": None, "actor_initial_context": {}, "prompt": "Fix the config @../../etc/passwd", "analyzed_requirements": {"description": "fix", "files_to_modify": [], "operation": "create"}, "generated_changes": [], "validation_result": {}, "retry_count": 0, "error": None, } # Should raise ValueError or SecurityError for path traversal with pytest.raises((ValueError, PermissionError, SecurityError)): graph._generate_plan(state) ``` ## Root Cause In `src/cleveragents/agents/graphs/plan_generation.py`, the `_generate_plan()` method extracts a path from the user prompt using `_extract_path_from_prompt()` and then reads the file without validating it stays within the project directory: ```python explicit_path = self._extract_path_from_prompt(state.get("prompt", "")) # ... existing_path = Path(file_path) if existing_path.exists(): if existing_path.is_dir(): # ... else: operation_type = OperationType.MODIFY try: original_content = existing_path.read_text() # BUG: reads arbitrary files! except Exception: original_content = None ``` The `PATH_HINT_RE = re.compile(r"@(?P<path>[\w./-]+)")` regex allows `../` sequences, enabling path traversal. A prompt like `"Fix @../../etc/passwd"` would cause the agent to read `/etc/passwd` and include its contents in the LLM prompt. ## Expected Fix Validate that the resolved path is within the project/workspace directory: ```python def _extract_path_from_prompt(self, prompt: str) -> str | None: match = PATH_HINT_RE.search(prompt or "") if not match: return None path = match.group("path") # Reject paths with traversal sequences resolved = Path(path).resolve() # Must be validated against project root before use return path ``` --- **Automated by CleverAgents Bot** Supervisor: Bug Hunt Pool | Agent: bug-hunt-pool-supervisor
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core#10358
No description provided.