Skip to content

fix(html): reject unsupported children of text tags - #254

Merged
LeadcodeDev merged 1 commit into
chantier/audit-2026-09from
fix/html-text-children
Sep 22, 2026
Merged

LeadcodeDev merged 1 commit into
chantier/audit-2026-09from
fix/html-text-children

Conversation

@LeadcodeDev

Copy link
Copy Markdown
Owner

Severity Medium, category correctness. Location: crates/rustmotion-html/src/element.rs:95

Impact

The TagKind::Text arm never calls element_to_value on its children, so the whole guard layer (StyleElementUnsupported, UnsupportedNativeElement, TagKind::Ignored) is unreachable below p/span/h1..h6/strong/em/label. Executed against the real crate: <p>Real <script>var secret = 1; alert(2);</script></p> transpiles to {"content":"Real var secret = 1; alert(2);","type":"text"} — the JS source is rendered on screen; <h1>Title<style>h1{color:#0f0}</style></h1> gives {"content":"Titleh1{color:#0f0}"}; <p>cap<img src="hero.png"></p>, <h1>t<svg>…</svg></h1> and <span>n=<rm-counter from="0" to="100"></rm-counter></span> drop the img/svg/counter entirely and exit 0. This is a live regression of "constat 1" and "constat 3", which tests/silent_loss_regressions.rs asserts are fixed — the fix only ever covered the Container path.

Fix

Make the Text arm walk children through element_to_value (or at minimum run a pre-pass over the subtree that raises StyleElementUnsupported / UnsupportedNativeElement / refuses any non-Text element child) instead of flattening with inner_text. inner_text should also skip TagKind::Ignored and style subtrees. Add the nested variants of the existing regression tests: <p><script>, <h1><style>, <p><img>, <span><rm-counter>.

Evidence the audit read

TagKind::Text => {
            let mut obj = Map::new();
            obj.insert("type".into(), Value::from("text"));
            obj.insert("content".into(), Value::from(inner_text(handle)));

// and inner_text's collector (element.rs:45-52) filters no tag at all:
fn collect_text(handle: &Handle, out: &mut String) {
    if let NodeData::Text { contents } = &handle.data {
        out.push_str(&contents.borrow());
    }
    for child in handle.children.borrow().iter() {
        collect_text(child, out);
    }
}

Stacked on fix/html-shorthand-lengths, which carries the previous finding of this workstream. GitHub shows only this finding's diff; merge in order.

Part of the September 2026 audit remediation chantier. Refs #220 (RM-11).

@LeadcodeDev LeadcodeDev added the bug Something isn't working label Sep 21, 2026
@LeadcodeDev LeadcodeDev self-assigned this Sep 21, 2026
@LeadcodeDev
LeadcodeDev force-pushed the fix/html-shorthand-lengths branch from 342377a to c0d2efd Compare September 22, 2026 06:11
@LeadcodeDev
LeadcodeDev force-pushed the fix/html-text-children branch from 8eb5453 to 7fced5c Compare September 22, 2026 06:11
@LeadcodeDev
LeadcodeDev force-pushed the fix/html-shorthand-lengths branch from c0d2efd to f36abbf Compare September 22, 2026 08:35
@LeadcodeDev
LeadcodeDev force-pushed the fix/html-text-children branch from 7fced5c to 5b45306 Compare September 22, 2026 08:35
@LeadcodeDev
LeadcodeDev force-pushed the fix/html-shorthand-lengths branch from f36abbf to 895d8e3 Compare September 22, 2026 08:45
@LeadcodeDev
LeadcodeDev force-pushed the fix/html-text-children branch from 5b45306 to 6717eb1 Compare September 22, 2026 08:45
@LeadcodeDev
LeadcodeDev changed the base branch from fix/html-shorthand-lengths to chantier/audit-2026-09 September 22, 2026 08:53
@LeadcodeDev
LeadcodeDev force-pushed the fix/html-text-children branch from 6717eb1 to cc9f247 Compare September 22, 2026 09:01
The `TagKind::Text` arm never calls `element_to_value` on its children, so the whole guard layer (`StyleElementUnsupported`, `UnsupportedNativeElement`, `TagKind::Ignored`) is unreachable below `p/span/h1..h6/strong/em/label`. Executed against the real crate: `<p>Real <script>var secret = 1; alert(2);</script></p>` transpiles to `{"content":"Real var secret = 1; alert(2);","type":"text"}` — the JS source is rendered on screen; `<h1>Title<style>h1{color:#0f0}</style></h1>` gives `{"content":"Titleh1{color:#0f0}"}`; `<p>cap<img src="hero.png"></p>`, `<h1>t<svg>…</svg></h1>` and `<span>n=<rm-counter from="0" to="100"></rm-counter></span>` drop the img/svg/counter entirely and exit 0. This is a live regression of "constat 1" and "constat 3", which tests/silent_loss_regressions.rs asserts are fixed — the fix only ever covered the `Container` path.

Refs #220
@LeadcodeDev
LeadcodeDev force-pushed the fix/html-text-children branch from cc9f247 to 36800b4 Compare September 22, 2026 09:06
@LeadcodeDev
LeadcodeDev merged commit 3a49519 into chantier/audit-2026-09 Sep 22, 2026
LeadcodeDev added a commit that referenced this pull request Sep 22, 2026
The `TagKind::Text` arm never calls `element_to_value` on its children, so the whole guard layer (`StyleElementUnsupported`, `UnsupportedNativeElement`, `TagKind::Ignored`) is unreachable below `p/span/h1..h6/strong/em/label`. Executed against the real crate: `<p>Real <script>var secret = 1; alert(2);</script></p>` transpiles to `{"content":"Real var secret = 1; alert(2);","type":"text"}` — the JS source is rendered on screen; `<h1>Title<style>h1{color:#0f0}</style></h1>` gives `{"content":"Titleh1{color:#0f0}"}`; `<p>cap<img src="hero.png"></p>`, `<h1>t<svg>…</svg></h1>` and `<span>n=<rm-counter from="0" to="100"></rm-counter></span>` drop the img/svg/counter entirely and exit 0. This is a live regression of "constat 1" and "constat 3", which tests/silent_loss_regressions.rs asserts are fixed — the fix only ever covered the `Container` path.

Refs #220
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant