BUG-HUNT: [security] InlineToolExecutor sentinel strings __INLINE_OK__ / __INLINE_ERR__ can be spoofed by inline tool code — false success or error injection #6592

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

Bug Report: Security — Output Sentinel String Spoofing

Severity Assessment

  • Impact: An inline tool that intentionally or accidentally prints the sentinel string __INLINE_OK__ can cause a genuinely failing execution to be reported as successful. Conversely, printing __INLINE_ERR__<message> to stderr poisons the error message returned to the caller. This breaks the integrity of the execution outcome reporting.
  • Likelihood: Medium — any tool printing debug info or processing text that happens to contain these strings will trigger false results. A malicious skill author can exploit this deliberately.
  • Priority: High

Location

  • File: src/cleveragents/skills/inline_executor.py
  • Function: InlineToolExecutor._run_with_timeout
  • Lines: ~308–315 (wrapper script), ~378–394 (sentinel parsing)

Description

The inline executor determines success or failure by embedding sentinel strings in the subprocess's stdout/stderr streams. The wrapper script injected into the child process uses:

# inline_executor.py  lines 308–315 — wrapper executed in subprocess
wrapper = textwrap.dedent("""\
    import json, sys
    input_data = json.loads(sys.argv[1])
    try:
        exec(sys.argv[2])
        print("__INLINE_OK__")
    except Exception as _exc:
        print(f"__INLINE_ERR__{_exc}", file=sys.stderr)
        sys.exit(1)
""")

The parent then checks:

# lines 378–394
if proc.returncode == 0 and "__INLINE_OK__" in raw_stdout:
    ...  # treated as success

if "__INLINE_ERR__" in raw_stderr:
    error_msg = raw_stderr.split("__INLINE_ERR__", 1)[1].strip()

Attack 1 — False success with returncode != 0:
If the inline tool code raises an exception but a previous print() call already wrote __INLINE_OK__ to stdout, and the exit code somehow becomes 0 via os._exit(0), the execution is declared successful even though an exception occurred.

Attack 2 — False success via stdout injection:
Tool code that prints __INLINE_OK__ to stdout before raising an exception causes the parent to see the sentinel in raw_stdout, but returncode will be 1, so this specific combination is blocked. However:

Attack 3 — Error message hijacking via stderr injection (confirmed):
Tool code that writes __INLINE_ERR__injected_error_message to stderr before any real exception will have its text embedded in the error_msg returned to the caller, completely replacing the actual exception message:

# Tool code that hijacks the error message:
import sys
sys.stderr.write("__INLINE_ERR__injected fake error\n")
# ... do anything, error message is now hijacked

Attack 4 — Accidental pollution:
A logging library or data-processing tool that happens to produce output containing these strings will corrupt result parsing.

Expected Behavior

The success/failure determination and error messages should not be injectable via the tool's output stream.

Actual Behavior

Any inline tool code can write the sentinel strings to stdout/stderr to manipulate the result reported to the caller.

Suggested Fix

Use the subprocess exit code as the sole success/failure discriminator, and use a side-channel (e.g., a dedicated file descriptor, a temporary JSON result file, or the process exit code) for the structured result:

# Option A: use exit code only
if proc.returncode == 0:
    # success — output is whatever the tool printed
    return InlineToolResult(success=True, output=raw_stdout, ...)
else:
    return InlineToolResult(success=False, error_message=raw_stderr, ...)

# Option B: write JSON result to a temp file (fd-3 pattern)
# Child writes {"ok": true, "output": "..."} or {"ok": false, "error": "..."} 
# to a temp file; parent reads the file for structured results

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 — Output Sentinel String Spoofing ### Severity Assessment - **Impact**: An inline tool that intentionally or accidentally prints the sentinel string `__INLINE_OK__` can cause a genuinely failing execution to be reported as successful. Conversely, printing `__INLINE_ERR__<message>` to stderr poisons the error message returned to the caller. This breaks the integrity of the execution outcome reporting. - **Likelihood**: Medium — any tool printing debug info or processing text that happens to contain these strings will trigger false results. A malicious skill author can exploit this deliberately. - **Priority**: High ### Location - **File**: `src/cleveragents/skills/inline_executor.py` - **Function**: `InlineToolExecutor._run_with_timeout` - **Lines**: ~308–315 (wrapper script), ~378–394 (sentinel parsing) ### Description The inline executor determines success or failure by embedding sentinel strings in the subprocess's stdout/stderr streams. The wrapper script injected into the child process uses: ```python # inline_executor.py lines 308–315 — wrapper executed in subprocess wrapper = textwrap.dedent("""\ import json, sys input_data = json.loads(sys.argv[1]) try: exec(sys.argv[2]) print("__INLINE_OK__") except Exception as _exc: print(f"__INLINE_ERR__{_exc}", file=sys.stderr) sys.exit(1) """) ``` The parent then checks: ```python # lines 378–394 if proc.returncode == 0 and "__INLINE_OK__" in raw_stdout: ... # treated as success if "__INLINE_ERR__" in raw_stderr: error_msg = raw_stderr.split("__INLINE_ERR__", 1)[1].strip() ``` **Attack 1 — False success with `returncode != 0`**: If the inline tool code raises an exception but a previous `print()` call already wrote `__INLINE_OK__` to stdout, and the exit code somehow becomes 0 via `os._exit(0)`, the execution is declared successful even though an exception occurred. **Attack 2 — False success via stdout injection**: Tool code that prints `__INLINE_OK__` to stdout before raising an exception causes the parent to see the sentinel in raw_stdout, but `returncode` will be 1, so this specific combination is blocked. However: **Attack 3 — Error message hijacking via stderr injection** (confirmed): Tool code that writes `__INLINE_ERR__injected_error_message` to stderr before any real exception will have its text embedded in the `error_msg` returned to the caller, completely replacing the actual exception message: ```python # Tool code that hijacks the error message: import sys sys.stderr.write("__INLINE_ERR__injected fake error\n") # ... do anything, error message is now hijacked ``` **Attack 4 — Accidental pollution**: A logging library or data-processing tool that happens to produce output containing these strings will corrupt result parsing. ### Expected Behavior The success/failure determination and error messages should not be injectable via the tool's output stream. ### Actual Behavior Any inline tool code can write the sentinel strings to stdout/stderr to manipulate the result reported to the caller. ### Suggested Fix Use the subprocess exit code as the **sole** success/failure discriminator, and use a **side-channel** (e.g., a dedicated file descriptor, a temporary JSON result file, or the process exit code) for the structured result: ```python # Option A: use exit code only if proc.returncode == 0: # success — output is whatever the tool printed return InlineToolResult(success=True, output=raw_stdout, ...) else: return InlineToolResult(success=False, error_message=raw_stderr, ...) # Option B: write JSON result to a temp file (fd-3 pattern) # Child writes {"ok": true, "output": "..."} or {"ok": false, "error": "..."} # to a temp file; parent reads the file for structured results ``` ### 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
HAL9000 added this to the v3.2.0 milestone 2026-04-09 22:13:18 +00:00
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#6592
No description provided.