BUG-HUNT: [security] Path traversal via unvalidated context_name in agents actor context import — attacker-controlled JSON file can write context files to arbitrary directories #6433

Open
opened 2026-04-09 21:02:57 +00:00 by HAL9000 · 0 comments
Owner

Bug Report: [security] — Path Traversal via Unvalidated context_name in agents actor context import

Severity Assessment

  • Impact: An attacker who can convince a user to import a crafted JSON/YAML context file can cause the CLI to create directories and write files at arbitrary filesystem paths outside the intended ~/.cleveragents/context/ directory. For example, a malicious context file with "context_name": "../../.ssh" would cause context data to be written to ~/.ssh/.
  • Likelihood: Medium — requires the user to import a file from an untrusted source. Not trivially exploitable but is a real attack vector (e.g., shared context files, CI pipelines consuming context exports from third-party sources).
  • Priority: High (security)

Location

  • File 1: src/cleveragents/cli/commands/actor_context.py

  • Function/Class: context_import() command

  • Lines: 395–410

  • File 2: src/cleveragents/reactive/context_manager.py

  • Function/Class: ContextManager.__init__()

  • Lines: 14–23

Description

When agents actor context import is run, the context_name is resolved from either:

  1. The positional CLI argument (name)
  2. The context_name field inside the imported JSON/YAML file

This resolved name is passed directly to ContextManager(resolved_name, context_dir) without any sanitization or path validation. ContextManager.__init__() constructs the context directory path by appending the name directly to the base path using Path / context_name, then calls mkdir(parents=True, exist_ok=True).

A context name containing .. components (e.g., "../../.ssh") will resolve to a path outside the intended context directory.

Evidence

actor_context.py — no validation before passing name to ContextManager:

# src/cleveragents/cli/commands/actor_context.py lines 395–412
resolved_name = name or file_data.get("context_name")  # ← from attacker-controlled file
if not resolved_name:
    resolved_name = input_file.stem

ctx_mgr = ContextManager(resolved_name, context_dir)   # ← NO validation!

if ctx_mgr.exists() and not update:
    ...

context_manager.py — directory is created immediately in __init__:

# src/cleveragents/reactive/context_manager.py lines 14–23
class ContextManager:
    def __init__(self, context_name: str, context_dir: Path | None = None):
        self.context_name = context_name
        if context_dir is None:
            home_dir = Path.home()
            self.context_dir = home_dir / ".cleveragents" / "context" / context_name
        else:
            self.context_dir = Path(context_dir) / context_name
        self.context_dir.mkdir(parents=True, exist_ok=True)  # ← CREATES DIRECTORIES IMMEDIATELY

Proof of attack path:
A malicious evil-context.json:

{
  "context_name": "../../.ssh",
  "messages": [{"role": "user", "content": "evil"}],
  "metadata": {},
  "state": {},
  "global_context": {}
}

Running:

agents actor context import --input evil-context.json

Results in:

  • ~/.cleveragents/context/../../.ssh/ being created (= ~/.ssh/)
  • ~/.ssh/messages.json, ~/.ssh/metadata.json, ~/.ssh/state.json, ~/.ssh/global_context.json being written

Expected Behavior

The import command should reject any context name containing .., /, or other path-traversal components. A clear error message should be shown: "Context name must not contain path separators or '..' components".

Actual Behavior

The context name from the imported file is used verbatim as a path component, allowing directory traversal outside the ~/.cleveragents/context/ base directory.

Suggested Fix

Add name validation in actor_context.py context_import() before passing to ContextManager:

import re

# Validate context name to prevent path traversal
_SAFE_CONTEXT_NAME_RE = re.compile(r'^[a-zA-Z0-9][a-zA-Z0-9_\-\.]{0,127}$')

resolved_name = name or file_data.get("context_name")
if not resolved_name:
    resolved_name = input_file.stem

# Guard against path traversal
if '..' in resolved_name or '/' in resolved_name or '\\' in resolved_name:
    typer.echo(
        f"Error: Context name '{resolved_name}' contains path traversal characters.",
        err=True,
    )
    raise typer.Exit(code=1)

if not _SAFE_CONTEXT_NAME_RE.match(resolved_name):
    typer.echo(
        f"Error: Context name '{resolved_name}' contains invalid characters. "
        "Use only alphanumerics, hyphens, underscores, and dots.",
        err=True,
    )
    raise typer.Exit(code=1)

Also consider adding this validation to ContextManager.__init__() as a defense-in-depth measure.

Category

security

TDD Note

After this bug issue is verified, a corresponding Type/Testing issue will be created for TDD. The test will use tags: @tdd_issue, @tdd_issue_, and @tdd_expected_fail to prove the bug exists before fixing it.


Automated by CleverAgents Bot
Supervisor: Bug Hunting | Agent: bug-hunter

## Bug Report: [security] — Path Traversal via Unvalidated `context_name` in `agents actor context import` ### Severity Assessment - **Impact**: An attacker who can convince a user to import a crafted JSON/YAML context file can cause the CLI to create directories and write files at **arbitrary filesystem paths** outside the intended `~/.cleveragents/context/` directory. For example, a malicious context file with `"context_name": "../../.ssh"` would cause context data to be written to `~/.ssh/`. - **Likelihood**: Medium — requires the user to import a file from an untrusted source. Not trivially exploitable but is a real attack vector (e.g., shared context files, CI pipelines consuming context exports from third-party sources). - **Priority**: High (security) ### Location - **File 1**: `src/cleveragents/cli/commands/actor_context.py` - **Function/Class**: `context_import()` command - **Lines**: 395–410 - **File 2**: `src/cleveragents/reactive/context_manager.py` - **Function/Class**: `ContextManager.__init__()` - **Lines**: 14–23 ### Description When `agents actor context import` is run, the `context_name` is resolved from either: 1. The positional CLI argument (`name`) 2. The `context_name` field **inside the imported JSON/YAML file** This resolved name is passed directly to `ContextManager(resolved_name, context_dir)` **without any sanitization or path validation**. `ContextManager.__init__()` constructs the context directory path by appending the name directly to the base path using `Path / context_name`, then calls `mkdir(parents=True, exist_ok=True)`. A context name containing `..` components (e.g., `"../../.ssh"`) will resolve to a path **outside the intended context directory**. ### Evidence **`actor_context.py` — no validation before passing name to ContextManager:** ```python # src/cleveragents/cli/commands/actor_context.py lines 395–412 resolved_name = name or file_data.get("context_name") # ← from attacker-controlled file if not resolved_name: resolved_name = input_file.stem ctx_mgr = ContextManager(resolved_name, context_dir) # ← NO validation! if ctx_mgr.exists() and not update: ... ``` **`context_manager.py` — directory is created immediately in `__init__`:** ```python # src/cleveragents/reactive/context_manager.py lines 14–23 class ContextManager: def __init__(self, context_name: str, context_dir: Path | None = None): self.context_name = context_name if context_dir is None: home_dir = Path.home() self.context_dir = home_dir / ".cleveragents" / "context" / context_name else: self.context_dir = Path(context_dir) / context_name self.context_dir.mkdir(parents=True, exist_ok=True) # ← CREATES DIRECTORIES IMMEDIATELY ``` **Proof of attack path:** A malicious `evil-context.json`: ```json { "context_name": "../../.ssh", "messages": [{"role": "user", "content": "evil"}], "metadata": {}, "state": {}, "global_context": {} } ``` Running: ```bash agents actor context import --input evil-context.json ``` Results in: - `~/.cleveragents/context/../../.ssh/` being created (= `~/.ssh/`) - `~/.ssh/messages.json`, `~/.ssh/metadata.json`, `~/.ssh/state.json`, `~/.ssh/global_context.json` being written ### Expected Behavior The import command should reject any context name containing `..`, `/`, or other path-traversal components. A clear error message should be shown: `"Context name must not contain path separators or '..' components"`. ### Actual Behavior The context name from the imported file is used verbatim as a path component, allowing directory traversal outside the `~/.cleveragents/context/` base directory. ### Suggested Fix Add name validation in `actor_context.py` `context_import()` before passing to `ContextManager`: ```python import re # Validate context name to prevent path traversal _SAFE_CONTEXT_NAME_RE = re.compile(r'^[a-zA-Z0-9][a-zA-Z0-9_\-\.]{0,127}$') resolved_name = name or file_data.get("context_name") if not resolved_name: resolved_name = input_file.stem # Guard against path traversal if '..' in resolved_name or '/' in resolved_name or '\\' in resolved_name: typer.echo( f"Error: Context name '{resolved_name}' contains path traversal characters.", err=True, ) raise typer.Exit(code=1) if not _SAFE_CONTEXT_NAME_RE.match(resolved_name): typer.echo( f"Error: Context name '{resolved_name}' contains invalid characters. " "Use only alphanumerics, hyphens, underscores, and dots.", err=True, ) raise typer.Exit(code=1) ``` Also consider adding this validation to `ContextManager.__init__()` as a defense-in-depth measure. ### Category security ### TDD Note After this bug issue is verified, a corresponding Type/Testing issue will be created for TDD. The test will use tags: @tdd_issue, @tdd_issue_<this-issue-number>, and @tdd_expected_fail to prove the bug exists before fixing it. --- **Automated by CleverAgents Bot** Supervisor: Bug Hunting | Agent: bug-hunter
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#6433
No description provided.