Skip to content

[AAP-76179] fix(security): enforce sandboxed Jinja2 rendering in EDA server - #1577

Merged
B-Whitt merged 2 commits into
ansible:mainfrom
B-Whitt:fix-cwe/AAP-76179-Jinja2-rendering
Jun 3, 2026
Merged

[AAP-76179] fix(security): enforce sandboxed Jinja2 rendering in EDA server#1577
B-Whitt merged 2 commits into
ansible:mainfrom
B-Whitt:fix-cwe/AAP-76179-Jinja2-rendering

Conversation

@B-Whitt

@B-Whitt B-Whitt commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

What

Replace unsandboxed jinja2.nativetypes.NativeTemplate with
jinja2.sandbox.ImmutableSandboxedEnvironment in all three rendering
call sites to prevent server-side template injection (SSTI):

  • strings.py: sandbox _render_string(); remove Django settings object
    (SECRET_KEY, DATABASES, REDIS_URL) from substitute_extra_vars() context
  • credentials.py: sandbox _check_jinja_string() in credential validation
  • ports.py: sandbox render_string() in port resolution

Why

The EDA server uses unsandboxed NativeTemplate which allows arbitrary
Python object traversal via template expressions. The substitute_extra_vars()
function also passes the entire Django settings object into the template
context, exposing secrets to any user who can craft activation extra_vars.

This matches the approach AWX already uses
(jinja2.sandbox.ImmutableSandboxedEnvironment at credential/__init__.py:519).

Test plan

  • 9 regression tests verifying SSTI payloads are blocked across all three call sites
  • Existing port tests pass (10/10)
  • Existing credential validation tests pass (schema/injector tests; DB-dependent tests require PostgreSQL)
  • 96 additional unit tests pass with no regressions

Resolves: AAP-76179
Assisted-by: Claude Code / Opus 4.6 (Anthropic)

Summary by CodeRabbit

  • Security
    • Stronger template sandboxing to block template injection and remove application settings from template rendering context.
  • Tests
    • New regression and coverage tests confirming malicious templates are blocked while legitimate templates render.
  • Bug Fixes
    • Adjusted WebSocket payload formatting for a file-template scenario to match updated rendering behavior.

@B-Whitt
B-Whitt requested a review from a team as a code owner June 1, 2026 21:03
@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR replaces per-call Jinja NativeTemplate rendering with a shared ImmutableSandboxedEnvironment (StrictUndefined) across string utilities, credentials validation, and activation ports; it maps sandbox SecurityError/UndefinedError to appropriate exceptions, removes substitute_extra_vars, adds SSTI regression unit tests, and updates one integration expected payload.

Changes

SSTI Prevention Implementation and Testing

Layer / File(s) Summary
Strings util: sandbox and removal of substitute_extra_vars
src/aap_eda/core/utils/strings.py
Adds module-level _SANDBOXED_ENV (ImmutableSandboxedEnvironment with StrictUndefined), updates _render_string to render via the sandbox and convert Jinja2 SecurityError into ValueError, and removes substitute_extra_vars.
Credentials util: sandboxed _check_jinja_string
src/aap_eda/core/utils/credentials.py
Adds module-level _SANDBOXED_ENV, updates _check_jinja_string to render via the sandbox, treats StrictUndefined/UndefinedError as InjectorMissingKeyException, and maps SecurityError to InjectorInvalidTemplateKey.
Activation ports: sandboxed render_string and error wrapping
src/aap_eda/services/activation/engine/ports.py
Introduces module-level _SANDBOXED_ENV, updates render_string to compile/render with the sandbox, and converts Jinja2 SecurityError in find_ports into ActivationStartError.
Unit tests: SSTI blocking and legitimate templates
tests/unit/test_ssti_prevention.py
Adds SSTI regression tests asserting payload rejection for _render_string, _check_jinja_string, and ports-layer render_string, plus tests for legitimate template rendering and variable substitution.
Integration test expectation update
tests/integration/wsapi/test_consumer.py
Adjusts an expected base64-encoded payload for test_handle_workers_with_file_contents to match a whitespace/formatting change in the rendered literal.

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: replacing unsandboxed Jinja2 template rendering with sandboxed versions to prevent SSTI vulnerabilities across the EDA server.
Description check ✅ Passed The description is comprehensive and well-structured, covering what is being changed, why it's needed, how it addresses the issue, test coverage, and the related issue number. All critical sections from the template are addressed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/aap_eda/core/utils/credentials.py (1)

517-524: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Normalize Jinja sandbox SecurityError into injector validation errors

ImmutableSandboxedEnvironment raises jinja2.exceptions.SecurityError for blocked attribute access, but _check_jinja_string() only remaps UndefinedError, and validate_injectors() doesn’t catch SecurityError, so it can escape instead of being collected in the "injectors" error list.

Suggested fix
 def _check_jinja_string(value: str, context: dict) -> str:
     try:
         if "{{" in value and "}}" in value:
             result = _SANDBOXED_ENV.from_string(value).render(context)
             if isinstance(result, jinja2.runtime.StrictUndefined):
                 raise InjectorMissingKeyException(f"{value} is undefined")
     except jinja2.exceptions.UndefinedError:
         raise InjectorMissingKeyException(f"{value} is undefined")
+    except jinja2.exceptions.SecurityError as e:
+        raise InjectorInvalidTemplateKey(str(e))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/aap_eda/core/utils/credentials.py` around lines 517 - 524, The
_check_jinja_string function currently only remaps
jinja2.exceptions.UndefinedError to InjectorMissingKeyException, so add handling
for jinja2.exceptions.SecurityError (raised by ImmutableSandboxedEnvironment on
blocked attribute access) by catching SecurityError and raising
InjectorMissingKeyException with the same message as for UndefinedError; update
the exception handling in _check_jinja_string (and ensure validate_injectors
continues to treat InjectorMissingKeyException as a validation error) so
SecurityError is normalized into the existing "injectors" error collection
instead of escaping.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/aap_eda/core/utils/strings.py`:
- Around line 27-32: _render_string currently renders templates via
_SANDBOXED_ENV.from_string(...).render(), which always returns strings and can
break typed extra_vars; change _render_string to use a Jinja environment that
preserves native result types (or explicitly coerce rendered outputs back to
native types) so templated bool/int/list/dict values remain native for
downstream yaml.safe_load; update or add regression tests for
substitute_variables/substitute_extra_vars to cover scalar and collection typed
outputs (booleans, ints, lists, dicts) to ensure round-tripping.

In `@src/aap_eda/services/activation/engine/ports.py`:
- Around line 17-20: The find_ports() error handling currently only maps
ValueError and jinja2.exceptions.UndefinedError to ActivationStartError, letting
jinja2.sandbox.SecurityError escape; update the exception handler in
find_ports() to also catch jinja2.sandbox.SecurityError (import it from
jinja2.sandbox) and re-raise it as exceptions.ActivationStartError with an
appropriate message so sandbox violations from render_string() (which uses
_SANDBOXED_ENV.from_string) are normalized to ActivationStartError.

---

Outside diff comments:
In `@src/aap_eda/core/utils/credentials.py`:
- Around line 517-524: The _check_jinja_string function currently only remaps
jinja2.exceptions.UndefinedError to InjectorMissingKeyException, so add handling
for jinja2.exceptions.SecurityError (raised by ImmutableSandboxedEnvironment on
blocked attribute access) by catching SecurityError and raising
InjectorMissingKeyException with the same message as for UndefinedError; update
the exception handling in _check_jinja_string (and ensure validate_injectors
continues to treat InjectorMissingKeyException as a validation error) so
SecurityError is normalized into the existing "injectors" error collection
instead of escaping.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: e61dbb57-8a2d-4979-8d59-c0194c77db52

📥 Commits

Reviewing files that changed from the base of the PR and between 72944af and 658677d.

📒 Files selected for processing (4)
  • src/aap_eda/core/utils/credentials.py
  • src/aap_eda/core/utils/strings.py
  • src/aap_eda/services/activation/engine/ports.py
  • tests/unit/test_ssti_prevention.py

Comment thread src/aap_eda/core/utils/strings.py Outdated
Comment thread src/aap_eda/services/activation/engine/ports.py
@B-Whitt
B-Whitt force-pushed the fix-cwe/AAP-76179-Jinja2-rendering branch from 0332a3e to ecfbf54 Compare June 1, 2026 21:32
@B-Whitt

B-Whitt commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

/run-e2e

Comment thread src/aap_eda/core/utils/credentials.py
@B-Whitt
B-Whitt force-pushed the fix-cwe/AAP-76179-Jinja2-rendering branch from ecfbf54 to 5a42e36 Compare June 1, 2026 22:14

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/aap_eda/services/activation/engine/ports.py (1)

73-76: 💤 Low value

Chain exception for consistency.

Same as credentials.py - consider chaining the SecurityError for better debugging. The UndefinedError on line 74 has the same pattern but wasn't flagged by static analysis (both could benefit from chaining).

♻️ Proposed fix
             except UndefinedError as e:
-                raise exceptions.ActivationStartError(str(e))
+                raise exceptions.ActivationStartError(str(e)) from e
             except SecurityError as e:
-                raise exceptions.ActivationStartError(str(e))
+                raise exceptions.ActivationStartError(str(e)) from e
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/aap_eda/services/activation/engine/ports.py` around lines 73 - 76, The
except blocks that catch UndefinedError and SecurityError should chain the
original exception into the ActivationStartError to preserve traceback and
context; update both handlers that currently do "raise
exceptions.ActivationStartError(str(e))" to raise
exceptions.ActivationStartError(...) from e so the original
UndefinedError/SecurityError is preserved (look for the except UndefinedError
and except SecurityError handling ActivationStartError in this module).
src/aap_eda/core/utils/credentials.py (1)

523-526: 💤 Low value

Chain exceptions for better debugging.

Static analysis correctly flags that exceptions raised within except blocks should chain to the original exception for clearer tracebacks.

♻️ Proposed fix to chain exceptions
     except jinja2.exceptions.UndefinedError:
-        raise InjectorMissingKeyException(f"{value} is undefined")
+        raise InjectorMissingKeyException(f"{value} is undefined") from None
     except jinja2.exceptions.SecurityError as e:
-        raise InjectorInvalidTemplateKey(str(e))
+        raise InjectorInvalidTemplateKey(str(e)) from e
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/aap_eda/core/utils/credentials.py` around lines 523 - 526, The except
handlers should chain the original jinja2 exceptions to preserve tracebacks:
change the first handler to catch jinja2.exceptions.UndefinedError as e and
raise InjectorMissingKeyException(f"{value} is undefined") from e, and change
the second handler to raise InjectorInvalidTemplateKey(str(e)) from e (it
already captures e). Update the except clauses in credentials.py around the
jinja2 handlers to use "as e" where missing and append "from e" to the raised
custom exceptions so the original exception is chained.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/aap_eda/core/utils/credentials.py`:
- Around line 523-526: The except handlers should chain the original jinja2
exceptions to preserve tracebacks: change the first handler to catch
jinja2.exceptions.UndefinedError as e and raise
InjectorMissingKeyException(f"{value} is undefined") from e, and change the
second handler to raise InjectorInvalidTemplateKey(str(e)) from e (it already
captures e). Update the except clauses in credentials.py around the jinja2
handlers to use "as e" where missing and append "from e" to the raised custom
exceptions so the original exception is chained.

In `@src/aap_eda/services/activation/engine/ports.py`:
- Around line 73-76: The except blocks that catch UndefinedError and
SecurityError should chain the original exception into the ActivationStartError
to preserve traceback and context; update both handlers that currently do "raise
exceptions.ActivationStartError(str(e))" to raise
exceptions.ActivationStartError(...) from e so the original
UndefinedError/SecurityError is preserved (look for the except UndefinedError
and except SecurityError handling ActivationStartError in this module).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 31c639ff-525e-4e9a-850b-ba99285c453a

📥 Commits

Reviewing files that changed from the base of the PR and between 4721ceb and 5a42e36.

📒 Files selected for processing (5)
  • src/aap_eda/core/utils/credentials.py
  • src/aap_eda/core/utils/strings.py
  • src/aap_eda/services/activation/engine/ports.py
  • tests/integration/wsapi/test_consumer.py
  • tests/unit/test_ssti_prevention.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/unit/test_ssti_prevention.py
  • src/aap_eda/core/utils/strings.py

@codecov-commenter

codecov-commenter commented Jun 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.37%. Comparing base (72944af) to head (720fb51).

@@            Coverage Diff             @@
##             main    #1577      +/-   ##
==========================================
+ Coverage   92.32%   92.37%   +0.05%     
==========================================
  Files         244      244              
  Lines       11214    11215       +1     
==========================================
+ Hits        10353    10360       +7     
+ Misses        861      855       -6     
Flag Coverage Δ
unit-int-tests-3.11 92.37% <100.00%> (+0.05%) ⬆️
unit-int-tests-3.12 92.37% <100.00%> (+0.05%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/aap_eda/core/utils/credentials.py 94.51% <100.00%> (+0.02%) ⬆️
src/aap_eda/core/utils/strings.py 76.36% <100.00%> (+9.69%) ⬆️
src/aap_eda/services/activation/engine/ports.py 97.61% <100.00%> (+0.05%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@B-Whitt

B-Whitt commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

/run-e2e

@B-Whitt
B-Whitt requested a review from mkanoor June 2, 2026 13:15
Comment thread src/aap_eda/core/utils/strings.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/unit/test_ssti_prevention.py (1)

63-79: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add a regression that proves settings is no longer in template context.

This file covers sandboxing, but it no longer verifies the other security fix called out in the PR: removing Django settings from the render context. Without an assertion that something like {{ settings.SECRET_KEY }} is unavailable, a future change could reintroduce secret exposure while all SSTI tests still pass.

Suggested test shape
 class TestSandboxEnforcement:
     """Each rendering call site rejects SSTI payloads."""
+    def test_settings_are_not_available_in_template_context(self):
+        with pytest.raises(Exception):
+            substitute_variables(
+                {"a": "{{ settings.SECRET_KEY }}"},
+                {},
+            )
As per coding guidelines, "Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_ssti_prevention.py` around lines 63 - 79, Add a regression
test to TestLegitimateTemplates that renders "{{ settings.SECRET_KEY }}" via
_render_string (and/or ports_render_string/substitute_variables variants) with
an empty context and asserts the output does NOT expose
django.conf.settings.SECRET_KEY; import django.conf.settings in the test and
assert the rendered value is either empty/Undefined or at minimum not equal to
settings.SECRET_KEY to ensure the Django settings object is not present in the
template context.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tests/unit/test_ssti_prevention.py`:
- Around line 63-79: Add a regression test to TestLegitimateTemplates that
renders "{{ settings.SECRET_KEY }}" via _render_string (and/or
ports_render_string/substitute_variables variants) with an empty context and
asserts the output does NOT expose django.conf.settings.SECRET_KEY; import
django.conf.settings in the test and assert the rendered value is either
empty/Undefined or at minimum not equal to settings.SECRET_KEY to ensure the
Django settings object is not present in the template context.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 3222812e-1082-49fc-bb7b-1046c71d5c24

📥 Commits

Reviewing files that changed from the base of the PR and between 5a42e36 and fded3a2.

📒 Files selected for processing (2)
  • src/aap_eda/core/utils/strings.py
  • tests/unit/test_ssti_prevention.py
💤 Files with no reviewable changes (1)
  • src/aap_eda/core/utils/strings.py

@B-Whitt
B-Whitt requested a review from wfealdel June 2, 2026 13:50
jcraiglo1
jcraiglo1 previously approved these changes Jun 2, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/aap_eda/core/utils/strings.py (1)

25-37: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Normalize StrictUndefined failures in _render_string
_SANDBOXED_ENV uses jinja2.StrictUndefined, but _render_string only catches jinja2.exceptions.SecurityError; missing variables raise jinja2.exceptions.UndefinedError which bubbles up through substitute_variables() into both src/aap_eda/wsapi/consumers.py and src/aap_eda/api/serializers/activation.py without being handled.

Suggested fix
-from jinja2.exceptions import SecurityError
+from jinja2.exceptions import SecurityError, UndefinedError
@@
-        except SecurityError:
-            raise ValueError(
-                f"Template contains unsafe operations: {value}"
-            )
+        except (SecurityError, UndefinedError) as err:
+            raise ValueError(
+                f"Invalid template: {value}"
+            ) from err
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/aap_eda/core/utils/strings.py` around lines 25 - 37, The function
_render_string should normalize StrictUndefined failures so UndefinedError
doesn't bubble up; update _render_string to catch
jinja2.exceptions.UndefinedError in addition to SecurityError (or catch
Exception subclasses from jinja2 that represent undefined/strict failures) and
re-raise a ValueError with a clear message (same pattern used for SecurityError)
so substitute_variables(), consumers and serializers receive a consistent
ValueError; reference _SANDBOXED_ENV and _render_string when making this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/aap_eda/core/utils/strings.py`:
- Around line 25-37: The function _render_string should normalize
StrictUndefined failures so UndefinedError doesn't bubble up; update
_render_string to catch jinja2.exceptions.UndefinedError in addition to
SecurityError (or catch Exception subclasses from jinja2 that represent
undefined/strict failures) and re-raise a ValueError with a clear message (same
pattern used for SecurityError) so substitute_variables(), consumers and
serializers receive a consistent ValueError; reference _SANDBOXED_ENV and
_render_string when making this change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 2f9f16ed-acf6-47b8-bfa8-ea2f7eef8a6f

📥 Commits

Reviewing files that changed from the base of the PR and between fded3a2 and 18ed9ec.

📒 Files selected for processing (2)
  • src/aap_eda/core/utils/strings.py
  • tests/unit/test_ssti_prevention.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/unit/test_ssti_prevention.py

@B-Whitt
B-Whitt force-pushed the fix-cwe/AAP-76179-Jinja2-rendering branch from 18ed9ec to 2f53453 Compare June 2, 2026 15:00
@B-Whitt
B-Whitt requested a review from jcraiglo1 June 2, 2026 15:22
Comment thread src/aap_eda/core/utils/strings.py Outdated
AlexSCorey
AlexSCorey previously approved these changes Jun 2, 2026

@AlexSCorey AlexSCorey left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good to me. It addresses the most pressing vulnerabilities.

Replace unsandboxed jinja2.nativetypes.NativeTemplate with
jinja2.sandbox.ImmutableSandboxedEnvironment in all three rendering
call sites to prevent server-side template injection (SSTI):

- strings.py: sandbox _render_string(); remove Django settings object
  (SECRET_KEY, DATABASES, REDIS_URL) from substitute_extra_vars() context
- credentials.py: sandbox _check_jinja_string() in credential validation
- ports.py: sandbox render_string() in port resolution

Add 9 regression tests covering sandbox enforcement across all three
call sites, legitimate template rendering, and settings removal.

Resolves: AAP-76179
Assisted-by: Claude Code / Opus 4.6 (Anthropic)
@B-Whitt
B-Whitt force-pushed the fix-cwe/AAP-76179-Jinja2-rendering branch from 2f53453 to 2b30e2f Compare June 3, 2026 19:09
@B-Whitt
B-Whitt requested review from AlexSCorey and wfealdel June 3, 2026 19:13
@sonarqubecloud

sonarqubecloud Bot commented Jun 3, 2026

Copy link
Copy Markdown

@wfealdel

wfealdel commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

/run-e2e

@B-Whitt
B-Whitt merged commit c83fd05 into ansible:main Jun 3, 2026
7 checks passed
@B-Whitt
B-Whitt deleted the fix-cwe/AAP-76179-Jinja2-rendering branch July 28, 2026 19:51
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.

6 participants