Skip to content

Make the sandbox a first-class citizen with a dedicated Sandbox class#4854

Open
fabpot wants to merge 1 commit into
twigphp:3.xfrom
fabpot:sandbox-first-class-citizen
Open

Make the sandbox a first-class citizen with a dedicated Sandbox class#4854
fabpot wants to merge 1 commit into
twigphp:3.xfrom
fabpot:sandbox-first-class-citizen

Conversation

@fabpot

@fabpot fabpot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

I've been thinking about making the sabdbox feature as a first class citizen for years. With all the work that has been done recently on security issues, I spent some time on it again. Here is the result.

The main ideas:

  • Currently, the sandbox is thigtly coupled to the "main" environment: SandboxExtension is registered on the environmen directly, so it instruments all compiled template, and adds runtime checks to all renders, trusted or not. As recommended in the docs, you should have a dedicated environment for sandboxes, different from the main one, but it's not really "enforced" nor natural to do.
  • As a consequence, we store some state via enableSandbox()/disableSandbox() with try/finally patterns scattered across the codebase to support rendering sandboxed and non-sandboxed templates from a environment.
  • When using one environment, a sandboxed template can include anything the loader can load, sees every application global, and inherits all extensions, this is a footgun (again, already not recommended in the docs).
  • There are too maybe "knobs": global mode, enableSandbox(), {% include(..., sandboxed: true) %}, and {% sandbox %}.

The new Twig\Sandbox\Sandbox class renders untrusted templates through a dedicated, always-sandboxed environment crafted by the developer. Taht way, there is no state to toggle and nothing leaks between the main environment and the sandbox, in either direction.

@upsun-dispatch

upsun-dispatch Bot commented Jul 4, 2026

Copy link
Copy Markdown

📋 PR Summary

This PR refactors Twig's sandbox into a first-class citizen by introducing a dedicated Sandbox class and extracting security-checking logic into a new SecurityChecker, while thinning out SandboxExtension. Node classes and the core/environment wiring are updated to route through the new components, and documentation, deprecations, and extensive tests are added.

Changes
Layer / File(s) Summary
Sandbox core — new Sandbox class & security checker
src/Sandbox/Sandbox.php New Sandbox class that wraps a dedicated Environment, enforces a strict SecurityPolicy, and exposes render/display/stream helpers.
src/Sandbox/SecurityChecker.php New class extracting the security-checking logic (tags/filters/functions/methods/properties) previously embedded in SandboxExtension.
src/Sandbox/SecurityPolicy.php Adds an isStrict() capability used by the Sandbox to enforce strict-mode policies.
src/Extension/SandboxExtension.php Significantly slimmed down (+43/-143) as security-checking responsibilities move to SecurityChecker.
Node wiring — route through new security components
src/Node/CheckSecurityCallNode.php Updated to reference the new security-checking component.
src/Node/Expression/Binary/HasEveryBinary.php Minor update to align with the new sandbox/security wiring.
src/Node/Expression/Binary/HasSomeBinary.php Minor update to align with the new sandbox/security wiring.
src/Node/Expression/CallExpression.php Minor update to align with the new sandbox/security wiring.
src/Node/SandboxNode.php Adjusted to work with the refactored sandbox components.
Environment & core extension integration
src/Environment.php Small wiring changes to integrate the dedicated Sandbox class.
src/Extension/CoreExtension.php Adjusts include handling and sandbox integration points.
Documentation & deprecations
CHANGELOG Documents the new Sandbox class and related deprecations.
doc/deprecated.rst Notes deprecated sandbox APIs superseded by the new class.
doc/functions/include.rst Updates include-function docs relating to sandbox behavior.
doc/sandbox.rst Expanded sandbox documentation describing the new first-class Sandbox usage.
doc/tags/sandbox.rst Minor sandbox tag doc update.
Static analysis
phpstan-baseline.neon Adds baseline entries for the new code.
Tests
tests/Extension/CoreTest.php Adds tests covering core-extension sandbox integration.
tests/Extension/SandboxDeprecationsTest.php New test verifying sandbox deprecation paths.
tests/Extension/SandboxStateChangeTest.php Minor test additions for sandbox state changes.
tests/Extension/SandboxTest.php Extends sandbox extension tests.
tests/Sandbox/SandboxTest.php New comprehensive test suite for the Sandbox class, including custom policy acceptance.
tests/Node/Expression/FilterTest.php Minor test adjustments for renamed/rewired components.
tests/Node/SandboxTest.php Minor test adjustments for the refactored sandbox nodes.
tests/Fixtures/functions/include/sandbox.legacy.test Renamed fixture reflecting legacy sandbox include behavior.
tests/Fixtures/functions/include/sandbox_disabling.legacy.test Renamed fixture reflecting legacy sandbox include behavior.
tests/Fixtures/functions/include/sandbox_disabling_ignore_missing.legacy.test Renamed fixture reflecting legacy sandbox include behavior.

@upsun-dispatch upsun-dispatch 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.

📋 Upsun Dispatch Review: full · 26 files reviewed · no issues found

Review details

Commit: Commit ac46f0a
Model: claude-opus-4-8
Panel: security · correctness · robustness · design

@fabpot fabpot force-pushed the sandbox-first-class-citizen branch from ac46f0a to 34a32bb Compare July 5, 2026 06:50

@upsun-dispatch upsun-dispatch 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.

📋 Upsun Dispatch Review: incremental · 27 files reviewed · no new issues

Review details

Commit: Commits ac46f0a..34a32bb
Model: claude-opus-4-8
Panel: security · correctness · robustness · design

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant