CVE-2026-48807: Twig: Sandbox `__toString()` policy bypass via `Traversable` in `join` and `replace` filters

Published Jun 30, 2026
·
Updated

Description

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.

Resolution

SandboxExtension::ensureToStringAllowed() now also recurses into Traversable operands when sandboxing is active for the current source: each value is materialised once and run through the same array-recursion path, so the policy is consulted before the filter implementation can coerce contained objects to strings. This applies to plain Traversable operands as well as to containers that implement both Stringable and Traversable: the container's own toString() is still policy-checked, and the yielded elements are additionally checked. The materialisation is guarded by isSandboxed($source) so that non-sandboxed code paths do not pay the cost or change generator-exhaustion semantics.

InBinary and NotInBinary now implement Twig\Node\CoercesChildrenToStringInterface and declare both operands as string-coerced, so SandboxNodeVisitor wraps each operand in CheckToStringNode. The policy is consulted before CoreExtension::inFilter() reaches PHP's <=> operator, matching the existing protection on the other comparison binaries (Equal, Less, Greater, Spaceship, ...).

Credits

Twig would like to thank Vincent55 Yang and Fabien Potencier for reporting the issues and Fabien Potencier for providing the fix.

Other sources

Twig is a template language for PHP. Prior to 3.27.0, the sandbox toString() checks do not fully cover Traversable values passed to join and replace filters or operands evaluated by the in and not in operators, allowing contained Stringable objects to be coerced to strings without consulting the sandbox policy. This issue is fixed in version 3.27.0.

MITRE

Affected Software

2 affected componentsFixes available
composer/twig/twig<=3.26.0
3.27.0
Symfony Twig<3.27.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade composer/twig/twig to a version that resolves this vulnerability.

    Fixed in 3.27.0
  2. Upgrade

    Upgrade to a fixed release to a version that resolves this vulnerability.

    Fixed in 3.27.0

Event History

Jun 30, 2026
Advisory Published
via GitHub·06:43 PM
Data Sourced
via GitHub·06:43 PM
DescriptionWeaknessAffected Software
Jul 14, 2026
CVE Published
via MITRE·09:29 PM
Data Sourced
via MITRE·09:29 PM
DescriptionWeakness
Data Sourced
via NVD·10:17 PM
DescriptionSeverityWeaknessAffected Software
Free Weekly Intel

Don't miss critical vulnerabilities

Join thousands of security professionals who receive our weekly digest of trending CVEs, zero-days, and exploited vulnerabilities.

No spam. Unsubscribe anytime.

Frequently Asked Questions

1

What is the severity of CVE-2026-48807?

The severity of CVE-2026-48807 is rated at 65.

2

What software is affected by CVE-2026-48807?

CVE-2026-48807 affects the Composer Twig Twig library.

3

What does CVE-2026-48807 address?

CVE-2026-48807 addresses a residual bypass related to unguarded __toString() calls.

4

How do I fix CVE-2026-48807?

To fix CVE-2026-48807, you should update the Twig library to the latest version that contains the patch.

5

What are the coercion points involved in CVE-2026-48807?

CVE-2026-48807 involves coercion points in the join and replace filters that were not addressed in the original patch.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203