Skip to content

Harden the TwigOperator sandbox - #2064

Merged
fashxp merged 8 commits into
2026.xfrom
feat/output-channels
Oct 9, 2026
Merged

fashxp merged 8 commits into
2026.xfrom
feat/output-channels

Conversation

@fashxp

@fashxp fashxp commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

Hardens the twigOperator grid transformer on top of #2054.

  • Templates render in a dedicated Twig environment with the sandbox always enabled and without Pimcore's Twig extensions or the application's Twig configuration.
  • No method or property access on objects. Core's sandbox class and method lists are accepted for BC but not applied; no pimcore_* function can be called (registered ones are blocked, allow-listed ones ignored).
  • Values are converted to plain data before rendering: dates to ISO 8601 strings, consent values, JsonSerializable objects and enums to their data, other objects to null.
  • range() returns at most 1000 elements (best effort; .. and nested loops are not limited).
  • New extension point: services tagged pimcore_studio_backend.twig_operator_extension.
  • BC: constructor signatures and interfaces unchanged; new TwigOperatorEnvironmentProviderInterface with a deprecated, fail-closed fallback. Behaviour changes for templates are listed in the 2026.4.0 upgrade note.

The advanced-column fix that was part of this PR shipped separately in #2071.

🤖 Generated with Claude Code

Copilot AI balanced review requested due to automatic review settings September 30, 2026 19:44

Copilot AI left a comment

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.

Copilot review overview

🟡 Changes recommended

The range cap remains bypassable, and previously public Twig contracts break without deprecation paths.

Review effort: Balanced
Findings: 3 Medium severity

Open (3)
What changed in this PR

Hardens TwigOperator rendering and corrects localized advanced-column exports.

Changes:

  • Adds an isolated, deny-all Twig sandbox with value sanitization and extension support.
  • Prevents unwanted default-language fallback and exports top-level null as ''.
  • Adds regression tests and upgrade documentation.

Review assessment:

  • The fixes target the correct transformer, resolver, and Twig initialization boundaries.
  • Regression tests and documentation are comprehensive.
  • Blocking issues remain: mixed range bounds bypass the cap (SandboxExtensionInitializer.php:278), and previously public Twig contracts introduce breaking changes without deprecation paths.
File Description
tests/​Unit/​Twig/​TemplateGeneratorTest.php Tests sandbox isolation and restrictions.
tests/​Unit/​Grid/​Util/​Trait/​LocalizedValueTraitTest.php Tests localization fallback behavior.
tests/​Unit/​Grid/​Util/​AdvancedColumnSourceFieldContextTest.php Tests source-field context state.
tests/​Unit/​Grid/​Column/​Transformer/​TwigOperatorTest.php Tests recursive value sanitization.
tests/​Unit/​Grid/​Column/​Resolver/​DataObject/​AdvancedColumnResolverTest.php Tests export consistency and null handling.
tests/​Unit/​Grid/​Column/​Resolver/​DataObject/​AdapterResolverTest.php Tests context-controlled fallback.
src/​Twig/​TemplateGeneratorInterface.php Marks the interface internal.
src/​Twig/​TemplateGenerator.php Uses the isolated environment.
src/​Twig/​Initializers/​SandboxExtensionInitializerInterface.php Adds environment access and extension tag.
src/​Twig/​Initializers/​SandboxExtensionInitializer.php Builds and secures the isolated sandbox.
src/​Twig/​Initializers/​NoObjectAccessAllowed.php Enables deny-all object access.
src/​Grid/​Util/​Trait/​LocalizedValueTrait.php Adds a fallback-control hook.
src/​Grid/​Util/​AdvancedColumnSourceFieldContextInterface.php Defines source-resolution state.
src/​Grid/​Util/​AdvancedColumnSourceFieldContext.php Implements that state.
src/​Grid/​Column/​Transformer/​TwigOperator.php Sanitizes template inputs.
src/​Grid/​Column/​Resolver/​DataObject/​AdvancedColumnResolver.php Aligns source-field exports and null output.
src/​Grid/​Column/​Resolver/​DataObject/​AdapterResolver.php Suppresses fallback in source resolution.
doc/​02_Installation_and_Configuration/​05_Upgrade.md Documents migration and behavior changes.
doc/​01_Architecture_Overview/​01_Grid.md Documents sandbox behavior and extension setup.
config/​twig.yaml Injects tagged Twig extensions.
config/​grid.yaml Registers the source-field context service.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/Twig/Initializers/SandboxExtensionInitializer.php
Comment thread src/Twig/Initializers/SandboxExtensionInitializerInterface.php Outdated
Comment thread src/Twig/TemplateGenerator.php Outdated
@fashxp

fashxp commented Sep 30, 2026

Copy link
Copy Markdown
Member Author

Range cap bypass (mixed bounds such as range('a', 1000000)) is fixed: the check is only skipped when both bounds are non-numeric strings; otherwise the span is computed treating a non-numeric bound as 0. Regression tests added for both bound orders.

Copilot AI left a comment

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.

Copilot review overview

🔵 Needs a closer look

The security-sensitive sandbox still permits cumulative resource exhaustion and incorrectly rejects some valid capped ranges.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (3)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Range cap rejects valid non-divisible endpoint ranges

src/​Twig/​Initializers/​SandboxExtensionInitializer.php:282

The cap rejects valid ranges whose endpoints are not evenly divisible by the step. For example, PHP's range(0, 1999, 2) contains exactly 1,000 values (0 through 1998), but this expression computes 1000.5 and throws. Floor the quotient before adding one, and cover a non-unit step at the cap in the regression tests.

Comment thread src/Twig/Initializers/SandboxExtensionInitializer.php Outdated
@fashxp

fashxp commented Sep 30, 2026

Copy link
Copy Markdown
Member Author

Fixed the non-divisible endpoint count (range(0, 1999, 2)): the span is now floored before adding one, with a regression test.

@fashxp
fashxp marked this pull request as draft October 1, 2026 13:57
@fashxp fashxp changed the title Harden the TwigOperator sandbox and fix advanced-column source field export Harden the TwigOperator sandbox Oct 6, 2026
@fashxp fashxp added this to the 2026.4.0 milestone Oct 6, 2026
@fashxp
fashxp force-pushed the feat/output-channels branch from 2cb4c8e to 1061c61 Compare October 7, 2026 07:41
@fashxp fashxp modified the milestones: 2026.4.0, 2026.3.2 Oct 7, 2026
@fashxp
fashxp changed the base branch from 2026.x to 2026.3 October 7, 2026 07:41
@brusch brusch mentioned this pull request Oct 7, 2026
45 of 78 tasks
Builds on #2054: core's sandbox lists still apply, on top of a dedicated
environment, plain-data values and a capped range().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fashxp
fashxp force-pushed the feat/output-channels branch from 1061c61 to 52c8709 Compare October 7, 2026 08:52
@fashxp fashxp modified the milestones: 2026.3.2, 2026.4.0 Oct 7, 2026
@fashxp
fashxp changed the base branch from 2026.3 to 2026.x October 7, 2026 08:52
fashxp and others added 2 commits October 7, 2026 12:05
Addresses the review on #2064: sandbox always on in the isolated environment,
decorated initializers fail closed, known value objects become plain data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Follow-ups from the second review on #2064.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fashxp and others added 2 commits October 8, 2026 08:46
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

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.

🟡 Changes recommended

Function blocking and range-cap enforcement have bypasses, and named range arguments break compatibility.

3 open findings
1 resolved since last review

🧠 Review effort: Balanced


Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.

Comment thread src/Twig/Initializers/SandboxExtensionInitializer.php Outdated
Comment thread src/Twig/Initializers/SandboxExtensionInitializer.php Outdated
Comment thread src/Twig/Initializers/SandboxExtensionInitializer.php Outdated
…allow-list

Addresses the latest Copilot review on #2064.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fashxp and others added 2 commits October 8, 2026 11:31
…arnings

Follow-ups from the third local review on #2064.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Oct 9, 2026

Copy link
Copy Markdown

@fashxp
fashxp marked this pull request as ready for review October 9, 2026 11:28
@fashxp fashxp self-assigned this Oct 9, 2026
@fashxp
fashxp merged commit 3466866 into 2026.x Oct 9, 2026
26 checks passed
@fashxp
fashxp deleted the feat/output-channels branch October 9, 2026 11:30
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 9, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants