fix(engine): never let document text close the generated HTML style element (#1955)

This commit is contained in:
Dylan M. Taylor authored and GitHub committed 2026-11-03 22:23:03 -05:00
1 parent 88851996a0
commit df07ac2d09
2 files changed
+17 -1

No files matched your search

+4 -1
View File
@@ -90,7 +90,10 @@ pub(super) fn html(doc: &Document, id: NodeId) -> String {
let ctx = Ctx::new(doc, id);
let mut out = Markup::new(&ctx, CodeLang::Html);
out.node(id, None, 0, "");
format!("{}\n<style>\n{}{}</style>\n", out.markup, out.tokens.root_rule(), out.styles)
// Document text can reach the stylesheet through any property: `<\/` reads back as `</` inside a CSS
// string and can never close the element early.
let css = format!("{}{}", out.tokens.root_rule(), out.styles).replace("</", "<\\/");
format!("{}\n<style>\n{css}</style>\n", out.markup)
}
/// Markup being built for a layer tree: HTML (`class`), React JSX (`className`), Vue
+13
View File
@@ -163,3 +163,16 @@ fn rem_preserves_text_property_default() {
}
}
}
#[test]
fn the_stylesheet_never_contains_a_closing_style_tag_from_document_text() {
let mut s = Session::new();
let f = id(s.execute("frame.create", &json!({"x": 0, "y": 0, "width": 100, "height": 100})).unwrap());
let t = id(s.execute("text.create", &json!({"text": "x", "x": 0, "y": 0})).unwrap());
s.execute("node.set", &json!({"id": t, "props": {"fontFamily": "a</style><script>alert(1)</script>"}})).unwrap();
s.execute("node.reparent", &json!({"ids": [t], "parent": f})).unwrap();
let code = code_for(s.doc().unwrap(), f, CodeLang::Html);
let (_, css) = code.split_once("<style>").unwrap();
assert_eq!(css.matches("</style>").count(), 1, "{code}");
assert!(!code.contains("<script>"), "{code}");
}