Opened 8 weeks ago
Last modified 8 days ago
#65372 reopened defect (bug)
HTML API: Fuzz-test and fix detections in 7.1
| Reported by: | dmsnell | Owned by: | dmsnell |
|---|---|---|---|
| Priority: | normal | Milestone: | 7.1 |
| Component: | HTML API | Version: | |
| Severity: | normal | Keywords: | has-patch has-unit-tests |
| Cc: | Focuses: |
Description (last modified by )
The purpose of this ticket is to track work to fuzz-test the HTML API against inputs that could raise issues or bugs and then fix them in 7.1.
Noted already are a few cases where:
- out-of-bounds text access throws warnings
- HTML normalization is non-idempotent on malformed inputs
- HTML normalization of prefixed attributes like
xlink:href=…(incorrectly serialized asxlink href=…
Change History (66)
This ticket was mentioned in PR #11982 on WordPress/wordpress-develop by @dmsnell.
8 weeks ago
#1
- Keywords has-patch has-unit-tests added
#3
@
6 weeks ago
- Resolution fixed
- Status closed → reopened
Reopening for additional fixes during 7.1 cycle. Additions to be included in description.
This ticket was mentioned in PR #12140 on WordPress/wordpress-develop by @jonsurrell.
6 weeks ago
#5
Fixes an issue where HTML Processor serialization issue where adjusted foreign attributes such as xlink:href were emitted with a space separator as xlink href. After normalization, this would become two attributes, xlink and href.
This keeps duplicate detection keyed to the adjusted qualified attribute name while converting adjusted foreign attributes back to colon-prefixed names when writing HTML. It also adds regression coverage for both WP_HTML_Processor::normalize() and serialize_token().
Trac ticket: https://core.trac.wordpress.org/ticket/65372
## Use of AI Tools
AI assistance: Yes
Tool(s): Codex
Model(s): GPT-5
Used for: Discover issue with fuzz testing, add the regression test, implement fix, perform verification, and draft this PR description.
@jonsurrell commented on PR #12140:
6 weeks ago
#6
### Code review
No issues found. Checked for bugs and CLAUDE.md compliance.
🤖 Generated with Claude Code
@jonsurrell commented on PR #12140:
6 weeks ago
#7
### Code review
No issues found. Checked for bugs and CLAUDE.md compliance.
🤖 Generated with Claude Code
@jonsurrell commented on PR #12140:
6 weeks ago
#8
### Code review
No issues found. Checked for bugs and CLAUDE.md compliance.
Verified that the space-to-colon replacement in `serialize_token()` is safe: the tokenizer never permits spaces in attribute names, so a space can only appear in a qualified name via the adjusted foreign attributes table in `get_qualified_attribute_name()`. Keeping duplicate detection keyed on the space-form qualified name is equivalent to keying on the serialized name, since attribute names from the tag processor are unique lowercase names and the mapping is collision-free.
🤖 Generated with Claude Code
<sub>- If this code review was useful, please react with 👍. Otherwise, react with 👎.</sub>
This ticket was mentioned in PR #12147 on WordPress/wordpress-develop by @dmsnell.
6 weeks ago
#9
Trac ticket: Core-65372
Detected while fuzz-testing the UTF-8 handling code, this defect meant that the tests were verifying the wrong behavior. Namely, they verified a stringification of ASCII digits, which always converted plainly, when they were meant to test handling of invalid UTF-8 sequences.
This patch fixes the test by calling chr() on the byte values before concatenating into a big string.
Follow-up to: [60950].
This ticket was mentioned in PR #12148 on WordPress/wordpress-develop by @dmsnell.
6 weeks ago
#10
Trac ticket: Core-65372
Found during fuzzing work on the HTML API and adjacent code. The previous version of this function used a Unicode PCRE to detect noncharacter code points, but that invocation failed if the input string contained sequences of invalid UTF-8 bytes.
This patch replaces the Unicode PCRE with a mapped sequence of raw bytes. This version works in environments without Unicode support and it works when invalid bytes are present, making it possible to remove the fallback function as well.
Follow-up to [61000].
@dmsnell commented on PR #12147:
6 weeks ago
#12
@dmsnell commented on PR #12148:
6 weeks ago
#14
This ticket was mentioned in PR #12155 on WordPress/wordpress-develop by @dmsnell.
6 weeks ago
#15
Trac ticket: Core-65372
⚠️ Not sure if this is a positive fix or not because things get really messy when a site isn’t running UTF-8. For example, if the
blog_charsetis non-UTF-8, then the encoded code points won’t render on the frontend; they will be corrupted. However, if we encode in the configured charset, it’s likely that the code points aren’t represented in that charset, and will be rendered as?instead, which is corrupt. It still makes most sense to run everything internally as UTF-8 and then only re-encode on transit to the browser if the site isn’t running UTF-8. In that case, this is a positive change.
This was brought up during fuzz testing of the HTML API. After polyfilling mb_chr() and relying on it in the HTML decoder, it became possible that for sites with a non-UTF-8 charset selected, then the creation of text from code points when decoding numeric character references might produce corrupted text, or text which encodes to non-UTF-8 bytes.
While for these sites, there are broader issues with non-UTF-8 support, this change ensures that code point encoding remains deterministic.
Follow-up to [62424].
@dmsnell commented on PR #12155:
6 weeks ago
#17
@dmsnell commented on PR #12140:
6 weeks ago
#19
This ticket was mentioned in PR #9657 on WordPress/wordpress-develop by @jonsurrell.
6 weeks ago
#20
</br> gets special treatment in the HTML standard. It creates a BR element with no attributes (regardless of whether it _appears_ to have attributes in the HTML like </br attr-name>).
- Stop on
</br>in::next_tag()like other openers. - Return
array()from::get_attribute_names_with_prefix()on</br>instead ofnulllike true close tags. - Add tests.
To do:
- [ ] Ensure
</br>works with functions like::set_attribute()and::add_class(). These will require rewriting the tag to<br attrs …>or the attributes will be discarded.
Trac ticket: https://core.trac.wordpress.org/ticket/65372
@jonsurrell commented on PR #9657:
6 weeks ago
#21
Moved to trac ticket https://core.trac.wordpress.org/ticket/65372, this has been appearing in fuzz testing. I added a to-do, this likely needs some extra work to handle attribute modification. It will basically need to rewrite the entire token span I think because any attribute-like syntax needs to be removed before rewriting to <br> and only then can attributes be added.
I would like to move this forward when those issues are handled.
This ticket was mentioned in PR #12184 on WordPress/wordpress-develop by @jonsurrell.
5 weeks ago
#22
## Summary
- Reconsume a non-matching byte after
<while scanning script data. - Prevent less-than sequences before the real
</script>closer from making normalization drop the script element. - Add coverage for script less-than parsing and normalization cases.
Trac ticket: https://core.trac.wordpress.org/ticket/65372
## Testing
- Focused script parsing and normalization PHPUnit coverage passes.
- See HTML API debugger text example.
## Use of AI Tools
AI assistance: Yes
Tool(s): Codex
Model(s): GPT-5.5
Used for: PR description cleanup and code review.
@jonsurrell commented on PR #12184:
5 weeks ago
#23
Yes! HTML API fuzz tests caught this.
@jonsurrell commented on PR #12184:
5 weeks ago
#25
Merged in r62509.
This ticket was mentioned in PR #12193 on WordPress/wordpress-develop by @jonsurrell.
5 weeks ago
#26
XMP contents are not decoded and should not use HTML character reference escaping when serializing.
Escaping on serialize does not preserve the contents, so sequential normalizations of the original become:
<xmp> < > & \" ' ␀ </xmp>(original, ␀ isU+0000 NUL)<xmp> < > & \" ' � </xmp><xmp> &lt; &gt; &amp; \&quot; &apos; � </xmp>- etc…
(Note that the NUL -> � replacement is correct).
Note that XMP is an obsolete element, however it is still present in the HTML5 specification.
Trac ticket: https://core.trac.wordpress.org/ticket/65372
## Testing
Compare the HTML API debugger example above on 7.0 with this branch.
## Use of AI Tools
AI assistance: Yes
Tool(s): Claude (Opus 4.8, Fable 5), Codex (GPT-5.5)
Used for: Fuzz test implementation, analysis of defects, fix implementation, review.
@jonsurrell commented on PR #12193:
4 weeks ago
#28
Merged in r62542.
This ticket was mentioned in PR #12286 on WordPress/wordpress-develop by @jonsurrell.
4 weeks ago
#29
Part of decoding HTML named character references in attribute values may involve checking the codepoint immediately following the named character reference:
13.2.5.73 Named character reference state
…
If there is a [named character reference] match
- If the character reference was consumed as part of an attribute, and the last character matched is not a U+003B SEMICOLON character (;), and the next input character is either a U+003D EQUALS SIGN character (=) or an ASCII alphanumeric, then, for historical reasons, flush code points consumed as a character reference and switch to the return state.
The ASCII alphanumeric check was implemented using ctype_alnum(). The behavior of this depends on the host system and the locale. On my system (macOS) it returns true for characters outside of the desired ASCII alphanumeric range.
php -r 'echo ctype_alnum( "\xC2" ) ? "Affected" : "Unaffected";' // Affected
This change compares the following byte with the well-defined ASCII alphanumeric ranges from the HTML specification.
This change also does some minor restructuring of the method to make it align clearly with the specification and to include an early return and avoid the byte comparison in the majority of cases.
Trac ticket: https://core.trac.wordpress.org/ticket/65372
## Use of AI Tools
@jonsurrell commented on PR #12286:
4 weeks ago
#30
I'm trying a revert of the ctype_alnum() change to see if there are any failures on CI.
On my system, I get these failures from one of the new tests:
1) Tests_HtmlApi_WpHtmlDecoder::test_semicolonless_legacy_reference_before_multibyte_attribute_follower with data set #0 ('©¯\_(ツ)_/¯', '©¯\_(ツ)_/¯', '©', 5)
Failed to decode the full attribute value as expected.
Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@
-'©¯\_(ツ)_/¯'
+'©¯\_(ツ)_/¯'
/Users/jonsurrell/a8c/wordpress-develop/trunk/tests/phpunit/tests/html-api/wpHtmlDecoder.php:121
2) Tests_HtmlApi_WpHtmlDecoder::test_semicolonless_legacy_reference_before_multibyte_attribute_follower with data set #1 ('¬ಠ_ಠ', '¬ಠ_ಠ', '¬', 4)
Failed to decode the full attribute value as expected.
Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@
-'¬ಠ_ಠ'
+'¬ಠ_ಠ'
/Users/jonsurrell/a8c/wordpress-develop/trunk/tests/phpunit/tests/html-api/wpHtmlDecoder.php:121
3) Tests_HtmlApi_WpHtmlDecoder::test_semicolonless_legacy_reference_before_multibyte_attribute_follower with data set #2 (' £20', ' £20', ' ', 5)
Failed to decode the full attribute value as expected.
Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@
-' £20'
+' £20'
/Users/jonsurrell/a8c/wordpress-develop/trunk/tests/phpunit/tests/html-api/wpHtmlDecoder.php:121
4) Tests_HtmlApi_WpHtmlDecoder::test_semicolonless_legacy_reference_before_multibyte_attribute_follower with data set #3 (' 🎉', ' 🎉', ' ', 5)
Failed to decode the full attribute value as expected.
Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@
-' 🎉'
+' 🎉'
/Users/jonsurrell/a8c/wordpress-develop/trunk/tests/phpunit/tests/html-api/wpHtmlDecoder.php:121
5) Tests_HtmlApi_WpHtmlDecoder::test_semicolonless_legacy_reference_before_multibyte_attribute_follower with data set #4 ('®™', '®™', '®', 4)
Failed to decode the full attribute value as expected.
Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@
-'®™'
+'®™'
/Users/jonsurrell/a8c/wordpress-develop/trunk/tests/phpunit/tests/html-api/wpHtmlDecoder.php:121
FAILURES!
Tests: 115, Assertions: 331, Failures: 5.
This ticket was mentioned in PR #12289 on WordPress/wordpress-develop by @jonsurrell.
4 weeks ago
#31
IFRAME, NOEMBED, and NOFRAMES contents were omitted from normalized HTML. Print their rawtext contents verbatim according to the standard for serializing HTML fragments.
If current node is a Text node
If the parent of current node is a style, script, xmp, iframe, noembed, noframes, or plaintext element, or if the parent of current node is a noscript element and scripting is enabled for the node, then append the value of current node's data literally.
Otherwise, append the value of current node's data, escaped as described below.
Trac ticket: https://core.trac.wordpress.org/ticket/65372
## Use of AI Tools
AI assistance: Yes
Tool(s): Codex
Model(s): GPT-5.5
Used for: Detection and initial implementation.
This ticket was mentioned in PR #12319 on WordPress/wordpress-develop by @jonsurrell.
4 weeks ago
#32
<g attr=/> is not a self-closing tag. It is a tag with the [attr="/"] attribute.
Trac ticket: https://core.trac.wordpress.org/ticket/65372
## Use of AI Tools
This ticket was mentioned in PR #12322 on WordPress/wordpress-develop by @jonsurrell.
4 weeks ago
#33
HTML PRE, LISTING, and TEXTAREA elements ignore a leading newline. Previous changes ensured those newline were preserved (r61747, r61754), but did not account for non-HTML examples of these elements (for example svg:textarea).
Trac ticket: https://core.trac.wordpress.org/ticket/65372
Follow-up to r61747, r61754
## Use of AI Tools
AI assistance: Yes
Tool(s): Codex GPT-5.5
Used for: Fuzz testing, diagnosis, initial fix implementation, review and feedback.
This ticket was mentioned in PR #12323 on WordPress/wordpress-develop by @jonsurrell.
4 weeks ago
#34
Trac ticket: https://core.trac.wordpress.org/ticket/65372
Follow-up to r61754.
## Use of AI Tools
AI assistance: Yes
Tool(s): Codex GPT-5.5
Used for: Fuzz testing, initial implementation, review and feedback.
@jonsurrell commented on PR #12323:
4 weeks ago
#35
@jonsurrell commented on PR #12286:
3 weeks ago
#37
Committed in r62573.
@jonsurrell commented on PR #12322:
3 weeks ago
#39
Committed in r62574.
This ticket was mentioned in PR #12353 on WordPress/wordpress-develop by @jonsurrell.
3 weeks ago
#40
Ensure foreign content elements do not match HTML elements on the stack of open element checks.
A closing HTML tag could close non-matching foreign content elements producing breadcrumgbs in some documents.
Trac ticket: https://core.trac.wordpress.org/ticket/65372
## Use of AI Tools
AI assistance: Yes
Tool(s): Codex
Model(s): GPT-5.5
Used for: Fuzzing, diagnosis, fix implementation.
@jonsurrell commented on PR #12353:
3 weeks ago
#42
Merged in r62575.
@jonsurrell commented on PR #12319:
3 weeks ago
#43
There’s a case of removing an attribute and leaving an ambiguous trailing
/>that came up in my fuzz-testing
Nice one, I'll see if variant of that is reproducible and add some unit tests.
@jonsurrell commented on PR #12319:
3 weeks ago
#44
I plan to land this and address that on its own. Attribute removal behavior with the self-closing flag are a different issue.
@jonsurrell commented on PR #12319:
3 weeks ago
#46
Merged in r62595.
This ticket was mentioned in PR #12367 on WordPress/wordpress-develop by @jonsurrell.
3 weeks ago
#47
r62595 introduced an test code that asserts the wrong behavior. Fix it.
This fixed a test that is currently failing on trunk.
Trac ticket: https://core.trac.wordpress.org/ticket/65372
Follow-up to r62595.
## Use of AI Tools
@jonsurrell commented on PR #12367:
3 weeks ago
#49
Merged in r62598.
This ticket was mentioned in PR #12368 on WordPress/wordpress-develop by @jonsurrell.
3 weeks ago
#50
Trac ticket: https://core.trac.wordpress.org/ticket/65372
## Use of AI Tools
@jonsurrell commented on PR #12319:
3 weeks ago
#51
There’s a case of removing an attribute and leaving an ambiguous trailing
/>that came up in my fuzz-testing, but I may have fixed it already.
<div a b=3 /b> <div a />something like this
Work-in-progress: https://github.com/WordPress/wordpress-develop/pull/12368
This ticket was mentioned in PR #12379 on WordPress/wordpress-develop by @jonsurrell.
3 weeks ago
#52
Prevent modifying the by-reference variable when WP_HTML_Decoder::read_character_reference() misses a named reference.
Trac ticket: https://core.trac.wordpress.org/ticket/65372
## Use of AI Tools
AI assistance: Yes
Tool(s): Codex
Model(s): GPT-5.5
Used for: Fuzz tests, diagnosis, implementation.
This ticket was mentioned in PR #12380 on WordPress/wordpress-develop by @jonsurrell.
3 weeks ago
#53
Trac ticket: https://core.trac.wordpress.org/ticket/65372
## Use of AI Tools
This ticket was mentioned in PR #12385 on WordPress/wordpress-develop by @jonsurrell.
3 weeks ago
#54
The tag processor defers some processing. It is currently applied inconsistently.
Newline should be normalized (CRLF and CR become LF). This is from the input stream pre-processing phase.
Null bytes are replaced with U+FFFD replacement. This depends on the tokenization rules, but relevant for this PR is that it applies in tag names, attribute names, and attribute values.
Trac ticket: https://core.trac.wordpress.org/ticket/65372
## Use of AI Tools
AI assistance: Yes
Tool(s): Codex
Model(s): GPT-5.5
Used for: Fuzz testing, diagnosis, initial implementation, review.
@jonsurrell commented on PR #12379:
3 weeks ago
#55
Failing tests are currently failing on trunk:
There was 1 failure: 1) WP_Test_REST_Attachments_Controller::test_sideload_animated_video_companions_write_metadata Sideloading animated_video should succeed. Failed asserting that 400 is identical to 200.
@jonsurrell commented on PR #12385:
2 weeks ago
#57
Merged in r62667.
This ticket was mentioned in PR #12466 on WordPress/wordpress-develop by @jonsurrell.
13 days ago
#58
## Summary
- Serialize decoded carriage returns in text, attributes, and RCDATA-style contents as
so normalization reaches a fixed point. - Built on r62667.
## Testing
- Regression coverage for decoded CR handling across text, attributes, RCDATA, tables, and templates.
- HTML API and html5lib PHPUnit groups and PHPCS pass.
Trac ticket: https://core.trac.wordpress.org/ticket/65372
## Use of AI Tools
AI assistance: Yes
Tool(s): Codex
Model(s): GPT-5.5
Used for: PR description cleanup and code review.
@jonsurrell commented on PR #12380:
9 days ago
#60
Merged in r62711.
@jonsurrell commented on PR #12289:
9 days ago
#62
Merged in r62715.
@jonsurrell commented on PR #12466:
9 days ago
#64
Merged in r62716.
This ticket was mentioned in PR #12518 on WordPress/wordpress-develop by @jonsurrell.
9 days ago
#65
Removing an attribute could accidentally change tag semantics. For example, removing attr from <svg><g /attr>ok produced <svg><g />ok, turning the g element into a self-closing tag.
The / is outside the recorded attribute span, so deleting the span exposed it directly before >. This keeps a separating space when removing a slash-adjacent attribute. Duplicate and repeated removals are covered and remain idempotent.
Tests were added first for primary, duplicate, and repeated removals. The Tag Processor and full HTML API test suites pass.
Trac ticket: https://core.trac.wordpress.org/ticket/65372
Alternative to #12368.
## Use of AI Tools
AI assistance: Yes
Tool(s): Codex
Model(s): GPT-5
Used for: Diagnosis, tests, implementation, adversarial review, and verification. All generated changes were reviewed before submission.
@jonsurrell commented on PR #12368:
8 days ago
#66
Closing in favor of https://github.com/WordPress/wordpress-develop/pull/12518.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)
Trac ticket: Core-65372
Fuzz-testing was performed against the HTML API for finding edge cases
that might be broken in the existing parsing code. A few issues were
discovered with HTML normalization and warnings from out-of-bounds
string reads.
This patch contains new tests catching regressions on these behaviors
and adds fixes for the discovered issues.
A special-case for FORM closing tags inside TABLEs was added after
investigation turned up that many normalization issues stem from this
single issue, where
next_tag()would already fail to proceed, but thenormalization was continuing with already-created virtual tokens. This
special-case should be investigated as more support is added to the HTML
API to ensure that it couldn’t be removed for more robust core code.