CVE-2026-46636 - Twig cached template sandbox-state bypass
Twig versions through 3.26.0 can make the sandbox filter, tag, and
function allow-list decision sticky for a cached Template instance. In
affected versions, checkSecurity() ran from the template constructor and
the resulting decision stayed attached to the Template object cached inside
the shared Environment.
The risky shape is a PHP application that renders both trusted and untrusted
Twig templates through the same long-lived Environment. If a trusted render
pre-warms a shared layout, include, parent template, trait, or macro outside
the sandbox, a later sandboxed render can reuse that Template instance
without re-checking the current sandbox allow-list for filters, tags, and
functions.
Method, property, and __toString() checks are still runtime checks. The
practical failure is narrower but important: sandboxed template authors may
use filters, tags, or functions that the current SecurityPolicy should have
blocked when mixed sandbox state and cached templates are present.
When to use it
Use this recipe when a PHP application renders both trusted and untrusted Twig
templates, especially with long-lived workers, shared Environment instances,
or cached layouts/macros/includes. It is built for source-code remediation,
sandbox-state review, template isolation, and audit evidence that cached
template security decisions cannot cross trust boundaries.
Inputs
twig/twigversion, package lockfiles, worker model, cache configuration, trusted/untrusted template sources, and sandbox policy.- Source paths that construct Twig environments, enable sandboxing, share template caches, render user-controlled templates, or preload templates.
- Regression fixtures for trusted pre-warm, untrusted render after cache reuse, blocked filters/functions/tags, separated environments, and worker restart.
- Boundary evidence: which users can submit templates, where templates render, enabled filters/functions/tags, cache storage, and rollout owner.
Affected versions
- Vulnerable package:
twig/twig <=3.26.0 - Fixed package:
twig/twig 3.27.0+ - Affected object: cached
Templateinstances held inTwig\Environment::$loadedTemplates - Affected control: filter, tag, and function allow-list checks in sandboxed rendering
- Most exposed runtimes: FrankenPHP, RoadRunner, Symfony Messenger
consumers, PHP-FPM pools with hot workers, queue workers, render services,
and application processes that reuse one
Twig\Environmentacross trusted and untrusted renders
Indicator-of-exposure
- The repository depends on
twig/twig <=3.26.0. - The application renders user-controlled or semi-trusted templates under
SandboxExtension,SecurityPolicy, orSourcePolicyInterface. - Trusted developer templates and untrusted tenant, CMS, email, report,
plugin, theme, or automation templates share the same
Twig\Environment. - Code toggles sandbox state with
enableSandbox()ordisableSandbox(), swaps policies withsetSecurityPolicy(), or uses a source policy that can produce different sandbox decisions over process lifetime. - Long-lived workers reuse the same Twig environment after rendering shared layouts, includes, parents, traits, imports, or macros outside the sandbox.
- Template contexts expose privileged functions, filters, tags, services, request/session objects, URL signers, database-backed objects, or internal helper functions to templates.
Quick checks:
rg -n "twig/twig|Twig\\Environment|SandboxExtension|SecurityPolicy|SourcePolicyInterface|enableSandbox|disableSandbox|setSecurityPolicy|createTemplate|render\(|include\(|extends|use|import|macro|FrankenPHP|RoadRunner|Messenger|worker|queue" .
composer show twig/twig
composer why twig/twig
rg -n "Twig\\Environment|SandboxExtension|SecurityPolicy|SourcePolicyInterface|enableSandbox|disableSandbox|setSecurityPolicy|twig.cache|var/cache|worker|messenger|roadrunner|frankenphp" composer.json composer.lock config src templates app resources . 2>/dev/null
Windows:
rg -n "twig/twig|Twig\\Environment|SandboxExtension|SecurityPolicy|SourcePolicyInterface|enableSandbox|disableSandbox|setSecurityPolicy|createTemplate|render\(|include\(|extends|use|import|macro|FrankenPHP|RoadRunner|Messenger|worker|queue" .
composer show twig/twig
composer why twig/twig
rg -n "Twig\\Environment|SandboxExtension|SecurityPolicy|SourcePolicyInterface|enableSandbox|disableSandbox|setSecurityPolicy|twig.cache|var/cache|worker|messenger|roadrunner|frankenphp" composer.json composer.lock config src templates app resources .
Do not render bypass templates, flip sandbox state in production, dump template contexts, or print rendered customer, tenant, session, or secret-bearing data during triage.
Remediation strategy
- Upgrade every controlled
twig/twigpackage, Composer lockfile, image, generated dependency report, SBOM, and deployment artifact to3.27.0+. - Restart long-lived PHP workers and render services so in-memory
EnvironmentandTemplateinstances are replaced after the package update. - Keep untrusted template rendering in a dedicated sandboxed
Twig\Environmentwith a restricted loader and minimal policy. Avoid toggling sandbox state on an environment also used for trusted templates. - Treat
SourcePolicyInterfaceas a legacy risk signal. Twig 3.27.0 deprecates it; prefer a dedicated sandboxed environment instead of per-source sandbox switching when accepting untrusted templates. - Clear or rebuild deployment caches and warmed template artifacts where this repository controls them. Record operator-owned cache and worker restart steps when the repo cannot perform them.
- Inventory template filters, functions, tags, and globals exposed to untrusted authors. Remove broad helpers and pass narrow view models instead of service containers or rich domain objects.
- Add safe regression checks that verify the fixed dependency and that untrusted rendering does not share a mutable Twig environment with trusted rendering.
The prompt
You are remediating CVE-2026-46636 / GHSA-529h-vh3j-85hq, a Twig sandbox
allow-list bypass where cached Template instances can keep a stale sandbox
state decision across trusted and untrusted renders. Produce exactly one
output:
- A reviewer-ready PR/change request that upgrades Twig, removes or documents
mixed sandbox-state rendering, restarts or documents long-lived workers,
refreshes generated artifacts, adds safe regression checks, and records
operator cleanup, or
- TRIAGE.md if this repository does not control an affected Twig runtime or
any sandboxed user-controlled template surface.
## Rules
- Scope only CVE-2026-46636 / GHSA-529h-vh3j-85hq and directly related Twig
sandbox state, cached Template instances, worker lifecycle, and template
environment separation.
- Treat template source, rendered output, template contexts, users, tenants,
sessions, orders, emails, service objects, secrets, logs, compiled templates,
and worker memory as sensitive.
- Do not render exploit templates, flip sandbox state in production, pre-warm
live templates to test bypass behavior, dump contexts, print rendered
sensitive data, or weaken sandbox policy for validation.
- Do not remove sandboxing, authorization, template review, audit logging,
cache isolation, or tests to silence the advisory.
- Do not auto-merge.
## Steps
1. Inventory all Twig usage controlled by the repository: `composer.json`,
`composer.lock`, Symfony/Laravel/Drupal/CMS integrations, Dockerfiles,
deployment manifests, CI images, generated dependency reports, SBOMs,
template loaders, template cache directories, worker entry points, local
forks, and render services.
2. Determine every resolved `twig/twig` version. A target is vulnerable if it
resolves to `<=3.26.0`.
3. Search for sandboxed template rendering and mixed state:
`SandboxExtension`, `SecurityPolicy`, `SourcePolicyInterface`,
`enableSandbox`, `disableSandbox`, `setSecurityPolicy`, shared
`Twig\Environment` services, `createTemplate`, user template preview/edit
endpoints, shared layouts, includes, parents, traits, imports, macros,
warmed caches, queues, workers, RoadRunner, FrankenPHP, Messenger consumers,
and custom render services.
4. Classify every template-rendering surface:
trusted developer templates, admin-authored templates, tenant templates,
CMS/theme/email/report templates, plugin templates, imported third-party
templates, automation-authored templates, and unauthenticated previews.
5. If Twig is absent, already fixed, or used only for trusted
developer-authored templates with no sandboxed user-template path, stop
with `TRIAGE.md` listing checked files, resolved version, and exposure
rationale.
6. Upgrade all controlled Twig dependencies to `3.27.0+`. Refresh
`composer.lock`, generated dependency reports, SBOMs, image metadata,
deployment manifests, and docs that pin Twig.
7. Remove or harden mixed sandbox-state rendering:
- render untrusted templates through a dedicated sandboxed
`Twig\Environment`;
- keep trusted developer templates in a separate environment or loader;
- avoid runtime calls to `enableSandbox()` and `disableSandbox()` on shared
environments;
- avoid policy swaps on shared environments used by long-lived workers;
- replace `SourcePolicyInterface` routing with an explicit sandboxed
environment when practical;
- document any remaining shared environment, why it is safe after 3.27.0,
and who owns future migration.
8. Restart or document runtime cleanup:
- restart PHP-FPM, FrankenPHP, RoadRunner, Messenger consumers, queue
workers, render workers, and cron workers that can hold Twig objects;
- clear or rebuild compiled Twig caches and warmed templates if this repo
owns deployment scripts;
- document operator-owned cache paths and restart commands outside the repo.
9. Audit untrusted template capabilities:
- list allowed filters, tags, functions, globals, and loaders;
- remove broad helpers, service containers, request/session objects,
filesystem clients, HTTP clients, database clients, and secret-bearing
objects from untrusted contexts;
- require authorization and audit logging for template edit and preview
endpoints.
10. Add safe regression coverage:
- dependency policy rejects `twig/twig <=3.26.0`;
- untrusted rendering uses a dedicated sandboxed environment or a reviewed
fixed-version shared environment;
- worker startup or deploy docs include restart coverage for Twig object
caches;
- benign sandboxed templates still render with the intended allow-list;
- tests do not execute bypass payloads or print sensitive rendered data.
11. Add a PR body section named `CVE-2026-46636 operator actions` that states:
- Twig versions before and after;
- which sandboxed template surfaces exist;
- whether trusted and untrusted renders share a `Twig\Environment`;
- which workers, caches, and warmed templates must restart or be cleared;
- whether `SourcePolicyInterface`, sandbox toggling, or policy swapping
remains;
- what filters, tags, functions, globals, or context objects were removed
or narrowed;
- whether audit review, template rollback, or secret rotation is needed;
- which validation commands passed.
12. Run available validation: Composer install/check-platform-reqs, lockfile
integrity, unit/feature tests, benign sandbox rendering tests, static
analysis, lint, deployment rendering, image build, SBOM refresh,
dependency/security scans, and worker startup checks.
13. Use PR title:
`fix(sec): remediate CVE-2026-46636 in Twig sandboxing`.
## Stop conditions
- No affected Twig package, sandboxed user-template renderer, shared Twig
environment, worker process, cache, or deployment artifact is controlled by
this repository.
- The Twig runtime or template service is owned by another team; name the
owner and required fixed version in `TRIAGE.md`.
- Upgrading Twig requires a broader PHP/framework migration or maintenance
window the agent cannot schedule.
- Verification would require rendering bypass templates, changing production
sandbox state, dumping template contexts, touching live customer templates,
or exposing secrets.
- Validation fails for unrelated pre-existing reasons; document those failures
instead of broadening scope.
Output contract
- A reviewer-ready PR or change request that upgrades Twig, restarts long-lived workers, separates trusted and untrusted environments, adds sandbox-state regression tests, and documents cache cleanup.
- Or a
TRIAGE.mdfile that lists inspected files, owner, observed version, template trust boundary, shared cache/environment use, required fix, and residual risk. - The output must include exact validation commands and must not render customer templates, expose secrets, or execute untrusted template code in production.
Verification - what the reviewer looks for
- No controlled Composer lockfile, package manifest, image, SBOM, generated
report, or deployment artifact resolves
twig/twig <=3.26.0. - Long-lived PHP workers and render services are restarted after the update or have explicit operator instructions.
- Trusted and untrusted template rendering do not depend on mutable sandbox
state in one shared
Twig\Environment, or the fixed-version shared design is documented with ownership. - Untrusted template filters, tags, functions, globals, loaders, and context objects are inventoried and narrowed.
- Regression checks cover dependency policy and benign sandbox rendering without exploit payloads or sensitive context output.
Watch for
- Updating
composer.jsonwhilecomposer.lock, images, SBOMs, or dependency reports still resolve an affected Twig version. - Forgetting workers that keep
Twig\Environmentobjects in memory after the package update. - Treating a non-sandboxed pre-warmed shared layout, import, trait, include, or macro as harmless because committed templates are trusted.
- Leaving
enableSandbox()/disableSandbox()toggles or policy swaps on a shared environment used by queue workers or render services. - Testing only short-lived CLI renders while production uses long-lived workers.
Related recipes
- Source code injection sink audit
- Source code authz tenant boundary audit
- OWASP Top 10 2026 audit
- SAST finding triage and fix
References
- GitHub Advisory Database: https://github.com/advisories/GHSA-529h-vh3j-85hq
- Twig vendor advisory: https://github.com/twigphp/Twig/security/advisories/GHSA-529h-vh3j-85hq
- Symfony advisory: https://symfony.com/blog/cve-2026-46636-sandbox-filter-tag-and-function-allow-list-bypass-when-sandbox-state-changes-between-renders
- Twig 3.27.0 release: https://symfony.com/blog/twig-3-27-0-released