Skip to content

Harden Jinja2 prompt rendering with ImmutableSandboxedEnvironment (CWE-1336 defense-in-depth)#2408

Open
JAE0Y2N wants to merge 3 commits into
The-PR-Agent:mainfrom
JAE0Y2N:harden/jinja2-sandboxed-environment
Open

Harden Jinja2 prompt rendering with ImmutableSandboxedEnvironment (CWE-1336 defense-in-depth)#2408
JAE0Y2N wants to merge 3 commits into
The-PR-Agent:mainfrom
JAE0Y2N:harden/jinja2-sandboxed-environment

Conversation

@JAE0Y2N
Copy link
Copy Markdown

@JAE0Y2N JAE0Y2N commented May 23, 2026

Summary

Switch all 13 Jinja2 Environment(undefined=StrictUndefined) instantiations to ImmutableSandboxedEnvironment(undefined=StrictUndefined). This is defense-in-depth against template-injection escalation (CWE-1336 / CWE-94) when prompt templates are sourced from configuration that may be influenced by untrusted input.

Background

apply_repo_settings (pr_agent/git_providers/utils.py:14-80) loads the target repo's .pr_agent.toml via Dynaconf with merge_enabled=True and no key-allowlist:

for section, contents in new_settings.as_dict().items():
    section_dict = copy.deepcopy(get_settings().as_dict().get(section, {}))
    for key, value in contents.items():
        section_dict[key] = value  # any key can be overridden
    get_settings().unset(section)
    get_settings().set(section, section_dict, merge=False)

The repo-side .pr_agent.toml therefore can override any settings value — including the prompt templates under [pr_code_suggestions_prompt], [pr_review_prompt], [pr_description_prompt], etc.

Those prompts are subsequently fed into Jinja2's Environment(undefined=StrictUndefined) and rendered:

# pr_agent/tools/pr_reviewer.py:216
environment = Environment(undefined=StrictUndefined)
system_prompt = environment.from_string(get_settings().pr_review_prompt.system).render(variables)
user_prompt = environment.from_string(get_settings().pr_review_prompt.user).render(variables)

The same pattern recurs across 13 sites in 11 files:

file line(s)
pr_agent/algo/token_handler.py 89
pr_agent/tools/pr_add_docs.py 86
pr_agent/tools/pr_code_suggestions.py 390, 923
pr_agent/tools/pr_description.py 429
pr_agent/tools/pr_generate_labels.py 137
pr_agent/tools/pr_help_docs.py 264
pr_agent/tools/pr_help_message.py 51
pr_agent/tools/pr_line_questions.py 155
pr_agent/tools/pr_questions.py 106
pr_agent/tools/pr_reviewer.py 216
pr_agent/tools/pr_update_changelog.py 108

The vanilla Environment permits Jinja2 sandbox-escape primitives — __class__.__mro__.__subclasses__(), cycler.__init__.__globals__.os, etc. — that can reach Python's stdlib and execute arbitrary code in pr-agent's runtime context.

ImmutableSandboxedEnvironment blocks unsafe attribute access (__init__, __globals__, __class__, __mro__, …) and mutation operations on shared collections, which is Jinja2's canonical defense for this class of issue. The same defense was recently adopted by sister AI-agent frameworks PrefectHQ/marvin and griptape-ai/griptape for the same threat class.

Verification

Local smoke test against the patched Environment constructor:

from jinja2 import StrictUndefined
from jinja2.sandbox import ImmutableSandboxedEnvironment

env = ImmutableSandboxedEnvironment(undefined=StrictUndefined)

# Safe templates render identically
env.from_string('{{ name }} files reviewed').render(name='42')
# → '42 files reviewed'

# Conditional + loop (used heavily in pr-agent's bundled prompts)
env.from_string('{% if items %}{% for x in items %}{{ x }} {% endfor %}{% endif %}').render(items=['a','b'])
# → 'a b '

# Untrusted template walking dunder attrs to reach `os`
env.from_string("{{ cycler.__init__.__globals__['os'].popen('id').read() }}").render()
# → SecurityError: access to attribute '__init__' of 'type' object is unsafe.

All shipped [*_prompt] template sections in pr_agent/settings/*.toml use only the safe feature subset (variable substitution, {% if %}, {% for %}, filters like tojson); none of them depend on __class__, __init__, __globals__, or any other surface that ImmutableSandboxedEnvironment restricts. The change is therefore non-breaking for current first-party prompts.

Patch shape

Per file, two lines change:

 from jinja2 import Environment, StrictUndefined
+from jinja2.sandbox import ImmutableSandboxedEnvironment

 ...

-        environment = Environment(undefined=StrictUndefined)
+        environment = ImmutableSandboxedEnvironment(undefined=StrictUndefined)

The Environment import is left in place because the existing from jinja2 import Environment, StrictUndefined line still serves the StrictUndefined import (and ImmutableSandboxedEnvironment is a subclass of Environment, so any code that downstream-type-checks against Environment continues to work).

Defense-in-depth framing

get_repo_settings() in pr_agent/git_providers/github_provider.py:733-738 deliberately reads .pr_agent.toml from the repository's default branch — not from the PR's head — which already prevents PR-authors from injecting malicious settings via a single PR. That existing defense is sound.

This change complements it by ensuring that even in the case where the repo's own default-branch configuration is somehow influenced by untrusted input (compromised maintainer account, supply-chain incident, accidental inclusion of user-data fields in prompt templates, etc.), the Jinja2 rendering step itself is sandboxed and cannot escalate to RCE in the pr-agent runtime. The runtime typically holds the GitHub App token, LLM API keys, and OS environment, so making this layer safe-by-construction reduces blast radius across the entire deployment population.

Related

  • Jinja2 sandbox docs: https://jinja.palletsprojects.com/en/stable/sandbox/
  • Sister landings: PrefectHQ/marvin#1348, griptape-ai/griptape#2183
  • CWE-1336: Improper Neutralization of Special Elements Used in a Template Engine
  • CWE-94: Improper Control of Generation of Code ("Code Injection")

…WE-1336 defense-in-depth)

Switch all 13 Environment(undefined=StrictUndefined) instantiations across
the 11 tools that render prompts loaded from settings to
ImmutableSandboxedEnvironment. Defense-in-depth against template-injection
escalation when prompt templates are sourced from .pr_agent.toml or other
configuration that may be influenced by untrusted input.

Shipped templates use only the safe feature subset (variable substitution,
conditionals, loops, filters), so the change is non-breaking for first-party
prompts. Sister AI-agent frameworks (marvin, griptape) recently adopted the
same defense for the same class of risk.
@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Harden Jinja2 prompt rendering with ImmutableSandboxedEnvironment

🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Replace 13 Jinja2 Environment instances with ImmutableSandboxedEnvironment
• Defense-in-depth against template-injection escalation (CWE-1336/CWE-94)
• Blocks unsafe attribute access and sandbox-escape primitives
• Non-breaking change; all shipped prompts use only safe features
Diagram
flowchart LR
  A["Jinja2 Environment<br/>undefined=StrictUndefined"] -->|"Replace with"| B["ImmutableSandboxedEnvironment<br/>undefined=StrictUndefined"]
  B -->|"Blocks"| C["Unsafe attributes<br/>__init__, __globals__, __class__"]
  B -->|"Prevents"| D["Sandbox-escape<br/>primitives"]
  D -->|"Mitigates"| E["CWE-1336<br/>Template Injection"]

Loading

File Changes

1. pr_agent/algo/token_handler.py Security hardening +2/-1

Replace Environment with ImmutableSandboxedEnvironment

pr_agent/algo/token_handler.py


2. pr_agent/tools/pr_add_docs.py Security hardening +2/-1

Replace Environment with ImmutableSandboxedEnvironment

pr_agent/tools/pr_add_docs.py


3. pr_agent/tools/pr_code_suggestions.py Security hardening +3/-2

Replace Environment with ImmutableSandboxedEnvironment (2 instances)

pr_agent/tools/pr_code_suggestions.py


View more (8)
4. pr_agent/tools/pr_description.py Security hardening +2/-1

Replace Environment with ImmutableSandboxedEnvironment

pr_agent/tools/pr_description.py


5. pr_agent/tools/pr_generate_labels.py Security hardening +2/-1

Replace Environment with ImmutableSandboxedEnvironment

pr_agent/tools/pr_generate_labels.py


6. pr_agent/tools/pr_help_docs.py Security hardening +2/-1

Replace Environment with ImmutableSandboxedEnvironment

pr_agent/tools/pr_help_docs.py


7. pr_agent/tools/pr_help_message.py Security hardening +2/-1

Replace Environment with ImmutableSandboxedEnvironment

pr_agent/tools/pr_help_message.py


8. pr_agent/tools/pr_line_questions.py Security hardening +2/-1

Replace Environment with ImmutableSandboxedEnvironment

pr_agent/tools/pr_line_questions.py


9. pr_agent/tools/pr_questions.py Security hardening +2/-1

Replace Environment with ImmutableSandboxedEnvironment

pr_agent/tools/pr_questions.py


10. pr_agent/tools/pr_reviewer.py Security hardening +2/-1

Replace Environment with ImmutableSandboxedEnvironment

pr_agent/tools/pr_reviewer.py


11. pr_agent/tools/pr_update_changelog.py Security hardening +2/-1

Replace Environment with ImmutableSandboxedEnvironment

pr_agent/tools/pr_update_changelog.py


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 23, 2026

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (2)

Grey Divider


Action required

1. pr_reviewer missing SecurityError handling 📘 Rule violation ☼ Reliability ⭐ New
Description
ImmutableSandboxedEnvironment rendering can raise jinja2.exceptions.SecurityError (and other
template errors) when repo-supplied prompts contain unsafe or invalid constructs.
_get_prediction() does not catch these exceptions, so the review flow can crash instead of failing
safely with a clear error.
Code

pr_agent/tools/pr_reviewer.py[R217-219]

Evidence
PR Compliance ID 3 requires anticipating and handling error scenarios. The updated code constructs
an ImmutableSandboxedEnvironment and immediately renders templates without any try/except, so
SecurityError/template errors will propagate and can terminate the review flow.

Rule 3: Robust Error Handling
pr_agent/tools/pr_reviewer.py[214-226]
pr_agent/tools/pr_questions.py[104-118]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Switching to `ImmutableSandboxedEnvironment` introduces explicit `SecurityError` failures for unsafe template access; rendering can also raise other `jinja2` template exceptions. The current code does not handle these errors, which can crash the tool instead of returning a safe/clear failure.

## Issue Context
Prompt templates are configurable and may be influenced by repository configuration. With the sandbox enabled, unsafe prompt payloads will now deterministically raise `SecurityError` during `.render()`.

## Fix Focus Areas
- pr_agent/tools/pr_reviewer.py[214-226]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Unused Environment imports ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The PR switches prompt rendering to ImmutableSandboxedEnvironment but leaves Environment
imported from jinja2 in several touched modules where it is no longer referenced. This creates
unused imports that violate the repo’s Ruff/isort conventions and will trigger Pyflakes/Ruff F401,
likely breaking CI.
Code

pr_agent/tools/pr_reviewer.py[R8-9]

Evidence
PR Compliance ID 9 requires adherence to Ruff/isort conventions, and the repo’s Ruff configuration
enables Pyflakes checks via lint.select including F. In the cited modules (e.g.,
pr_reviewer.py), Environment is still imported even though the code now instantiates only
ImmutableSandboxedEnvironment, making Environment an unused imported name; because Ruff flags
unused *names* even when they appear in a multi-import line with still-used names like
StrictUndefined, this pattern is F401-eligible and repeats across the other modified files
listed.

AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions
pr_agent/algo/token_handler.py[5-6]
pr_agent/tools/pr_add_docs.py[6-7]
pr_agent/tools/pr_code_suggestions.py[11-12]
pr_agent/tools/pr_description.py[9-10]
pr_agent/tools/pr_generate_labels.py[6-7]
pr_agent/tools/pr_help_docs.py[4-5]
pr_agent/tools/pr_help_message.py[6-7]
pr_agent/tools/pr_line_questions.py[5-6]
pr_agent/tools/pr_questions.py[4-6]
pr_agent/tools/pr_reviewer.py[8-9]
pr_agent/tools/pr_update_changelog.py[7-8]
pyproject.toml[52-61]
pr_agent/tools/pr_reviewer.py[8-10]
pr_agent/tools/pr_reviewer.py[214-219]
pr_agent/algo/token_handler.py[5-7]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
After replacing `Environment(...)` usages with `ImmutableSandboxedEnvironment(...)`, multiple modules still import `Environment` from `jinja2` but never reference it. This will trip Ruff/Pyflakes unused-import checks (`F401`) and violate the repo’s lint/import conventions.
## Issue Context
Ruff is enabled with `lint.select` including `F` (Pyflakes), which reports unused imported names. Some files still use `StrictUndefined`, but Ruff flags unused *names* within a multi-import line, so `Environment` must be removed while keeping `StrictUndefined` and the sandbox environment import.
Example target form:

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. pr_help_docs import order ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
pr_agent/tools/pr_help_docs.py has standard-library imports (math, os, re, etc.) after
third-party imports, which is inconsistent with isort/Ruff import grouping expectations. This can
cause pre-commit/isort failures in a touched file.
Code

pr_agent/tools/pr_help_docs.py[R4-8]

Evidence
PR Compliance ID 9 requires Python changes to follow Ruff/isort conventions. The cited import block
shows third-party jinja2 imports placed before standard-library modules like math/os/re,
which is an isort ordering violation.

AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions
pr_agent/tools/pr_help_docs.py[1-10]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
In `pr_agent/tools/pr_help_docs.py`, standard library imports appear after third-party imports, which conflicts with typical isort/Ruff import grouping.
## Issue Context
This file was modified in this PR, so it should remain pre-commit clean.
## Fix Focus Areas
- pr_agent/tools/pr_help_docs.py[1-10]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Token undercount on render 🐞 Bug ≡ Correctness ⭐ New
Description
TokenHandler._get_system_user_tokens() returns 0 when Jinja rendering raises (including
SecurityError from the new sandbox), which can undercount prompt_tokens and cause diff
packing/clipping logic to exceed the intended model token budget. This can lead to oversized prompts
and downstream LLM request failures or unexpected truncation behavior.
Code

pr_agent/algo/token_handler.py[90]

Evidence
The token counting path returns 0 on any render exception, and prompt_tokens is used as the
baseline for diff token budgeting/clipping. Under-counting here directly affects whether patches are
clipped/skipped and whether the final diff stays under model limits.

pr_agent/algo/token_handler.py[89-98]
pr_agent/algo/pr_processing.py[172-173]
pr_agent/algo/pr_processing.py[456-466]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`TokenHandler._get_system_user_tokens()` catches all exceptions from Jinja rendering and returns `0`, which makes `token_handler.prompt_tokens` artificially low. Since `prompt_tokens` is used as the base budget when assembling/clipping diffs, returning 0 can cause the final prompt to exceed the model’s token limits.

## Issue Context
With this PR, sandboxing increases the chance of raising `jinja2.exceptions.SecurityError` for unsafe templates; these will currently be treated the same as any error and result in a 0-token estimate.

## Fix Focus Areas
- pr_agent/algo/token_handler.py[89-98]

## Suggested fix
In the `except` path, do not return `0`. Instead, fall back to a conservative estimate that does not require rendering, e.g.:
- `len(encoder.encode(system)) + len(encoder.encode(user))` (count the raw template strings), or
- count rendered prompts only if rendering succeeds, otherwise count the raw templates plus a small buffer.
Also consider logging with `get_logger().exception(...)` to retain stack traces for misconfigured prompts.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. No tests for sandboxing 📘 Rule violation ☼ Reliability
Description
Template rendering behavior changes by switching to ImmutableSandboxedEnvironment, but the PR diff
does not add/update pytest coverage for this behavior change. This risks regressions or unexpected
rendering/security behavior going untested.
Code

pr_agent/tools/pr_reviewer.py[217]

Evidence
PR Compliance ID 13 requires updating/adding tests when behavior changes. The cited code shows the
new sandboxed environment in a core flow, while the existing PRReviewer unit tests do not cover
prompt rendering behavior.

AGENTS.md: Add/Place Tests Appropriately for New or Changed Behavior: AGENTS.md: Add/Place Tests Appropriately for New or Changed Behavior: AGENTS.md: Add/Place Tests Appropriately for New or Changed Behavior: AGENTS.md: Add/Place Tests Appropriately for New or Changed Behavior: AGENTS.md: Add/Place Tests Appropriately for New or Changed Behavior: AGENTS.md: Add/Place Tests Appropriately for New or Changed Behavior: AGENTS.md: Add/Place Tests Appropriately for New or Changed Behavior: AGENTS.md: Add/Place Tests Appropriately for New or Changed Behavior
pr_agent/tools/pr_reviewer.py[213-219]
tests/unittest/test_pr_reviewer_core.py[1-120]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR changes the Jinja2 rendering engine from `Environment` to `ImmutableSandboxedEnvironment`, which can alter rendering outcomes and security behavior (e.g., blocked attributes). There should be focused pytest coverage that asserts safe templates still render and unsafe attribute access raises `SecurityError`.
## Issue Context
Existing unit tests for `PRReviewer` appear to focus on other logic and do not exercise prompt rendering/sandbox restrictions.
## Fix Focus Areas
- pr_agent/tools/pr_reviewer.py[213-219]
- tests/unittest/test_pr_reviewer_core.py[1-120]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Previous review results

Review updated until commit 1e98f05

Results up to commit N/A


🐞 Bugs (0) 📘 Rule violations (1) 📎 Requirement gaps (0)


Action required
1. Unused Environment imports ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The PR switches prompt rendering to ImmutableSandboxedEnvironment but leaves Environment
imported from jinja2 in several touched modules where it is no longer referenced. This creates
unused imports that violate the repo’s Ruff/isort conventions and will trigger Pyflakes/Ruff F401,
likely breaking CI.
Code

pr_agent/tools/pr_reviewer.py[R8-9]

Evidence
PR Compliance ID 9 requires adherence to Ruff/isort conventions, and the repo’s Ruff configuration
enables Pyflakes checks via lint.select including F. In the cited modules (e.g.,
pr_reviewer.py), Environment is still imported even though the code now instantiates only
ImmutableSandboxedEnvironment, making Environment an unused imported name; because Ruff flags
unused *names* even when they appear in a multi-import line with still-used names like
StrictUndefined, this pattern is F401-eligible and repeats across the other modified files
listed.

AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions
pr_agent/algo/token_handler.py[5-6]
pr_agent/tools/pr_add_docs.py[6-7]
pr_agent/tools/pr_code_suggestions.py[11-12]
pr_agent/tools/pr_description.py[9-10]
pr_agent/tools/pr_generate_labels.py[6-7]
pr_agent/tools/pr_help_docs.py[4-5]
pr_agent/tools/pr_help_message.py[6-7]
pr_agent/tools/pr_line_questions.py[5-6]
pr_agent/tools/pr_questions.py[4-6]
pr_agent/tools/pr_reviewer.py[8-9]
pr_agent/tools/pr_update_changelog.py[7-8]
pyproject.toml[52-61]
pr_agent/tools/pr_reviewer.py[8-10]
pr_agent/tools/pr_reviewer.py[214-219]
pr_agent/algo/token_handler.py[5-7]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
After replacing `Environment(...)` usages with `ImmutableSandboxedEnvironment(...)`, multiple modules still import `Environment` from `jinja2` but never reference it. This will trip Ruff/Pyflakes unused-import checks (`F401`) and violate the repo’s lint/import conventions.
## Issue Context
Ruff is enabled with `lint.select` including `F` (Pyflakes), which reports unused imported names. Some files still use `StrictUndefined`, but Ruff flags unused *names* within a multi-import line, so `Environment` must be removed while keeping `StrictUndefined` and the sandbox environment import.
Example target form:

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. pr_help_docs import order ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
pr_agent/tools/pr_help_docs.py has standard-library imports (math, os, re, etc.) after
third-party imports, which is inconsistent with isort/Ruff import grouping expectations. This can
cause pre-commit/isort failures in a touched file.
Code

pr_agent/tools/pr_help_docs.py[R4-8]

Evidence
PR Compliance ID 9 requires Python changes to follow Ruff/isort conventions. The cited import block
shows third-party jinja2 imports placed before standard-library modules like math/os/re,
which is an isort ordering violation.

AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions: AGENTS.md: Python Code Must Follow Repository Ruff/Import/String Style Conventions
pr_agent/tools/pr_help_docs.py[1-10]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
In `pr_agent/tools/pr_help_docs.py`, standard library imports appear after third-party imports, which conflicts with typical isort/Ruff import grouping.
## Issue Context
This file was modified in this PR, so it should remain pre-commit clean.
## Fix Focus Areas
- pr_agent/tools/pr_help_docs.py[1-10]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
3. No tests for sandboxing 📘 Rule violation ☼ Reliability
Description
Template rendering behavior changes by switching to ImmutableSandboxedEnvironment, but the PR diff
does not add/update pytest coverage for this behavior change. This risks regressions or unexpected
rendering/security behavior going untested.
Code

pr_agent/tools/pr_reviewer.py[217]

Evidence
PR Compliance ID 13 requires updating/adding tests when behavior changes. The cited code shows the
new sandboxed environment in a core flow, while the existing PRReviewer unit tests do not cover
prompt rendering behavior.

AGENTS.md: Add/Place Tests Appropriately for New or Changed Behavior: AGENTS.md: Add/Place Tests Appropriately for New or Changed Behavior: AGENTS.md: Add/Place Tests Appropriately for New or Changed Behavior: AGENTS.md: Add/Place Tests Appropriately for New or Changed Behavior
pr_agent/tools/pr_reviewer.py[213-219]
tests/unittest/test_pr_reviewer_core.py[1-120]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR changes the Jinja2 rendering engine from `Environment` to `ImmutableSandboxedEnvironment`, which can alter rendering outcomes and security behavior (e.g., blocked attributes). There should be focused pytest coverage that asserts safe templates still render and unsafe attribute access raises `SecurityError`.
## Issue Context
Existing unit tests for `PRReviewer` appear to focus on other logic and do not exercise prompt rendering/sandbox restrictions.
## Fix Focus Areas
- pr_agent/tools/pr_reviewer.py[213-219]
- tests/unittest/test_pr_reviewer_core.py[1-120]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

Comment thread pr_agent/tools/pr_reviewer.py Outdated
Comment thread pr_agent/tools/pr_help_docs.py Outdated
…_docs imports, add sandbox unit tests

(1) F401 unused-import fix: ruff -- the 11 files that touch jinja2 had
    'from jinja2 import Environment, StrictUndefined' but after the
    sandbox migration Environment is no longer referenced. Surgical
    removal of just Environment, preserving StrictUndefined.

(2) pr_help_docs.py import order: stdlib (math/os/re/tempfile)
    re-ordered above third-party (jinja2) per PEP 8 / isort grouping.

(3) Regression coverage: tests/unittest/test_jinja_sandbox.py exercises
    both arms of the sandbox swap — safe templates (variable
    substitution, conditionals, for-loops, dict-attr, filters) render
    identically, and unsafe templates (__class__, __subclasses__,
    cycler.__init__.__globals__.os.popen) raise SecurityError.

Addresses all three rule violations flagged in the Qodo code review on
PR The-PR-Agent#2408.
@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 23, 2026

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

…party

Addresses Qodo review item 2: stdlib imports (math, os, re, tempfile)
were placed after third-party jinja2 imports. Reordered to put stdlib
first per PEP 8 / isort grouping conventions.
@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 23, 2026

Persistent review updated to latest commit 1e98f05

Comment on lines +217 to 219
environment = ImmutableSandboxedEnvironment(undefined=StrictUndefined)
system_prompt = environment.from_string(get_settings().pr_review_prompt.system).render(variables)
user_prompt = environment.from_string(get_settings().pr_review_prompt.user).render(variables)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. pr_reviewer missing securityerror handling 📘 Rule violation ☼ Reliability

ImmutableSandboxedEnvironment rendering can raise jinja2.exceptions.SecurityError (and other
template errors) when repo-supplied prompts contain unsafe or invalid constructs.
_get_prediction() does not catch these exceptions, so the review flow can crash instead of failing
safely with a clear error.
Agent Prompt
## Issue description
Switching to `ImmutableSandboxedEnvironment` introduces explicit `SecurityError` failures for unsafe template access; rendering can also raise other `jinja2` template exceptions. The current code does not handle these errors, which can crash the tool instead of returning a safe/clear failure.

## Issue Context
Prompt templates are configurable and may be influenced by repository configuration. With the sandbox enabled, unsafe prompt payloads will now deterministically raise `SecurityError` during `.render()`.

## Fix Focus Areas
- pr_agent/tools/pr_reviewer.py[214-226]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant