Files
cleveragents-core/features/lsp_path_traversal_security.feature
T
HAL9000 2406e17d2a fix(lsp): validate workspace boundary in _read_file to prevent path traversal
Introduced strict workspace boundary checks in _read_file to ensure that any
resolved path remains within the workspace root, preventing path traversal.
The implementation resolves the requested path against the workspace root and
rejects paths that escape, returning a proper LspError to the client.

Added _validate_workspace_path() static helper that canonicalises both the
resolved path and the workspace root before comparing, ensuring symlinks and
dot-dot segments cannot bypass the check.

Added _workspace_roots dict to LspRuntime to track the workspace path per
server name, populated in start_server() and consumed by get_diagnostics(),
get_completions(), get_hover(), and get_definitions().

Added BDD scenarios in features/lsp_path_traversal_security.feature covering:
- Path traversal via dot-dot segments
- Absolute paths outside the workspace
- Symlinks pointing outside the workspace
- Valid paths within the workspace
- Backward-compatible behaviour when workspace_root is None

ISSUES CLOSED: #7215
2026-06-05 23:53:43 -04:00

88 lines
5.2 KiB
Gherkin

Feature: LSP runtime path traversal security
As a security-conscious platform operator
I want the LSP runtime to reject file paths that escape the workspace
So that malicious LSP clients cannot read sensitive system files
# _validate_workspace_path unit tests
Scenario: validate_workspace_path accepts a file directly inside workspace
Given lspsec a workspace root at a temp directory
And lspsec a file "safe.py" inside the workspace
When lspsec I call validate_workspace_path with the file inside workspace
Then lspsec no error should be raised
Scenario: validate_workspace_path accepts a file in a subdirectory of workspace
Given lspsec a workspace root at a temp directory
And lspsec a file "subdir/nested.py" inside the workspace
When lspsec I call validate_workspace_path with the nested file
Then lspsec no error should be raised
Scenario: validate_workspace_path rejects a path outside the workspace
Given lspsec a workspace root at a temp directory
When lspsec I call validate_workspace_path with absolute path "/etc/passwd"
Then lspsec an LspError should be raised with message containing "Path traversal attempt blocked"
Scenario: validate_workspace_path rejects a path that is a sibling directory
Given lspsec a workspace root at a temp directory
And lspsec a sibling directory exists next to the workspace
When lspsec I call validate_workspace_path with a file in the sibling directory
Then lspsec an LspError should be raised with message containing "Path traversal attempt blocked"
Scenario: validate_workspace_path rejects the workspace root itself
Given lspsec a workspace root at a temp directory
When lspsec I call validate_workspace_path with the workspace root itself
Then lspsec an LspError should be raised with message containing "Path traversal attempt blocked"
# ── _read_file with workspace_root ───────────────────────────────
Scenario: read_file succeeds for a valid file inside workspace
Given lspsec a workspace root at a temp directory
And lspsec a file "hello.py" inside the workspace with content "x = 1"
When lspsec I call read_file on workspace file "hello.py" with the workspace root
Then lspsec the file content should be "x = 1"
Scenario: read_file blocks path traversal via dot-dot segments
Given lspsec a workspace root at a temp directory
And lspsec a file "hello.py" inside the workspace with content "x = 1"
When lspsec I call read_file on a dot-dot traversal path targeting etc-passwd
Then lspsec an LspError should be raised with message containing "Path traversal attempt blocked"
Scenario: read_file blocks an absolute path outside the workspace
Given lspsec a workspace root at a temp directory
When lspsec I call read_file on absolute path "/etc/hostname" with the workspace root
Then lspsec an LspError should be raised with message containing "Path traversal attempt blocked"
Scenario: read_file blocks a symlink that points outside the workspace
Given lspsec a workspace root at a temp directory
And lspsec a symlink "evil_link.py" inside the workspace pointing to "/etc/passwd"
When lspsec I call read_file on the symlink with the workspace root
Then lspsec an LspError should be raised with message containing "Path traversal attempt blocked"
Scenario: read_file without workspace_root skips boundary check
Given lspsec a temp file outside any workspace with content "secret"
When lspsec I call read_file on the temp file without workspace_root
Then lspsec the file content should be "secret"
# ── LspRuntime.get_diagnostics with workspace boundary ───────────
Scenario: get_diagnostics blocks path traversal when workspace is registered
Given lspsec an LspRuntime with workspace root at a temp directory
And lspsec a healthy mock server "local/pyright" registered for the workspace
When lspsec I try to get diagnostics for "local/pyright" on absolute path "/etc/passwd"
Then lspsec an LspError should be raised with message containing "Path traversal attempt blocked"
Scenario: get_diagnostics succeeds for a valid file inside the workspace
Given lspsec an LspRuntime with workspace root at a temp directory
And lspsec a healthy mock server "local/pyright" registered for the workspace
And lspsec a file "main.py" inside the workspace with content "print('hello')"
When lspsec I get diagnostics for "local/pyright" on workspace file "main.py"
Then lspsec diagnostics should be returned as a list
# ── LspRuntime.get_completions with workspace boundary ───────────
Scenario: get_completions blocks path traversal when workspace is registered
Given lspsec an LspRuntime with workspace root at a temp directory
And lspsec a healthy mock server "local/pyright" registered for the workspace
When lspsec I try to get completions for "local/pyright" on absolute path "/etc/passwd" at line 1 column 1
Then lspsec an LspError should be raised with message containing "Path traversal attempt blocked"