CVE-2026-48807: Twig: Sandbox `__toString()` policy bypass via `Traversable` in `join` and `replace` filters
This is a residual bypass of CVE-2026-47732 / GHSA-pr2w-4gpj-cpq4 left after the initial fix for unguarded __toString() calls. It covers two related coercion points that were not caught by the original patch.
Traversable in join and replace filters. SandboxExtension::ensureToStringAllowed() recurses into PHP arrays so that a Stringable object hidden inside an array argument cannot be string-coerced without consulting the security policy. The recursion stops at PHP arrays: a Traversable value passed at the same position is not materialised, so its contents are not policy-checked. CoreExtension::join() and CoreExtension::replace() later materialise such Traversable inputs through self::toArray() and feed them to implode() / strtr(), both of which implicitly call __toString() on contained Stringable objects. The bypass also reproduces when the container implements both Stringable and Traversable: the container’s own __toString() is policy-checked, but the elements yielded by getIterator() are not, and the consuming filters still coerce them to string.
in and not in operators. InBinary and NotInBinary compile to CoreExtension::inFilter(), which falls through to PHP’s <=> operator when comparing a string with a Stringable object. PHP coerces the object to string via __toString() without the sandbox policy being consulted. Beyond the direct side effect, in can also be used as a content-leak oracle: each probe against an attacker-chosen needle leaks one bit of equality, and chained probes can reconstruct the string returned by __toString() even when every method is denied. The bypass reproduces with both array and Traversable haystacks, and on both operand sides.
A sandboxed template author who is allowed to call join / replace, or to use the in / not in operators, can therefore trigger a disallowed __toString() method on objects reachable from the render context, even when that method is not on SecurityPolicy::$allowedMethods. The bypass reproduces both under global sandbox mode and when sandboxing is enabled through SourcePolicyInterface.
References
- github.com/FriendsOfPHP/security-advisories/blob/master/twig/twig/CVE-2026-48807.yaml
- github.com/advisories/GHSA-8x9c-rmqh-456c
- github.com/twigphp/Twig/releases/tag/v3.27.0
- github.com/twigphp/Twig/security/advisories/GHSA-8x9c-rmqh-456c
- nvd.nist.gov/vuln/detail/CVE-2026-48807
- symfony.com/blog/cve-2026-48807-sandbox-tostring-policy-bypass-via-traversable-in-join-replace-and-in-not-in-operators
Code Behaviors & Features
Detect and mitigate CVE-2026-48807 with GitLab Dependency Scanning
Secure your software supply chain by verifying that all open source dependencies used in your projects contain no disclosed vulnerabilities. Learn more about Dependency Scanning →