Skip to content

fix: reject raw '<' and stray '&' in attribute values - #913

Open
jeromeyyy wants to merge 2 commits into
NaturalIntelligence:masterfrom
jeromeyyy:fix-validator-attvalue-literals
Open

jeromeyyy wants to merge 2 commits into
NaturalIntelligence:masterfrom
jeromeyyy:fix-validator-attvalue-literals

Conversation

@jeromeyyy

Copy link
Copy Markdown

Purpose / Goal

Fixes #900.

XML 1.0's AttValue grammar excludes a literal < character in attribute values, and every literal & must begin a reference. XMLValidator accepted both: <a x="a & b"/> and <a x="a < b"/> returned true, and parsing with validation enabled accepted them too. (Escaped forms such as &lt;, &amp; and &#60; are unaffected and stay valid, before and after.)

validateAttributeString now also checks the extracted attribute value:

  • a literal < character is rejected;
  • a literal & that is not followed by a reference start (# or a name start character) is rejected.

Scope of the ampersand check

This is intentionally a start-of-reference check, not full XML reference validation. The parser keeps unknown and malformed references literal instead of rejecting them - verified on master: &#xZZ;, &#;, &unknown; and the HTML-legacy &ampbar all parse to their literal text, and existing entity/value-processor specs depend on that contract (3 specs fail if references are validated strictly). So name-like runs after &, including malformed numeric references, stay accepted here, consistent with the parser. Resolving or strictly validating references would be a separate behavior decision.

Input (from the issue):

<a x="a & b"/>
<a x="a < b"/>
<a x="a &amp; b"/>

Before: all three validate as true. After: the first two return InvalidAttr ("value contains '&' that doesn't start a reference" / "value can't contain '<'"), the escaped &amp; control stays valid, and parse(xml, true) rejects the malformed inputs.

Type

  • Bug Fix

Tests

  • 5 new specs in spec/validator_spec.js: 3 negative cases (literal & not starting a reference, literal <, & not followed by a name), one control spec pinning that escaped references (&amp;, &lt;, &#65;, &#x41;) stay valid, and one control spec pinning the intentional HTML-legacy &ampbar leniency.
  • The 3 negative specs fail on current master and pass with this change; full suite npm test: 345 specs, 0 failures (2 pending, pre-existing).
  • Perf: node benchmark/XmlParser.mjs before/after this change: 46,443 vs 45,424 requests/second for fxp, within run-to-run noise (fxp v3 moved the same direction between the same two runs); the validator is not on this benchmark's parse path.
  • Lint: no new findings; the single no-extra-semi report at src/validator.js:191 exists on master.

@cocosend

cocosend commented Nov 5, 2026

Copy link
Copy Markdown

Purpose / Goal

Fixes #900.

XML 1.0's AttValue grammar excludes a literal < character in attribute values, and every literal & must begin a reference. XMLValidator accepted both: <a x="a & b"/> and <a x="a < b"/> returned true, and parsing with validation enabled accepted them too. (Escaped forms such as &lt;, &amp; and &#60; are unaffected and stay valid, before and after.)

validateAttributeString now also checks the extracted attribute value:

  • a literal < character is rejected;
  • a literal & that is not followed by a reference start (# or a name start character) is rejected.

Scope of the ampersand check

This is intentionally a start-of-reference check, not full XML reference validation. The parser keeps unknown and malformed references literal instead of rejecting them - verified on master: &#xZZ;, &#;, &unknown; and the HTML-legacy &ampbar all parse to their literal text, and existing entity/value-processor specs depend on that contract (3 specs fail if references are validated strictly). So name-like runs after &, including malformed numeric references, stay accepted here, consistent with the parser. Resolving or strictly validating references would be a separate behavior decision.

Input (from the issue):

<a x="a & b"/>
<a x="a < b"/>
<a x="a &amp; b"/>

Before: all three validate as true. After: the first two return InvalidAttr ("value contains '&' that doesn't start a reference" / "value can't contain '<'"), the escaped &amp; control stays valid, and parse(xml, true) rejects the malformed inputs.

Type

  • Bug Fix

Tests

  • 5 new specs in spec/validator_spec.js: 3 negative cases (literal & not starting a reference, literal <, & not followed by a name), one control spec pinning that escaped references (&amp;, &lt;, &#65;, &#x41;) stay valid, and one control spec pinning the intentional HTML-legacy &ampbar leniency.
  • The 3 negative specs fail on current master and pass with this change; full suite npm test: 345 specs, 0 failures (2 pending, pre-existing).
  • Perf: node benchmark/XmlParser.mjs before/after this change: 46,443 vs 45,424 requests/second for fxp, within run-to-run noise (fxp v3 moved the same direction between the same two runs); the validator is not on this benchmark's parse path.
  • Lint: no new findings; the single no-extra-semi report at src/validator.js:191 exists on master.

@cocosend

cocosend commented Nov 5, 2026

Copy link
Copy Markdown

Purpose / Goal

Fixes #900.

XML 1.0's AttValue grammar excludes a literal < character in attribute values, and every literal & must begin a reference. XMLValidator accepted both: <a x="a & b"/> and <a x="a < b"/> returned true, and parsing with validation enabled accepted them too. (Escaped forms such as &lt;, &amp; and &#60; are unaffected and stay valid, before and after.)

validateAttributeString now also checks the extracted attribute value:

  • a literal < character is rejected;
  • a literal & that is not followed by a reference start (# or a name start character) is rejected.

Scope of the ampersand check

This is intentionally a start-of-reference check, not full XML reference validation. The parser keeps unknown and malformed references literal instead of rejecting them - verified on master: &#xZZ;, &#;, &unknown; and the HTML-legacy &ampbar all parse to their literal text, and existing entity/value-processor specs depend on that contract (3 specs fail if references are validated strictly). So name-like runs after &, including malformed numeric references, stay accepted here, consistent with the parser. Resolving or strictly validating references would be a separate behavior decision.

Input (from the issue):

<a x="a & b"/>
<a x="a < b"/>
<a x="a &amp; b"/>

Before: all three validate as true. After: the first two return InvalidAttr ("value contains '&' that doesn't start a reference" / "value can't contain '<'"), the escaped &amp; control stays valid, and parse(xml, true) rejects the malformed inputs.

Type

  • Bug Fix

Tests

  • 5 new specs in spec/validator_spec.js: 3 negative cases (literal & not starting a reference, literal <, & not followed by a name), one control spec pinning that escaped references (&amp;, &lt;, &#65;, &#x41;) stay valid, and one control spec pinning the intentional HTML-legacy &ampbar leniency.
  • The 3 negative specs fail on current master and pass with this change; full suite npm test: 345 specs, 0 failures (2 pending, pre-existing).
  • Perf: node benchmark/XmlParser.mjs before/after this change: 46,443 vs 45,424 requests/second for fxp, within run-to-run noise (fxp v3 moved the same direction between the same two runs); the validator is not on this benchmark's parse path.
  • Lint: no new findings; the single no-extra-semi report at src/validator.js:191 exists on master.

@cocosend

cocosend commented Nov 5, 2026

Copy link
Copy Markdown

Purpose / Goal

Fixes #900.

XML 1.0's AttValue grammar excludes a literal < character in attribute values, and every literal & must begin a reference. XMLValidator accepted both: <a x="a & b"/> and <a x="a < b"/> returned true, and parsing with validation enabled accepted them too. (Escaped forms such as &lt;, &amp; and &#60; are unaffected and stay valid, before and after.)

validateAttributeString now also checks the extracted attribute value:

  • a literal < character is rejected;
  • a literal & that is not followed by a reference start (# or a name start character) is rejected.

Scope of the ampersand check

This is intentionally a start-of-reference check, not full XML reference validation. The parser keeps unknown and malformed references literal instead of rejecting them - verified on master: &#xZZ;, &#;, &unknown; and the HTML-legacy &ampbar all parse to their literal text, and existing entity/value-processor specs depend on that contract (3 specs fail if references are validated strictly). So name-like runs after &, including malformed numeric references, stay accepted here, consistent with the parser. Resolving or strictly validating references would be a separate behavior decision.

Input (from the issue):

<a x="a & b"/>
<a x="a < b"/>
<a x="a &amp; b"/>

Before: all three validate as true. After: the first two return InvalidAttr ("value contains '&' that doesn't start a reference" / "value can't contain '<'"), the escaped &amp; control stays valid, and parse(xml, true) rejects the malformed inputs.

Type

  • Bug Fix

Tests

  • 5 new specs in spec/validator_spec.js: 3 negative cases (literal & not starting a reference, literal <, & not followed by a name), one control spec pinning that escaped references (&amp;, &lt;, &#65;, &#x41;) stay valid, and one control spec pinning the intentional HTML-legacy &ampbar leniency.
  • The 3 negative specs fail on current master and pass with this change; full suite npm test: 345 specs, 0 failures (2 pending, pre-existing).
  • Perf: node benchmark/XmlParser.mjs before/after this change: 46,443 vs 45,424 requests/second for fxp, within run-to-run noise (fxp v3 moved the same direction between the same two runs); the validator is not on this benchmark's parse path.
  • Lint: no new findings; the single no-extra-semi report at src/validator.js:191 exists on master.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

XMLValidator accepts raw < and & in attribute values

2 participants