<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[58424] trunk: KSES: Fix tests and detection of HTML Bogus Comment spans.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/58424">58424</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/58424","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>dmsnell</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2024-06-17 12:02:50 +0000 (Mon, 17 Jun 2024)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>KSES: Fix tests and detection of HTML Bogus Comment spans.

In <a href="https://core.trac.wordpress.org/changeset/58418">[58418]</a> a test was added without the `test_` prefix in its function
name, and because of that, it wasn't run in the test suite.
The prefix has been added to ensure that it runs.

In the original patch, due to a logical bug, a recursive loop to
transform the inside contents of the bogus comments was never run
more than once. This has been fixed.

This patch also includes one more case where `kses` wasn't
properly detecting the bogus comment state, and adds a test case
to cover this. It limits itself to some but not all constructions
of invalid markup declaration so that it doesn't conflict with
existing behaviors around those and other kinds of invalid comments.

Props ellatrix, dmsnell.
See <a href="https://core.trac.wordpress.org/ticket/61009">#61009</a>.
Follow-up to <a href="https://core.trac.wordpress.org/changeset/58418">[58418]</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesksesphp">trunk/src/wp-includes/kses.php</a></li>
<li><a href="#trunktestsphpunittestsksesphp">trunk/tests/phpunit/tests/kses.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpincludesksesphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/kses.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/kses.php    2024-06-17 10:06:38 UTC (rev 58423)
+++ trunk/src/wp-includes/kses.php      2024-06-17 12:02:50 UTC (rev 58424)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -988,6 +988,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                (<!--.*?(-->|$))   #  - Normative HTML comments.
</span><span class="cx" style="display: block; padding: 0 10px">                |
</span><span class="cx" style="display: block; padding: 0 10px">                </[^a-zA-Z][^>]*>  #  - Closing tags with invalid tag names.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                |
+               <![^>]*>           #  - Invalid markup declaration nodes. Not all invalid nodes
+                                  #    are matched so as to avoid breaking legacy behaviors.
</ins><span class="cx" style="display: block; padding: 0 10px">         )
</span><span class="cx" style="display: block; padding: 0 10px">        |
</span><span class="cx" style="display: block; padding: 0 10px">        (<[^>]*(>|$)|>)        # Tag-like spans of text.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1114,22 +1117,30 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /*
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * When a closing tag appears with a name that isn't a valid tag name,
-        * it must be interpreted as an HTML comment. It extends until the
-        * first `>` character after the initial opening `</`.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * When certain invalid syntax constructs appear, the HTML parser
+        * shifts into what's called the "bogus comment state." This is a
+        * plaintext state that consumes everything until the nearest `>`
+        * and then transforms the entire span into an HTML comment.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * Preserve these comments and do not treat them like tags.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         *
+        * @see https://html.spec.whatwg.org/#bogus-comment-state
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        if ( 1 === preg_match( '~^</[^a-zA-Z][^>]*>$~', $content ) ) {
-               $content     = substr( $content, 2, -1 );
-               $transformed = null;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( 1 === preg_match( '~^(?:</[^a-zA-Z][^>]*>|<![a-z][^>]*>)$~', $content ) ) {
+               /**
+                * Since the pattern matches `</…>` and also `<!…>`, this will
+                * preserve the type of the cleaned-up token in the output.
+                */
+               $opener  = $content[1];
+               $content = substr( $content, 2, -1 );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                while ( $transformed !== $content ) {
-                       $transformed = wp_kses( $content, $allowed_html, $allowed_protocols );
-                       $content     = $transformed;
-               }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         do {
+                       $prev    = $content;
+                       $content = wp_kses( $content, $allowed_html, $allowed_protocols );
+               } while ( $prev !== $content );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                return "</{$transformed}>";
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         // Recombine the modified inner content with the original token structure.
+               return "<{$opener}{$content}>";
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /*
</span></span></pre></div>
<a id="trunktestsphpunittestsksesphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/kses.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/kses.php        2024-06-17 10:06:38 UTC (rev 58423)
+++ trunk/tests/phpunit/tests/kses.php  2024-06-17 12:02:50 UTC (rev 58424)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1936,11 +1936,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @ticket 61009
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @dataProvider data_html_containing_various_kinds_of_html_comments
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * @param string $html_comment    HTML containing a comment; must not be a valid comment
</span><span class="cx" style="display: block; padding: 0 10px">         *                                but must be syntax which a browser interprets as a comment.
</span><span class="cx" style="display: block; padding: 0 10px">         * @param string $expected_output How `wp_kses()` ought to transform the comment.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        public function wp_kses_preserves_html_comments( $html_comment, $expected_output ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public function test_wp_kses_preserves_html_comments( $html_comment, $expected_output ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 $this->assertSame(
</span><span class="cx" style="display: block; padding: 0 10px">                        $expected_output,
</span><span class="cx" style="display: block; padding: 0 10px">                        wp_kses( $html_comment, array() ),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1957,6 +1959,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                return array(
</span><span class="cx" style="display: block; padding: 0 10px">                        'Normative HTML comment'            => array( 'before<!-- this is a comment -->after', 'before<!-- this is a comment -->after' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'Closing tag with invalid tag name' => array( 'before<//not a tag>after', 'before<//not a tag>after' ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'Incorrectly opened comment (Markup declaration)' => array( 'before<!also not a tag>after', 'before<!also not a tag>after' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre>
</div>
</div>

</body>
</html>