Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected]
Control: affects -1 + src:php-league-commonmark
User: [email protected]
Usertags: pu

Hi,

As agreed with the security team, I’d like to address the following two
security issues via the next point release (as in #1132184 for
bookworm).

  * Fix DisallowedRawHtml bypass via newline/tab in tag names [CVE-2026-30838]
  * Fix DomainFilteringAdapter hostname boundary bypass [CVE-2026-33347]

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

Regards,

taffit
diff -Nru php-league-commonmark-2.7.0/debian/changelog php-league-commonmark-2.7.0/debian/changelog
--- php-league-commonmark-2.7.0/debian/changelog	2025-05-05 16:16:52.000000000 +0200
+++ php-league-commonmark-2.7.0/debian/changelog	2026-03-26 07:55:35.000000000 +0100
@@ -1,7 +1,16 @@
+php-league-commonmark (2.7.0-1+deb13u1) trixie; urgency=medium
+
+  * Track debian/trixie
+  * Fix DisallowedRawHtml bypass via newline/tab in tag names [CVE-2026-30838]
+  * Fix DomainFilteringAdapter hostname boundary bypass [CVE-2026-33347]
+  * Document CVE fixed in previous changelog entry
+
+ -- David Prévot <[email protected]>  Thu, 26 Mar 2026 07:55:35 +0100
+
 php-league-commonmark (2.7.0-1) unstable; urgency=medium
 
   [ Colin O'Dell ]
-  * Fix XSS in AttributesExtension
+  * Fix XSS in AttributesExtension [CVE-2025-46734]
   * Prepare to release 2.7.0
 
  -- David Prévot <[email protected]>  Mon, 05 May 2025 16:16:52 +0200
diff -Nru php-league-commonmark-2.7.0/debian/control php-league-commonmark-2.7.0/debian/control
--- php-league-commonmark-2.7.0/debian/control	2025-05-05 16:07:46.000000000 +0200
+++ php-league-commonmark-2.7.0/debian/control	2026-03-26 07:55:35.000000000 +0100
@@ -17,7 +17,7 @@
                phpunit
 Standards-Version: 4.7.2
 Homepage: https://commonmark.thephpleague.com/
-Vcs-Git: https://salsa.debian.org/php-team/pear/php-league-commonmark.git -b debian/latest
+Vcs-Git: https://salsa.debian.org/php-team/pear/php-league-commonmark.git -b debian/trixie
 Vcs-Browser: https://salsa.debian.org/php-team/pear/php-league-commonmark
 Rules-Requires-Root: no
 
diff -Nru php-league-commonmark-2.7.0/debian/gbp.conf php-league-commonmark-2.7.0/debian/gbp.conf
--- php-league-commonmark-2.7.0/debian/gbp.conf	2024-07-23 12:20:01.000000000 +0200
+++ php-league-commonmark-2.7.0/debian/gbp.conf	2026-03-26 07:55:35.000000000 +0100
@@ -1,5 +1,5 @@
 [DEFAULT]
-debian-branch = debian/latest
+debian-branch = debian/trixie
 filter = [ '.gitattributes' ]
 pristine-tar = True
 upstream-vcs-tag = %(version%~%-)s
diff -Nru php-league-commonmark-2.7.0/debian/patches/0005-Add-regression-test.patch php-league-commonmark-2.7.0/debian/patches/0005-Add-regression-test.patch
--- php-league-commonmark-2.7.0/debian/patches/0005-Add-regression-test.patch	1970-01-01 01:00:00.000000000 +0100
+++ php-league-commonmark-2.7.0/debian/patches/0005-Add-regression-test.patch	2026-03-26 07:55:35.000000000 +0100
@@ -0,0 +1,42 @@
+From: Colin O'Dell <[email protected]>
+Date: Thu, 5 Mar 2026 07:22:58 -0500
+Subject: Add regression test
+
+Origin: upstream, https://github.com/thephpleague/commonmark/commit/f6e74434dd1a91f195f80cb0184b746a4187272a
+---
+ .../DisallowedRawHtml/DisallowedRawHtmlRendererTest.php   | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+diff --git a/tests/unit/Extension/DisallowedRawHtml/DisallowedRawHtmlRendererTest.php b/tests/unit/Extension/DisallowedRawHtml/DisallowedRawHtmlRendererTest.php
+index 66c4bad..d64d699 100644
+--- a/tests/unit/Extension/DisallowedRawHtml/DisallowedRawHtmlRendererTest.php
++++ b/tests/unit/Extension/DisallowedRawHtml/DisallowedRawHtmlRendererTest.php
+@@ -70,6 +70,16 @@ final class DisallowedRawHtmlRendererTest extends TestCase
+         yield ['<script>', '&lt;script>'];
+         yield ['<plaintext>', '&lt;plaintext>'];
+ 
++        // Newline/whitespace bypass attempts (security fix)
++        yield ["<script   >", "&lt;script   >"];
++        yield ["<script\n>", "&lt;script\n>"];
++        yield ["<script\t>", "&lt;script\t>"];
++        yield ["<script\r\n>", "&lt;script\r\n>"];
++        yield ["<iframe\nwidth=\"560\">", "&lt;iframe\nwidth=\"560\">"];
++
++        // Ensure non-disallowed tags with similar names are NOT filtered
++        yield ['<scriptfoo>', '<scriptfoo>'];
++
+         // Tags not escaped by default
+         yield ['<strong>', '<strong>'];
+     }
+@@ -104,6 +114,11 @@ final class DisallowedRawHtmlRendererTest extends TestCase
+         yield ['<strong/>', '&lt;strong/>'];
+         yield ['<strong />', '&lt;strong />'];
+ 
++        // Newline bypass with custom config
++        yield ["<strong   >", "&lt;strong   >"];
++        yield ["<strong\n>", "&lt;strong\n>"];
++        yield ["<strong\t>", "&lt;strong\t>"];
++
+         // Defaults that I didn't include in my custom config
+         yield ['<title>', '<title>'];
+         yield ['<textarea>', '<textarea>'];
diff -Nru php-league-commonmark-2.7.0/debian/patches/0006-Fix-DisallowedRawHtml-bypass-via-newline-tab-in-tag-.patch php-league-commonmark-2.7.0/debian/patches/0006-Fix-DisallowedRawHtml-bypass-via-newline-tab-in-tag-.patch
--- php-league-commonmark-2.7.0/debian/patches/0006-Fix-DisallowedRawHtml-bypass-via-newline-tab-in-tag-.patch	1970-01-01 01:00:00.000000000 +0100
+++ php-league-commonmark-2.7.0/debian/patches/0006-Fix-DisallowedRawHtml-bypass-via-newline-tab-in-tag-.patch	2026-03-26 07:55:35.000000000 +0100
@@ -0,0 +1,47 @@
+From: Colin O'Dell <[email protected]>
+Date: Thu, 5 Mar 2026 07:43:00 -0500
+Subject: Fix DisallowedRawHtml bypass via newline/tab in tag names
+
+Origin: upstream, https://github.com/thephpleague/commonmark/commit/5c0c4c8fe5a31e8260be99e0afad7136a27c79e6
+Bug: https://github.com/thephpleague/commonmark/security/advisories/GHSA-4v6x-c7xx-hw9f
+Bug-Debian: https://security-tracker.debian.org/tracker/CVE-2026-30838
+---
+ src/Extension/DisallowedRawHtml/DisallowedRawHtmlRenderer.php         | 2 +-
+ .../Extension/DisallowedRawHtml/DisallowedRawHtmlRendererTest.php     | 4 ++--
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/Extension/DisallowedRawHtml/DisallowedRawHtmlRenderer.php b/src/Extension/DisallowedRawHtml/DisallowedRawHtmlRenderer.php
+index 06252a3..2bcb89a 100644
+--- a/src/Extension/DisallowedRawHtml/DisallowedRawHtmlRenderer.php
++++ b/src/Extension/DisallowedRawHtml/DisallowedRawHtmlRenderer.php
+@@ -45,7 +45,7 @@ final class DisallowedRawHtmlRenderer implements NodeRendererInterface, Configur
+             return $rendered;
+         }
+ 
+-        $regex = \sprintf('/<(\/?(?:%s)[ \/>])/i', \implode('|', \array_map('preg_quote', $tags)));
++        $regex = \sprintf('/<(\/?(?:%s)[\s\/>])/i', \implode('|', \array_map('preg_quote', $tags)));
+ 
+         // Match these types of tags: <title> </title> <title x="sdf"> <title/> <title />
+         return \preg_replace($regex, '&lt;$1', $rendered);
+diff --git a/tests/unit/Extension/DisallowedRawHtml/DisallowedRawHtmlRendererTest.php b/tests/unit/Extension/DisallowedRawHtml/DisallowedRawHtmlRendererTest.php
+index d64d699..36d4c18 100644
+--- a/tests/unit/Extension/DisallowedRawHtml/DisallowedRawHtmlRendererTest.php
++++ b/tests/unit/Extension/DisallowedRawHtml/DisallowedRawHtmlRendererTest.php
+@@ -71,7 +71,7 @@ final class DisallowedRawHtmlRendererTest extends TestCase
+         yield ['<plaintext>', '&lt;plaintext>'];
+ 
+         // Newline/whitespace bypass attempts (security fix)
+-        yield ["<script   >", "&lt;script   >"];
++        yield ['<script   >', '&lt;script   >'];
+         yield ["<script\n>", "&lt;script\n>"];
+         yield ["<script\t>", "&lt;script\t>"];
+         yield ["<script\r\n>", "&lt;script\r\n>"];
+@@ -115,7 +115,7 @@ final class DisallowedRawHtmlRendererTest extends TestCase
+         yield ['<strong />', '&lt;strong />'];
+ 
+         // Newline bypass with custom config
+-        yield ["<strong   >", "&lt;strong   >"];
++        yield ['<strong   >', '&lt;strong   >'];
+         yield ["<strong\n>", "&lt;strong\n>"];
+         yield ["<strong\t>", "&lt;strong\t>"];
+ 
diff -Nru php-league-commonmark-2.7.0/debian/patches/0007-Fix-DomainFilteringAdapter-hostname-boundary-bypass.patch php-league-commonmark-2.7.0/debian/patches/0007-Fix-DomainFilteringAdapter-hostname-boundary-bypass.patch
--- php-league-commonmark-2.7.0/debian/patches/0007-Fix-DomainFilteringAdapter-hostname-boundary-bypass.patch	1970-01-01 01:00:00.000000000 +0100
+++ php-league-commonmark-2.7.0/debian/patches/0007-Fix-DomainFilteringAdapter-hostname-boundary-bypass.patch	2026-03-26 07:55:35.000000000 +0100
@@ -0,0 +1,109 @@
+From: Colin O'Dell <[email protected]>
+Date: Thu, 19 Mar 2026 09:01:30 -0400
+Subject: Fix DomainFilteringAdapter hostname boundary bypass
+
+Origin: backport, https://github.com/thephpleague/commonmark/commit/59fb075d2101740c337c7216e3f32b36c204218b
+Bug: https://github.com/thephpleague/commonmark/security/advisories/GHSA-hh8v-hgvp-g3f5
+Bug-Debian: https://security-tracker.debian.org/tracker/CVE-2026-33347
+---
+ src/Extension/Embed/DomainFilteringAdapter.php     | 41 +++++++++++++---------
+ .../Extension/Embed/DomainFilteringAdapterTest.php | 18 ++++++++--
+ 2 files changed, 41 insertions(+), 18 deletions(-)
+
+diff --git a/src/Extension/Embed/DomainFilteringAdapter.php b/src/Extension/Embed/DomainFilteringAdapter.php
+index d150764..b00b028 100644
+--- a/src/Extension/Embed/DomainFilteringAdapter.php
++++ b/src/Extension/Embed/DomainFilteringAdapter.php
+@@ -17,16 +17,16 @@ class DomainFilteringAdapter implements EmbedAdapterInterface
+ {
+     private EmbedAdapterInterface $decorated;
+ 
+-    /** @psalm-var non-empty-string */
+-    private string $regex;
++    /** @var string[] */
++    private array $allowedDomains;
+ 
+     /**
+      * @param string[] $allowedDomains
+      */
+     public function __construct(EmbedAdapterInterface $decorated, array $allowedDomains)
+     {
+-        $this->decorated = $decorated;
+-        $this->regex     = self::createRegex($allowedDomains);
++        $this->decorated      = $decorated;
++        $this->allowedDomains = \array_map('strtolower', $allowedDomains);
+     }
+ 
+     /**
+@@ -34,20 +34,29 @@ class DomainFilteringAdapter implements EmbedAdapterInterface
+      */
+     public function updateEmbeds(array $embeds): void
+     {
+-        $this->decorated->updateEmbeds(\array_values(\array_filter($embeds, function (Embed $embed): bool {
+-            return \preg_match($this->regex, $embed->getUrl()) === 1;
+-        })));
++        $this->decorated->updateEmbeds(\array_values(\array_filter($embeds, [$this, 'isAllowed'])));
+     }
+ 
+-    /**
+-     * @param string[] $allowedDomains
+-     *
+-     * @psalm-return non-empty-string
+-     */
+-    private static function createRegex(array $allowedDomains): string
++    private function isAllowed(Embed $embed): bool
+     {
+-        $allowedDomains = \array_map('preg_quote', $allowedDomains);
+-
+-        return '/^(?:https?:\/\/)?(?:[^.]+\.)*(' . \implode('|', $allowedDomains) . ')/';
++        $url    = $embed->getUrl();
++        $scheme = \parse_url($url, \PHP_URL_SCHEME);
++        if ($scheme === null || $scheme === false) {
++            // Bare domain (no scheme) - assume https:// so parse_url can extract the host
++            $url = 'https://' . $url;
++        } elseif (\strtolower($scheme) !== 'http' && \strtolower($scheme) !== 'https') {
++            return false;
++        }
++
++        $host = \parse_url($url, \PHP_URL_HOST);
++        $host = \strtolower(\rtrim((string) $host, '.'));
++
++        foreach ($this->allowedDomains as $domain) {
++            if ($host === $domain || \str_ends_with($host, '.' . $domain)) {
++                return true;
++            }
++        }
++
++        return false;
+     }
+ }
+diff --git a/tests/unit/Extension/Embed/DomainFilteringAdapterTest.php b/tests/unit/Extension/Embed/DomainFilteringAdapterTest.php
+index 436e398..d320eb7 100644
+--- a/tests/unit/Extension/Embed/DomainFilteringAdapterTest.php
++++ b/tests/unit/Extension/Embed/DomainFilteringAdapterTest.php
+@@ -28,9 +28,23 @@ final class DomainFilteringAdapterTest extends TestCase
+             $embed2 = new Embed('foo.example.com'),
+             new Embed('www.bar.com'),
+             new Embed('badexample.com'),
+-            $embed3 = new Embed('http://foo.bar.com'),
+-            $embed4 = new Embed('https://foo.bar.com/baz'),
++            $embed3 = new Embed('HTTP://foo.bar.com'),
++            $embed4 = new Embed('hTtPs://foo.bar.com/baz'),
+             new Embed('https://bar.com'),
++            new Embed('https://example.com.evil'),
++            new Embed('https://example.com.evil/path'),
++            new Embed('https://foo.bar.com.evil'),
++            new Embed('example.com.evil'),
++            new Embed('example.com.evil/path'),
++            new Embed('foo.bar.com.evil'),
++            new Embed('https://[email protected]'),
++            new Embed('https://user:[email protected]'),
++            new Embed('https://example.com:[email protected]/path'),
++            new Embed('javascript:alert(1)'),
++            new Embed('ftp://example.com'),
++            new Embed('file:///etc/passwd'),
++            new Embed('data:text/html,<script>alert(1)</script>'),
++            new Embed('//example.com/path'),
+         ];
+ 
+         $inner = $this->createMock(EmbedAdapterInterface::class);
diff -Nru php-league-commonmark-2.7.0/debian/patches/series php-league-commonmark-2.7.0/debian/patches/series
--- php-league-commonmark-2.7.0/debian/patches/series	2025-05-05 16:07:48.000000000 +0200
+++ php-league-commonmark-2.7.0/debian/patches/series	2026-03-26 07:55:35.000000000 +0100
@@ -2,3 +2,6 @@
 0002-Drop-tests-breaking-under-PHPUnit-11.patch
 0003-Mark-Data-Provider-method-as-static.patch
 0004-Modernize-PHPUnit-syntax.patch
+0005-Add-regression-test.patch
+0006-Fix-DisallowedRawHtml-bypass-via-newline-tab-in-tag-.patch
+0007-Fix-DomainFilteringAdapter-hostname-boundary-bypass.patch

Attachment: signature.asc
Description: PGP signature

Reply via email to