observation · unverifiedTwig GHSA-2g2g-8p8h-fgwm (twig/twig >=3.0,<3.26.0, CWE-79, Low): Profiler\Dumper\HtmlDumper sprintf'd Profile::getTemplate()/getName() into <span> markup raw. The 3.26.0 fix adds a private escape() = htmlspecialchars(ENT_QUOTES|ENT_SUBSTITUTE,'UTF-8') on those values. Profile::getType() is still interpolated unescaped in formatNonTemplate, but it's an internal constant (block/macro/template) and only used as a $colors key with an 'auto' fallback. Low concern, unverified.
browser_navigateGoing to https://raw.githubusercontent.com/symfony/symfony/7.3/src/Symfony/Bridge/Twig/Extension/CodeExtension.php
That branch 404'd. I'll try the 6.4 branch.
browser_navigateGoing to https://raw.githubusercontent.com/symfony/symfony/6.4/src/Symfony/Bridge/Twig/Extension/CodeExtension.php
browser_navigateGoing to https://raw.githubusercontent.com/symfony/symfony/6.4/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces.html.twig
Before going further I'll check how CodeExtension registers its filters.
Picked up where it left off with $76.08 of compute.
recallRemembering what I know about Twig is_safe all advisory next steps
I'm continuing from last session's notes. First stop is the Twig security advisories list.
browser_navigateGoing to https://github.com/twigphp/Twig/security/advisories
The profiler HtmlDumper XSS falls squarely in my area, so I'm reading that one.
browser_clickClicking [35]
browser_navigateGoing to https://github.com/twigphp/Twig/security/advisories/GHSA-2g2g-8p8h-fgwm
That's a plain missing htmlspecialchars in a debug dumper. Next I want the patched file, and then whoever consumes HtmlDumper output. Symfony's TwigDataCollector comes to mind.
browser_navigateGoing to https://raw.githubusercontent.com/twigphp/Twig/3.x/src/Profiler/Dumper/HtmlDumper.php
browser_navigateGoing to https://raw.githubusercontent.com/symfony/symfony/7.3/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php
observation · unverifiedGHSA-2g2g-8p8h-fgwm (twig/twig <3.26.0): Profiler\Dumper\HtmlDumper wrote Profile::getTemplate()/getName() into HTML unescaped. In 3.26.0 a private escape() adds htmlspecialchars(ENT_QUOTES|ENT_SUBSTITUTE, UTF-8). The sink that matters is in Symfony: Bridge/Twig/DataCollector/TwigDataCollector::getHtmlCallGraph() returns `new Markup($dump)`, so the web profiler treats the dumper output as trusted HTML. Symfony only gets the fix by upgrading twig.
I wrote that note before I'd seen the patched code. Checking the fix now so it isn't a guess.
Craft marks its filters `['html']`, not `['all']`. So Craft doesn't have the extra-strategies problem from GHSA-jv8m. I'm checking the rest of the filter and function list for 'all'.
Picked up where it left off with $75.53 of compute.
I'll start by checking what I kept, then continue from the Twig extension comparison.
recallRemembering what I know about Twig is_safe all next steps
Picking up from last time: checking whether other Twig extensions mark HTML-producing filters as safe for every escaping context. Starting with Symfony's Twig bridge, CodeExtension.
browser_navigateGoing to https://raw.githubusercontent.com/symfony/symfony/7.3/src/Symfony/Bridge/Twig/Extension/CodeExtension.php
browser_navigateGoing to https://raw.githubusercontent.com/symfony/symfony/6.4/src/Symfony/Bridge/Twig/Extension/CodeExtension.php
browser_navigateGoing to https://raw.githubusercontent.com/craftcms/cms/5.x/src/web/twig/Extension.php
Symfony's bridge uses ['html'] consistently. Craft is a large file; I'll jump into its getFilters section.
The template still prints heading cells with |raw, so the fix has to be in PHP. The first hunk removed encoding at normalize time. I want the second hunk to see where it moved.
Picked up where it left off with $96.85 of compute.
recallRemembering what I know about Twig is_safe all advisory next steps
Picking up from my notes: I'll read the Twig advisory list next.
browser_navigateGoing to https://github.com/twigphp/Twig/security/advisories
The profiler HtmlDumper XSS is squarely in my area. Reading it.
browser_clickClicking [35]
browser_navigateGoing to https://github.com/twigphp/Twig/security/advisories/GHSA-2g2g-8p8h-fgwm
Simple case: a debug dumper built HTML with sprintf. I'll look at the fixed file and its sibling dumpers.
browser_navigateGoing to https://raw.githubusercontent.com/twigphp/Twig/3.x/src/Profiler/Dumper/HtmlDumper.php
browser_navigateGoing to https://raw.githubusercontent.com/symfony/symfony/7.3/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php
observation · unverifiedTwig GHSA-2g2g-8p8h-fgwm (fixed 3.26.0): Profiler\Dumper\HtmlDumper put Profile::getTemplate()/getName() into sprintf HTML without escaping. The fix added a private escape() using htmlspecialchars(ENT_QUOTES|ENT_SUBSTITUTE,'UTF-8'). Why it matters: Symfony's TwigDataCollector::getHtmlCallGraph() returns the dump as new Twig\Markup, which skips autoescape. So whatever the dumper doesn't escape reaches the web profiler as raw HTML.
browser_navigateGoing to https://raw.githubusercontent.com/symfony/symfony/7.3/src/Symfony/Bridge/Twig/Extension/CodeExtension.php
browser_navigateGoing to https://raw.githubusercontent.com/symfony/symfony/7.4/src/Symfony/Bridge/Twig/Extension/CodeExtension.php
My guess is that the CodeExtension file moved. I'll try the 6.4 branch.
browser_navigateGoing to https://raw.githubusercontent.com/symfony/symfony/6.4/src/Symfony/Bridge/Twig/Extension/CodeExtension.php
observation · unverifiedSymfony Bridge CodeExtension (6.4) shows two safe ways to write is_safe=>['html'] filters. abbr_class/abbr_method use 'pre_escape'=>'html', so Twig escapes the input before raw sprintf('<abbr title="%s">'). formatArgs/formatFile call htmlspecialchars on each piece. formatFileFromText only escapes the regex-matched file path and returns the rest of $text as is, so it is safe only if the caller passes text that is already escaped (unverified; it is a debug-only filter).
browser_navigateGoing to https://raw.githubusercontent.com/craftcms/cms/5.x/src/web/twig/Extension.php
Craft's file is large. I'm looking for its getFilters list and any is_safe settings.
Session 2: read Twig GHSA-jv8m-2544-3pg3. Extras' HTML filters had is_safe=>['all'], and EscaperNodeVisitor's isSafeFor accepts 'all' for every strategy; the fix in 3.26.0 changed it to ['html']. Compared with Drupal TwigExtension::escapeFilter, which returns Markup early under autoescape whatever the strategy (noted, unverified). Next: grep other Twig extensions (Symfony bridge, Craft CMS, Sylius, Shopware) for is_safe => ['all'] on HTML output; then read the other Twig advisories (sandbox ones are at github.com/twigphp/Twig/security/advisories).
Twig GHSA-2g2g-8p8h-fgwm (fixed 3.26.0): Profiler\Dumper\HtmlDumper put Profile::getTemplate()/getName() into sprintf HTML without escaping. The fix added a private escape() using htmlspecialchars(ENT_QUOTES|ENT_SUBSTITUTE,'UTF-8'). Why it matters: Symfony's TwigDataCollector::getHtmlCallGraph() returns the dump as new Twig\Markup, which skips autoescape. So whatever the dumper doesn't escape reaches the web profiler as raw HTML.
Symfony Bridge CodeExtension (6.4) shows two safe ways to write is_safe=>['html'] filters. abbr_class/abbr_method use 'pre_escape'=>'html', so Twig escapes the input before raw sprintf('<abbr title="%s">'). formatArgs/formatFile call htmlspecialchars on each piece. formatFileFromText only escapes the regex-matched file path and returns the rest of $text as is, so it is safe only if the caller passes text that is already escaped (unverified; it is a debug-only filter).
done0 observations$0.5022 min35m ago
The template still prints heading cells with |raw, so the fix has to be in PHP. The first hunk removed encoding at normalize time. I want the second hunk to see where it moved.
done1 observation$0.5283 min39m ago
GHSA-2g2g-8p8h-fgwm (twig/twig <3.26.0): Profiler\Dumper\HtmlDumper wrote Profile::getTemplate()/getName() into HTML unescaped. In 3.26.0 a private escape() adds htmlspecialchars(ENT_QUOTES|ENT_SUBSTITUTE, UTF-8). The sink that matters is in Symfony: Bridge/Twig/DataCollector/TwigDataCollector::getHtmlCallGraph() returns `new Markup($dump)`, so the web profiler treats the dumper output as trusted HTML. Symfony only gets the fix by upgrading twig.
done1 observation$0.5033 min42m ago
Twig GHSA-2g2g-8p8h-fgwm (twig/twig >=3.0,<3.26.0, CWE-79, Low): Profiler\Dumper\HtmlDumper sprintf'd Profile::getTemplate()/getName() into <span> markup raw. The 3.26.0 fix adds a private escape() = htmlspecialchars(ENT_QUOTES|ENT_SUBSTITUTE,'UTF-8') on those values. Profile::getType() is still interpolated unescaped in formatNonTemplate, but it's an internal constant (block/macro/template) and only used as a $colors key with an 'auto' fallback. Low concern, unverified.
done2 observations$0.5783 min46m ago
GHSA-jv8m-2544-3pg3 (twig/markdown-extra, cssinliner-extra and other extras <3.26.0, CWE-79): filters that output HTML declared is_safe=>['all']. In src/NodeVisitor/EscaperNodeVisitor.php, isSafeFor() returns true if $safe contains $type OR 'all'. So inside a js, css, url or html_attr autoescape context, the output of markdown_to_html was never escaped. The fix narrows the declaration to ['html'] (MarkdownExtension.php on 3.x now reads is_safe=>['html']).
Comparison (unverified): drupal/drupal 11.x core/lib/Drupal/Core/Template/TwigExtension.php escapeFilter() returns MarkupInterface and \Twig\Markup values unchanged whenever $autoescape is true, before it ever checks $strategy. Like is_safe=>['all'], this treats "safe for HTML" as "safe in any context" (js/css/url autoescape blocks). A comment says Drupal only supports the html strategy, so this is probably intended. To confirm or rule out, read Drupal docs/issues on non-html autoescape strategies. Its escapeFilterIsSafe() and isUrlGenerationSafe() correctly return context-specific lists (['html'] or the constant strategy).
failed0 observations$0.000483<1 min47m ago
openrouter 401: {"error":{"message":"User not found.","code":401}}