BUG-HUNT: [security] Path traversal vulnerability in LSP runtime file operations #7101

Open
opened 2026-04-10 07:42:50 +00:00 by HAL9000 · 1 comment
Owner

Metadata

  • Branch: bugfix/m3.6.0-lsp-runtime-path-traversal
  • Commit Message: fix(lsp): add workspace boundary validation to prevent path traversal in runtime file operations
  • Milestone: v3.6.0
  • Parent Epic: #824

Background and Context

The LSP runtime (src/cleveragents/lsp/runtime.py) contains path traversal vulnerabilities in two locations that together allow a malicious actor to read arbitrary files outside the workspace boundaries. The vulnerabilities exist in _path_to_uri() (lines 366–370) and _read_file() (line 379).

Vulnerability 1 — _path_to_uri() (lines 366–370): The method blindly prepends "file://" to any path string without validating the path content. A caller can supply a path containing .. sequences or absolute paths pointing outside the workspace, and the resulting URI will faithfully encode the traversal.

Vulnerability 2 — _read_file() (lines 379–384): The method calls os.path.realpath() to resolve symlinks and relative components, which is correct, but it never validates that the resolved path remains within the workspace root. After resolution, open(resolved, ...) will open any file accessible to the process — including sensitive system files, credentials, or configuration outside the workspace.

Evidence

# Lines 366-370 in runtime.py — No path validation before URI construction
@staticmethod
def _path_to_uri(file_path: str) -> str:
    """Convert a filesystem path to a ``file://`` URI."""
    if file_path.startswith("file://"):
        return file_path
    return f"file://{file_path}"  # No validation of file_path content

# Lines 379-384 in _read_file() — Resolves path but no workspace boundary check
resolved = os.path.realpath(file_path)
if not os.path.isfile(resolved):
    raise LspError(f"Not a regular file: {file_path}")
with open(resolved, encoding="utf-8") as f:  # Opens any resolved path
    return f.read()

Current Behavior

The system allows reading arbitrary files on the filesystem if accessible to the process. A crafted file path such as ../../etc/passwd or an absolute path like /home/user/.ssh/id_rsa will be resolved and opened without restriction, exposing sensitive files outside the workspace.

Expected Behavior

File operations must validate that all paths remain within the configured workspace root directory. Any path that resolves outside the workspace boundary must be rejected with an LspError before any I/O is attempted.

Acceptance Criteria

  • _path_to_uri() rejects or sanitizes paths containing .. sequences or absolute paths that escape the workspace root
  • _read_file() validates that os.path.realpath(file_path) resolves to a path that starts with the workspace root; raises LspError with a clear message if the boundary is violated
  • A workspace root configuration is available to the runtime (injected or resolved from context) and used as the boundary for all path validation
  • No regression in legitimate file reads within the workspace
  • BDD scenarios cover: path traversal via .., absolute path outside workspace, symlink pointing outside workspace, and valid paths within workspace

Subtasks

  • Identify how workspace root is (or should be) made available to LspRuntime — inject via constructor or resolve from project context
  • Implement _validate_workspace_path(resolved: str, workspace_root: str) -> None helper that raises LspError if resolved does not start with workspace_root
  • Update _read_file() to call _validate_workspace_path() after os.path.realpath() resolution
  • Update _path_to_uri() to reject paths that would escape the workspace (or delegate validation to callers)
  • Tests (Behave): Add @tdd_issue @tdd_issue_<N> @tdd_expected_fail scenarios capturing the path traversal behavior for both _path_to_uri() and _read_file()
  • Tests (Robot): Add integration test verifying workspace boundary enforcement end-to-end
  • Verify coverage ≥ 97% via nox -s coverage_report
  • Run nox (all default sessions), fix any errors

Definition of Done

This issue is complete when:

  • All subtasks above are completed and checked off.
  • A Git commit is created where the first line of the commit message matches the Commit Message in Metadata exactly, followed by a blank line, then additional lines providing relevant details about the implementation.
  • The commit is pushed to the remote on the branch matching the Branch in Metadata exactly.
  • The commit is submitted as a pull request to master, reviewed, and merged before this issue is marked done.
  • All nox stages pass.
  • Coverage ≥ 97%.

Note: Per the Bug Fix Workflow in CONTRIBUTING.md, a companion Type/Testing TDD issue must be created with the TDD: prefix before this fix is implemented. The original bug issue depends on (is blocked by) the TDD issue.


Automated by CleverAgents Bot
Supervisor: Bug Hunting | Agent: new-issue-creator

## Metadata - **Branch**: `bugfix/m3.6.0-lsp-runtime-path-traversal` - **Commit Message**: `fix(lsp): add workspace boundary validation to prevent path traversal in runtime file operations` - **Milestone**: v3.6.0 - **Parent Epic**: #824 ## Background and Context The LSP runtime (`src/cleveragents/lsp/runtime.py`) contains path traversal vulnerabilities in two locations that together allow a malicious actor to read arbitrary files outside the workspace boundaries. The vulnerabilities exist in `_path_to_uri()` (lines 366–370) and `_read_file()` (line 379). **Vulnerability 1 — `_path_to_uri()` (lines 366–370):** The method blindly prepends `"file://"` to any path string without validating the path content. A caller can supply a path containing `..` sequences or absolute paths pointing outside the workspace, and the resulting URI will faithfully encode the traversal. **Vulnerability 2 — `_read_file()` (lines 379–384):** The method calls `os.path.realpath()` to resolve symlinks and relative components, which is correct, but it never validates that the resolved path remains within the workspace root. After resolution, `open(resolved, ...)` will open any file accessible to the process — including sensitive system files, credentials, or configuration outside the workspace. ### Evidence ```python # Lines 366-370 in runtime.py — No path validation before URI construction @staticmethod def _path_to_uri(file_path: str) -> str: """Convert a filesystem path to a ``file://`` URI.""" if file_path.startswith("file://"): return file_path return f"file://{file_path}" # No validation of file_path content # Lines 379-384 in _read_file() — Resolves path but no workspace boundary check resolved = os.path.realpath(file_path) if not os.path.isfile(resolved): raise LspError(f"Not a regular file: {file_path}") with open(resolved, encoding="utf-8") as f: # Opens any resolved path return f.read() ``` ## Current Behavior The system allows reading arbitrary files on the filesystem if accessible to the process. A crafted file path such as `../../etc/passwd` or an absolute path like `/home/user/.ssh/id_rsa` will be resolved and opened without restriction, exposing sensitive files outside the workspace. ## Expected Behavior File operations must validate that all paths remain within the configured workspace root directory. Any path that resolves outside the workspace boundary must be rejected with an `LspError` before any I/O is attempted. ## Acceptance Criteria - `_path_to_uri()` rejects or sanitizes paths containing `..` sequences or absolute paths that escape the workspace root - `_read_file()` validates that `os.path.realpath(file_path)` resolves to a path that starts with the workspace root; raises `LspError` with a clear message if the boundary is violated - A workspace root configuration is available to the runtime (injected or resolved from context) and used as the boundary for all path validation - No regression in legitimate file reads within the workspace - BDD scenarios cover: path traversal via `..`, absolute path outside workspace, symlink pointing outside workspace, and valid paths within workspace ## Subtasks - [ ] Identify how workspace root is (or should be) made available to `LspRuntime` — inject via constructor or resolve from project context - [ ] Implement `_validate_workspace_path(resolved: str, workspace_root: str) -> None` helper that raises `LspError` if `resolved` does not start with `workspace_root` - [ ] Update `_read_file()` to call `_validate_workspace_path()` after `os.path.realpath()` resolution - [ ] Update `_path_to_uri()` to reject paths that would escape the workspace (or delegate validation to callers) - [ ] Tests (Behave): Add `@tdd_issue @tdd_issue_<N> @tdd_expected_fail` scenarios capturing the path traversal behavior for both `_path_to_uri()` and `_read_file()` - [ ] Tests (Robot): Add integration test verifying workspace boundary enforcement end-to-end - [ ] Verify coverage ≥ 97% via `nox -s coverage_report` - [ ] Run `nox` (all default sessions), fix any errors ## Definition of Done This issue is complete when: - All subtasks above are completed and checked off. - A Git commit is created where the **first line** of the commit message matches the Commit Message in Metadata exactly, followed by a blank line, then additional lines providing relevant details about the implementation. - The commit is pushed to the remote on the branch matching the **Branch** in Metadata exactly. - The commit is submitted as a **pull request** to `master`, reviewed, and **merged** before this issue is marked done. - All nox stages pass. - Coverage ≥ 97%. > **Note:** Per the Bug Fix Workflow in CONTRIBUTING.md, a companion `Type/Testing` TDD issue must be created with the `TDD:` prefix before this fix is implemented. The original bug issue depends on (is blocked by) the TDD issue. --- **Automated by CleverAgents Bot** Supervisor: Bug Hunting | Agent: new-issue-creator
HAL9000 added this to the v3.6.0 milestone 2026-04-10 07:42:57 +00:00
Author
Owner

Verified — Critical security bug: path traversal in LSP runtime file operations. MoSCoW: Must-have. Priority: Critical.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Verified** — Critical security bug: path traversal in LSP runtime file operations. MoSCoW: Must-have. Priority: Critical. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-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.

Blocks
#824 Epic: LSP Functional Runtime
cleveragents/cleveragents-core
Reference
cleveragents/cleveragents-core#7101
No description provided.