BUG-HUNT: [resource] builtin/file-read tool has no output size limit — reading a large file causes unbounded memory allocation #6580

Open
opened 2026-04-09 21:45:19 +00:00 by HAL9000 · 1 comment
Owner

Bug Report: [resource] — _handle_file_read has no output size limit, enabling memory exhaustion

Severity Assessment

  • Impact: An agent (or malicious tool call) can trigger unbounded memory allocation by reading a very large file (e.g., a multi-GB log or binary). The Python process will OOM-kill or run out of memory, taking down the entire CleverAgents runtime.
  • Likelihood: Medium — any tool call to builtin/file-read on a large file triggers this.
  • Priority: High

Location

  • File: src/cleveragents/tool/builtins/file_tools.py
  • Function: _handle_file_read
  • Lines: 96–115

Description

_handle_file_read reads entire file content into memory via path.read_text() with no size guard. There is no maximum file size check before reading, and the limit parameter only trims lines — the initial path.read_text() call on line 103 has already loaded the entire file by then.

def _handle_file_read(inputs: dict[str, Any]) -> dict[str, Any]:
    path = validate_path(inputs["path"], inputs.get("sandbox_root"))
    encoding: str = inputs.get("encoding", "utf-8")
    offset: int = inputs.get("offset", 0)
    limit: int | None = inputs.get("limit")

    content = path.read_text(encoding=encoding)   # <-- BUG: entire file read with no size limit
    lines = content.splitlines(keepends=True)

    if offset > 0 or limit is not None:
        end = offset + limit if limit is not None else len(lines)
        lines = lines[offset:end]
        content = "".join(lines)

    return {
        "content": content,     # <-- potentially returned verbatim, no truncation
        "size": path.stat().st_size,
        "encoding": encoding,
    }

Compare this to ContainerToolExecutor._run_command() which has _MAX_OUTPUT_BYTES = 50 * 1024 * 1024 as a bounded read guard. The builtin/file-read tool has no analogous guard.

Expected Behavior

File reads should be size-bounded. Attempts to read files exceeding a configurable maximum size (e.g., 10 MiB) should either:

  1. Return an error with success=False (recommended), or
  2. Return only the first N bytes with a truncation warning in the output.

Actual Behavior

Reading a 2 GB file via builtin/file-read will:

  1. Load the entire 2 GB into a Python string (path.read_text()),
  2. Split into lines (another 2 GB allocation),
  3. Serialize to JSON (another 2 GB+ allocation),
    resulting in potentially 6–8 GB of peak memory usage before OOM.

Suggested Fix

Add a size check before reading:

_MAX_FILE_READ_BYTES = 10 * 1024 * 1024  # 10 MiB

def _handle_file_read(inputs: dict[str, Any]) -> dict[str, Any]:
    path = validate_path(inputs["path"], inputs.get("sandbox_root"))
    encoding: str = inputs.get("encoding", "utf-8")
    offset: int = inputs.get("offset", 0)
    limit: int | None = inputs.get("limit")

    # Guard against unbounded memory allocation
    file_size = path.stat().st_size
    if file_size > _MAX_FILE_READ_BYTES:
        raise ValueError(
            f"File size ({file_size} bytes) exceeds maximum allowed "
            f"read size ({_MAX_FILE_READ_BYTES} bytes). "
            f"Use offset/limit parameters to read in chunks."
        )

    content = path.read_text(encoding=encoding)
    ...

Category

resource

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

## Bug Report: [resource] — `_handle_file_read` has no output size limit, enabling memory exhaustion ### Severity Assessment - **Impact**: An agent (or malicious tool call) can trigger unbounded memory allocation by reading a very large file (e.g., a multi-GB log or binary). The Python process will OOM-kill or run out of memory, taking down the entire CleverAgents runtime. - **Likelihood**: Medium — any tool call to `builtin/file-read` on a large file triggers this. - **Priority**: High ### Location - **File**: `src/cleveragents/tool/builtins/file_tools.py` - **Function**: `_handle_file_read` - **Lines**: 96–115 ### Description `_handle_file_read` reads entire file content into memory via `path.read_text()` with no size guard. There is no maximum file size check before reading, and the `limit` parameter only trims *lines* — the initial `path.read_text()` call on line 103 has already loaded the entire file by then. ```python def _handle_file_read(inputs: dict[str, Any]) -> dict[str, Any]: path = validate_path(inputs["path"], inputs.get("sandbox_root")) encoding: str = inputs.get("encoding", "utf-8") offset: int = inputs.get("offset", 0) limit: int | None = inputs.get("limit") content = path.read_text(encoding=encoding) # <-- BUG: entire file read with no size limit lines = content.splitlines(keepends=True) if offset > 0 or limit is not None: end = offset + limit if limit is not None else len(lines) lines = lines[offset:end] content = "".join(lines) return { "content": content, # <-- potentially returned verbatim, no truncation "size": path.stat().st_size, "encoding": encoding, } ``` Compare this to `ContainerToolExecutor._run_command()` which has `_MAX_OUTPUT_BYTES = 50 * 1024 * 1024` as a bounded read guard. The `builtin/file-read` tool has no analogous guard. ### Expected Behavior File reads should be size-bounded. Attempts to read files exceeding a configurable maximum size (e.g., 10 MiB) should either: 1. Return an error with `success=False` (recommended), or 2. Return only the first N bytes with a truncation warning in the output. ### Actual Behavior Reading a 2 GB file via `builtin/file-read` will: 1. Load the entire 2 GB into a Python string (`path.read_text()`), 2. Split into lines (another 2 GB allocation), 3. Serialize to JSON (another 2 GB+ allocation), resulting in potentially 6–8 GB of peak memory usage before OOM. ### Suggested Fix Add a size check before reading: ```python _MAX_FILE_READ_BYTES = 10 * 1024 * 1024 # 10 MiB def _handle_file_read(inputs: dict[str, Any]) -> dict[str, Any]: path = validate_path(inputs["path"], inputs.get("sandbox_root")) encoding: str = inputs.get("encoding", "utf-8") offset: int = inputs.get("offset", 0) limit: int | None = inputs.get("limit") # Guard against unbounded memory allocation file_size = path.stat().st_size if file_size > _MAX_FILE_READ_BYTES: raise ValueError( f"File size ({file_size} bytes) exceeds maximum allowed " f"read size ({_MAX_FILE_READ_BYTES} bytes). " f"Use offset/limit parameters to read in chunks." ) content = path.read_text(encoding=encoding) ... ``` ### Category `resource` ### 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
HAL9000 added this to the v3.2.0 milestone 2026-04-09 21:52:43 +00:00
Author
Owner

Issue triaged by project owner:

  • State: Unverified
  • Priority: Critical — DENIAL OF SERVICE RISK: builtin/file-read tool has no output size limit. Reading a large file (e.g., a multi-GB log file or binary) could exhaust memory and crash the process.
  • Milestone: v3.2.0 — DoS vulnerabilities must be fixed in the earliest milestone
  • MoSCoW: Must Have — Unbounded resource consumption is a critical vulnerability

Security Impact: Without an output size limit, an actor (or malicious input) could cause the system to read arbitrarily large files, exhausting memory. A configurable max_bytes limit with a sensible default (e.g., 10MB) must be enforced.


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

Issue triaged by project owner: - **State**: Unverified - **Priority**: Critical — **DENIAL OF SERVICE RISK**: `builtin/file-read` tool has no output size limit. Reading a large file (e.g., a multi-GB log file or binary) could exhaust memory and crash the process. - **Milestone**: v3.2.0 — DoS vulnerabilities must be fixed in the earliest milestone - **MoSCoW**: Must Have — Unbounded resource consumption is a critical vulnerability **Security Impact**: Without an output size limit, an actor (or malicious input) could cause the system to read arbitrarily large files, exhausting memory. A configurable max_bytes limit with a sensible default (e.g., 10MB) must be enforced. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner
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#6580
No description provided.