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/twig version, 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 Template instances held in Twig\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\Environment across 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, or SourcePolicyInterface.
  • 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() or disableSandbox(), swaps policies with setSecurityPolicy(), 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/twig package, Composer lockfile, image, generated dependency report, SBOM, and deployment artifact to 3.27.0+.
  • Restart long-lived PHP workers and render services so in-memory Environment and Template instances are replaced after the package update.
  • Keep untrusted template rendering in a dedicated sandboxed Twig\Environment with a restricted loader and minimal policy. Avoid toggling sandbox state on an environment also used for trusted templates.
  • Treat SourcePolicyInterface as 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.md file 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.json while composer.lock, images, SBOMs, or dependency reports still resolve an affected Twig version.
  • Forgetting workers that keep Twig\Environment objects 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.

References