[Bug Hunt][Cycle 2][Reactive] Template Injection Risk in SimpleLLMAgent #7154

Open
opened 2026-04-10 08:15:53 +00:00 by HAL9000 · 1 comment
Owner

Metadata

  • Branch: bugfix/reactive-template-injection-simplelllmagent
  • Commit Message: fix(reactive): add template string validation to SimpleLLMAgent._render_prompt()
  • Milestone: (none — see backlog note below)
  • Parent Epic: #7052

Background and Context

SimpleLLMAgent._render_prompt() in src/cleveragents/reactive/stream_router.py (lines 202–212) uses Jinja2 SandboxedEnvironment to render prompt templates, but performs no validation of the template string itself before passing it to from_string(). This means a caller who can supply an arbitrary template string may be able to:

  1. Bypass the sandbox via known Jinja2 sandbox-escape techniques (e.g., accessing __class__.__mro__ chains through object attributes exposed in the context).
  2. Cause denial of service through resource-exhausting constructs such as deeply recursive macros, infinite loops via {% for %} over generated sequences, or extremely large string repetitions.

The bare except Exception: return template fallback silently swallows all rendering errors, making exploitation attempts invisible in logs and preventing any alerting on malicious input.

Evidence

# src/cleveragents/reactive/stream_router.py, lines 202-212
# Template rendering without input validation
def _render_prompt(self, template: str, context: dict[str, Any]) -> str:
    if not template:
        return ""
    if self._template_env is None:
        return template
    try:
        return self._template_env.from_string(template).render(
            context=context, **context
        )
    except Exception:
        return template

Issues identified:

  • No allowlist/denylist check on template before from_string() is called.
  • No length cap on the template string (DoS vector).
  • No restriction on Jinja2 constructs (macros, loops, filters) that can exhaust resources.
  • Silent exception swallowing hides both errors and exploitation attempts.

Impact

  • Code execution risk: Sandbox bypass via attribute traversal on objects exposed in context.
  • Denial of service: Resource exhaustion through unbounded template complexity.
  • Observability gap: The except Exception: return template pattern means malicious or malformed templates produce no log entries, making detection impossible.

Severity

Medium — security risk with limited exposure. The attack surface is constrained to callers who control the template argument, but the silent failure mode and sandbox-bypass potential elevate this above a low-severity finding.

Subtasks

  • Audit all call sites of _render_prompt() to determine whether template strings can originate from untrusted input
  • Add a maximum template length guard (e.g., reject templates exceeding a configurable byte limit)
  • Add a Jinja2 AST pre-scan to block disallowed node types (e.g., CallBlock, macro definitions, getattr/getitem chains targeting dunder attributes)
  • Replace the bare except Exception: return template with explicit exception handling that logs a warning and raises or returns a safe error indicator
  • Add BDD scenarios covering: oversized template rejection, disallowed construct rejection, and error-path logging
  • Add integration test verifying sandbox cannot be escaped via common bypass patterns
  • Add ASV benchmark to confirm template validation overhead is within acceptable bounds

Definition of Done

  • _render_prompt() rejects templates exceeding the configured length limit
  • _render_prompt() rejects templates containing disallowed Jinja2 AST node types
  • All rendering errors are logged at WARNING level with the template hash (not the raw template) before falling back or raising
  • BDD feature file covers all new validation paths with Gherkin scenarios
  • Integration test confirms sandbox escape attempts are blocked
  • All nox stages pass
  • Coverage >= 97%

Backlog note: This issue was discovered during autonomous operation
on milestone v3.2.0. It does not block milestone completion and has been
placed in the backlog for human review and future milestone assignment.


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

## Metadata - **Branch**: `bugfix/reactive-template-injection-simplelllmagent` - **Commit Message**: `fix(reactive): add template string validation to SimpleLLMAgent._render_prompt()` - **Milestone**: *(none — see backlog note below)* - **Parent Epic**: #7052 ## Background and Context `SimpleLLMAgent._render_prompt()` in `src/cleveragents/reactive/stream_router.py` (lines 202–212) uses Jinja2 `SandboxedEnvironment` to render prompt templates, but performs **no validation of the template string itself** before passing it to `from_string()`. This means a caller who can supply an arbitrary template string may be able to: 1. **Bypass the sandbox** via known Jinja2 sandbox-escape techniques (e.g., accessing `__class__.__mro__` chains through object attributes exposed in the context). 2. **Cause denial of service** through resource-exhausting constructs such as deeply recursive macros, infinite loops via `{% for %}` over generated sequences, or extremely large string repetitions. The bare `except Exception: return template` fallback silently swallows all rendering errors, making exploitation attempts invisible in logs and preventing any alerting on malicious input. ## Evidence ```python # src/cleveragents/reactive/stream_router.py, lines 202-212 # Template rendering without input validation def _render_prompt(self, template: str, context: dict[str, Any]) -> str: if not template: return "" if self._template_env is None: return template try: return self._template_env.from_string(template).render( context=context, **context ) except Exception: return template ``` **Issues identified:** - No allowlist/denylist check on `template` before `from_string()` is called. - No length cap on the template string (DoS vector). - No restriction on Jinja2 constructs (macros, loops, filters) that can exhaust resources. - Silent exception swallowing hides both errors and exploitation attempts. ## Impact - **Code execution risk**: Sandbox bypass via attribute traversal on objects exposed in `context`. - **Denial of service**: Resource exhaustion through unbounded template complexity. - **Observability gap**: The `except Exception: return template` pattern means malicious or malformed templates produce no log entries, making detection impossible. ## Severity **Medium** — security risk with limited exposure. The attack surface is constrained to callers who control the `template` argument, but the silent failure mode and sandbox-bypass potential elevate this above a low-severity finding. ## Subtasks - [ ] Audit all call sites of `_render_prompt()` to determine whether template strings can originate from untrusted input - [ ] Add a maximum template length guard (e.g., reject templates exceeding a configurable byte limit) - [ ] Add a Jinja2 AST pre-scan to block disallowed node types (e.g., `CallBlock`, macro definitions, `getattr`/`getitem` chains targeting dunder attributes) - [ ] Replace the bare `except Exception: return template` with explicit exception handling that logs a warning and raises or returns a safe error indicator - [ ] Add BDD scenarios covering: oversized template rejection, disallowed construct rejection, and error-path logging - [ ] Add integration test verifying sandbox cannot be escaped via common bypass patterns - [ ] Add ASV benchmark to confirm template validation overhead is within acceptable bounds ## Definition of Done - [ ] `_render_prompt()` rejects templates exceeding the configured length limit - [ ] `_render_prompt()` rejects templates containing disallowed Jinja2 AST node types - [ ] All rendering errors are logged at `WARNING` level with the template hash (not the raw template) before falling back or raising - [ ] BDD feature file covers all new validation paths with Gherkin scenarios - [ ] Integration test confirms sandbox escape attempts are blocked - [ ] All nox stages pass - [ ] Coverage >= 97% > **Backlog note:** This issue was discovered during autonomous operation > on milestone v3.2.0. It does not block milestone completion and has been > placed in the backlog for human review and future milestone assignment. --- **Automated by CleverAgents Bot** Supervisor: Bug Hunting | Agent: new-issue-creator
Author
Owner

Verified — Security bug: template injection risk in SimpleLLMAgent. MoSCoW: Must-have. Priority: High.


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

✅ **Verified** — Security bug: template injection risk in SimpleLLMAgent. MoSCoW: Must-have. Priority: High. --- **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.

Reference
cleveragents/cleveragents-core#7154
No description provided.