BUG-HUNT: [consistency] validate_template() does not accept max_output_length, creating false validation confidence — templates that pass validation can still fail at render time #6574

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

Bug Report: [consistency] — validate_template() API omits max_output_length, creating asymmetry with render_template_secure()

Severity Assessment

  • Impact: A caller who pre-validates a template with validate_template(template, max_template_length=X) may receive an empty error list (valid!) but later encounter a TemplateSizeError at render time if the rendered output exceeds max_output_length. There is no way to pass a custom max_output_length into validate_template(), so output-size constraints cannot be validated ahead of time. This false validation confidence can lead to runtime exceptions in production paths where callers expected pre-validation to be comprehensive.
  • Likelihood: Medium — any caller that pre-validates templates with a strict max_output_length in the render step is silently under-validated.
  • Priority: Medium

Location

  • File: src/cleveragents/templates/secure_renderer.py
  • Function: validate_template
  • Lines: 288–312

Description

render_template_secure() (lines 315–346) accepts both max_template_length and max_output_length as parameters and builds a TemplateConfig with both bounds:

def render_template_secure(
    template: str,
    context: dict[str, Any],
    *,
    allowed_keys: frozenset[str] | None = None,
    max_template_length: int = DEFAULT_MAX_TEMPLATE_LENGTH,
    max_output_length: int = DEFAULT_MAX_OUTPUT_LENGTH,   # ← accepted here
) -> str:
    config = TemplateConfig(
        allowed_keys=allowed_keys or frozenset(),
        max_template_length=max_template_length,
        max_output_length=max_output_length,
    )

validate_template() (lines 288–312), however, only accepts max_template_length and hard-codes max_output_length to the default via TemplateConfig:

def validate_template(
    template: str,
    *,
    allowed_keys: frozenset[str] | None = None,
    max_template_length: int = DEFAULT_MAX_TEMPLATE_LENGTH,
    # max_output_length is NOT a parameter — no way to pass it
) -> list[str]:
    config = TemplateConfig(
        allowed_keys=allowed_keys or frozenset(),
        max_template_length=max_template_length,
        # max_output_length always defaults to DEFAULT_MAX_OUTPUT_LENGTH (50 000)
    )
    renderer = SecureTemplateRenderer(config=config)
    return renderer.validate(template)

Furthermore, SecureTemplateRenderer.validate() (lines 199–222) does not check output length at all — which is expected since output length requires a rendered result — but this means validate_template() cannot warn users that their template, given large context values, might blow up max_output_length at render time.

The concrete inconsistency: callers who want to use a tighter max_output_length (e.g. 5_000 instead of the default 50_000) with render_template_secure() cannot express that constraint to validate_template(). The validation step will always use the default 50 000-char output limit regardless.

Evidence

# src/cleveragents/templates/secure_renderer.py, lines 288–312

def validate_template(
    template: str,
    *,
    allowed_keys: frozenset[str] | None = None,
    max_template_length: int = DEFAULT_MAX_TEMPLATE_LENGTH,
    # ← max_output_length missing
) -> list[str]:
    config = TemplateConfig(
        allowed_keys=allowed_keys or frozenset(),
        max_template_length=max_template_length,
        # ← max_output_length silently defaults to 50 000
    )
    renderer = SecureTemplateRenderer(config=config)
    return renderer.validate(template)

Scenario demonstrating false confidence:

# Step 1: validate with a tight output limit — but validate_template doesn't support it
errors = validate_template(template, max_template_length=1000)
assert errors == []   # passes — no errors reported

# Step 2: render with a tight output limit
result = render_template_secure(
    template,
    context,
    max_template_length=1000,
    max_output_length=500,   # tighter than the default
)
# → TemplateSizeError: "Rendered output length 800 exceeds maximum 500"
# Caller expected this to be safe because validate_template() returned []

Note: validate_template() cannot fully prevent output-size errors (it doesn't have context values to measure), but it should at least accept and document the parameter so callers can align their validation and rendering configurations.

Expected Behavior

validate_template() should accept max_output_length as a parameter and document that it cannot validate actual output size but will use the value to configure the same TemplateConfig as the matching render_template_secure() call. This aligns the two APIs:

def validate_template(
    template: str,
    *,
    allowed_keys: frozenset[str] | None = None,
    max_template_length: int = DEFAULT_MAX_TEMPLATE_LENGTH,
    max_output_length: int = DEFAULT_MAX_OUTPUT_LENGTH,  # ← add this
) -> list[str]:
    config = TemplateConfig(
        allowed_keys=allowed_keys or frozenset(),
        max_template_length=max_template_length,
        max_output_length=max_output_length,  # ← pass through for config symmetry
    )
    renderer = SecureTemplateRenderer(config=config)
    return renderer.validate(template)

A docstring note should clarify that output-length validation cannot be performed without a concrete context.

Actual Behavior

validate_template() silently ignores max_output_length, always creating a TemplateConfig with the 50 000-char default regardless of what the caller will pass to render_template_secure(). Callers using a tighter output limit have no way to pre-validate against that limit.

Suggested Fix

Add max_output_length: int = DEFAULT_MAX_OUTPUT_LENGTH as a keyword argument to validate_template() and pass it through to TemplateConfig. Add a docstring note explaining the limitation (output size can only be checked at render time with real context values).

Category

consistency

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: [consistency] — `validate_template()` API omits `max_output_length`, creating asymmetry with `render_template_secure()` ### Severity Assessment - **Impact**: A caller who pre-validates a template with `validate_template(template, max_template_length=X)` may receive an empty error list (valid!) but later encounter a `TemplateSizeError` at render time if the rendered output exceeds `max_output_length`. There is no way to pass a custom `max_output_length` into `validate_template()`, so output-size constraints cannot be validated ahead of time. This false validation confidence can lead to runtime exceptions in production paths where callers expected pre-validation to be comprehensive. - **Likelihood**: Medium — any caller that pre-validates templates with a strict `max_output_length` in the render step is silently under-validated. - **Priority**: Medium ### Location - **File**: `src/cleveragents/templates/secure_renderer.py` - **Function**: `validate_template` - **Lines**: 288–312 ### Description `render_template_secure()` (lines 315–346) accepts both `max_template_length` and `max_output_length` as parameters and builds a `TemplateConfig` with both bounds: ```python def render_template_secure( template: str, context: dict[str, Any], *, allowed_keys: frozenset[str] | None = None, max_template_length: int = DEFAULT_MAX_TEMPLATE_LENGTH, max_output_length: int = DEFAULT_MAX_OUTPUT_LENGTH, # ← accepted here ) -> str: config = TemplateConfig( allowed_keys=allowed_keys or frozenset(), max_template_length=max_template_length, max_output_length=max_output_length, ) ``` `validate_template()` (lines 288–312), however, only accepts `max_template_length` and hard-codes `max_output_length` to the default via `TemplateConfig`: ```python def validate_template( template: str, *, allowed_keys: frozenset[str] | None = None, max_template_length: int = DEFAULT_MAX_TEMPLATE_LENGTH, # max_output_length is NOT a parameter — no way to pass it ) -> list[str]: config = TemplateConfig( allowed_keys=allowed_keys or frozenset(), max_template_length=max_template_length, # max_output_length always defaults to DEFAULT_MAX_OUTPUT_LENGTH (50 000) ) renderer = SecureTemplateRenderer(config=config) return renderer.validate(template) ``` Furthermore, `SecureTemplateRenderer.validate()` (lines 199–222) does not check output length at all — which is expected since output length requires a rendered result — but this means `validate_template()` cannot warn users that their template, given large context values, might blow up `max_output_length` at render time. The concrete inconsistency: callers who want to use a tighter `max_output_length` (e.g. `5_000` instead of the default `50_000`) with `render_template_secure()` cannot express that constraint to `validate_template()`. The validation step will always use the default 50 000-char output limit regardless. ### Evidence ```python # src/cleveragents/templates/secure_renderer.py, lines 288–312 def validate_template( template: str, *, allowed_keys: frozenset[str] | None = None, max_template_length: int = DEFAULT_MAX_TEMPLATE_LENGTH, # ← max_output_length missing ) -> list[str]: config = TemplateConfig( allowed_keys=allowed_keys or frozenset(), max_template_length=max_template_length, # ← max_output_length silently defaults to 50 000 ) renderer = SecureTemplateRenderer(config=config) return renderer.validate(template) ``` Scenario demonstrating false confidence: ```python # Step 1: validate with a tight output limit — but validate_template doesn't support it errors = validate_template(template, max_template_length=1000) assert errors == [] # passes — no errors reported # Step 2: render with a tight output limit result = render_template_secure( template, context, max_template_length=1000, max_output_length=500, # tighter than the default ) # → TemplateSizeError: "Rendered output length 800 exceeds maximum 500" # Caller expected this to be safe because validate_template() returned [] ``` Note: `validate_template()` cannot fully prevent output-size errors (it doesn't have context values to measure), but it should at least accept and document the parameter so callers can align their validation and rendering configurations. ### Expected Behavior `validate_template()` should accept `max_output_length` as a parameter and document that it cannot validate actual output size but will use the value to configure the same `TemplateConfig` as the matching `render_template_secure()` call. This aligns the two APIs: ```python def validate_template( template: str, *, allowed_keys: frozenset[str] | None = None, max_template_length: int = DEFAULT_MAX_TEMPLATE_LENGTH, max_output_length: int = DEFAULT_MAX_OUTPUT_LENGTH, # ← add this ) -> list[str]: config = TemplateConfig( allowed_keys=allowed_keys or frozenset(), max_template_length=max_template_length, max_output_length=max_output_length, # ← pass through for config symmetry ) renderer = SecureTemplateRenderer(config=config) return renderer.validate(template) ``` A docstring note should clarify that output-length validation cannot be performed without a concrete context. ### Actual Behavior `validate_template()` silently ignores `max_output_length`, always creating a `TemplateConfig` with the 50 000-char default regardless of what the caller will pass to `render_template_secure()`. Callers using a tighter output limit have no way to pre-validate against that limit. ### Suggested Fix Add `max_output_length: int = DEFAULT_MAX_OUTPUT_LENGTH` as a keyword argument to `validate_template()` and pass it through to `TemplateConfig`. Add a docstring note explaining the limitation (output size can only be checked at render time with real context values). ### Category consistency ### 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:46 +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#6574
No description provided.