Skip to content

Commit

Permalink
HTML API: Prevent out-of-bounds string access.
Browse files Browse the repository at this point in the history
Rebuild of 8905faf after #5725.

Co-authored-by: Jon Surrell <sirreal@users.noreply.github.com>
Co-authored-by: Bernie Reiter <bernhard-reiter@git.wordpress.org>
  • Loading branch information
3 people committed Dec 20, 2023
1 parent c3166be commit 9e7167a
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 8 deletions.
21 changes: 13 additions & 8 deletions src/wp-includes/html-api/class-wp-html-tag-processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -1312,7 +1312,7 @@ private function parse_next_tag() {

$this->token_starts_at = $at;

if ( '/' === $this->html[ $at + 1 ] ) {
if ( $at + 1 < $doc_length && '/' === $this->html[ $at + 1 ] ) {
$this->is_closing_tag = true;
++$at;
} else {
Expand Down Expand Up @@ -1346,7 +1346,7 @@ private function parse_next_tag() {
* Abort if no tag is found before the end of
* the document. There is nothing left to parse.
*/
if ( $at + 1 >= strlen( $html ) ) {
if ( $at + 1 >= $doc_length ) {
$this->parser_state = self::STATE_INCOMPLETE;

return false;
Expand All @@ -1368,7 +1368,7 @@ private function parse_next_tag() {
) {
$closer_at = $at + 4;
// If it's not possible to close the comment then there is nothing more to scan.
if ( strlen( $html ) <= $closer_at ) {
if ( $doc_length <= $closer_at ) {
$this->parser_state = self::STATE_INCOMPLETE;

return false;
Expand All @@ -1388,20 +1388,20 @@ private function parse_next_tag() {
* See https://html.spec.whatwg.org/#parse-error-incorrectly-closed-comment
*/
--$closer_at; // Pre-increment inside condition below reduces risk of accidental infinite looping.
while ( ++$closer_at < strlen( $html ) ) {
while ( ++$closer_at < $doc_length ) {
$closer_at = strpos( $html, '--', $closer_at );
if ( false === $closer_at ) {
$this->parser_state = self::STATE_INCOMPLETE;

return false;
}

if ( $closer_at + 2 < strlen( $html ) && '>' === $html[ $closer_at + 2 ] ) {
if ( $closer_at + 2 < $doc_length && '>' === $html[ $closer_at + 2 ] ) {
$at = $closer_at + 3;
continue 2;
}

if ( $closer_at + 3 < strlen( $html ) && '!' === $html[ $closer_at + 2 ] && '>' === $html[ $closer_at + 3 ] ) {
if ( $closer_at + 3 < $doc_length && '!' === $html[ $closer_at + 2 ] && '>' === $html[ $closer_at + 3 ] ) {
$at = $closer_at + 4;
continue 2;
}
Expand All @@ -1414,7 +1414,7 @@ private function parse_next_tag() {
* https://html.spec.whatwg.org/multipage/parsing.html#tag-open-state
*/
if (
strlen( $html ) > $at + 8 &&
$doc_length > $at + 8 &&
'[' === $html[ $at + 2 ] &&
'C' === $html[ $at + 3 ] &&
'D' === $html[ $at + 4 ] &&
Expand All @@ -1440,7 +1440,7 @@ private function parse_next_tag() {
* https://html.spec.whatwg.org/multipage/parsing.html#tag-open-state
*/
if (
strlen( $html ) > $at + 8 &&
$doc_length > $at + 8 &&
( 'D' === $html[ $at + 2 ] || 'd' === $html[ $at + 2 ] ) &&
( 'O' === $html[ $at + 3 ] || 'o' === $html[ $at + 3 ] ) &&
( 'C' === $html[ $at + 4 ] || 'c' === $html[ $at + 4 ] ) &&
Expand Down Expand Up @@ -1512,6 +1512,11 @@ private function parse_next_tag() {
* See https://html.spec.whatwg.org/#parse-error-invalid-first-character-of-tag-name
*/
if ( $this->is_closing_tag ) {
// No chance of finding a closer.
if ( $at + 3 > $doc_length ) {
return false;
}

$closer_at = strpos( $html, '>', $at + 3 );
if ( false === $closer_at ) {
$this->parser_state = self::STATE_INCOMPLETE;
Expand Down
18 changes: 18 additions & 0 deletions tests/phpunit/tests/html-api/wpHtmlTagProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -2691,4 +2691,22 @@ public function data_updating_attributes_in_malformed_html() {
),
);
}

/**
* @covers WP_HTML_Tag_Processor::next_tag
*/
public function test_handles_malformed_taglike_open_short_html() {
$p = new WP_HTML_Tag_Processor( '<' );
$result = $p->next_tag();
$this->assertFalse( $result, 'Did not handle "<" html properly.' );
}

/**
* @covers WP_HTML_Tag_Processor::next_tag
*/
public function test_handles_malformed_taglike_close_short_html() {
$p = new WP_HTML_Tag_Processor( '</ ' );
$result = $p->next_tag();
$this->assertFalse( $result, 'Did not handle "</ " html properly.' );
}
}

0 comments on commit 9e7167a

Please sign in to comment.