From a75248f34b0be4c78c470d341d0c80b6524c4750 Mon Sep 17 00:00:00 2001 From: Goutam Adwant <8672451+goutamadwant@users.noreply.github.com> Date: Sat, 12 Sep 2026 12:13:55 -0700 Subject: [PATCH] Preserve text gates in renamed literal elements --- change_log.md | 4 ++++ ...ndAttributePolicyBasedSanitizerPolicy.java | 20 +++++++++++++++++++ .../owasp/html/HtmlChangeReporterTest.java | 7 ++++--- .../org/owasp/html/HtmlPolicyBuilderTest.java | 14 +++++++++++++ 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/change_log.md b/change_log.md index 9f8970f1..7aa027da 100644 --- a/change_log.md +++ b/change_log.md @@ -2,6 +2,10 @@ Most recent at top. * Next release + * A nested element inside one renamed to a literal-content element no + longer opens its own text gate. Its text now follows the outer element's + gate, so it cannot reach `style` or similar content where text was not + allowed (#482). * Docs: Add focused README files for each Maven module and an index for supporting documentation, including safe build and regeneration steps. * Table parts inside cell content now return to the existing table, and a diff --git a/owasp-java-html-sanitizer/src/main/java/org/owasp/html/ElementAndAttributePolicyBasedSanitizerPolicy.java b/owasp-java-html-sanitizer/src/main/java/org/owasp/html/ElementAndAttributePolicyBasedSanitizerPolicy.java index 7322263f..fd3238c2 100644 --- a/owasp-java-html-sanitizer/src/main/java/org/owasp/html/ElementAndAttributePolicyBasedSanitizerPolicy.java +++ b/owasp-java-html-sanitizer/src/main/java/org/owasp/html/ElementAndAttributePolicyBasedSanitizerPolicy.java @@ -85,6 +85,12 @@ class ElementAndAttributePolicyBasedSanitizerPolicy * without counting them toward its nesting limit. */ transient boolean skipText = true; + /** + * True while an emitted element whose content the renderer writes literally + * is open. A nested start tag cannot be emitted in that context, so it must + * be treated as dropped before it can become a text container in the policy. + */ + private boolean inKeptLiteralElement; /** * True while a kept element whose text the renderer emits unescaped, such * as {@code ", result.html); assertEquals(" ", result.log); diff --git a/owasp-java-html-sanitizer/src/test/java/org/owasp/html/HtmlPolicyBuilderTest.java b/owasp-java-html-sanitizer/src/test/java/org/owasp/html/HtmlPolicyBuilderTest.java index 5efa2281..3100a812 100644 --- a/owasp-java-html-sanitizer/src/test/java/org/owasp/html/HtmlPolicyBuilderTest.java +++ b/owasp-java-html-sanitizer/src/test/java/org/owasp/html/HtmlPolicyBuilderTest.java @@ -2059,6 +2059,20 @@ void testRenameIntoALiteralContentElementStillNeedsAllowTextInOnTheTarget() { apply(divToStyle.allowTextIn("style"), "
a{b:c}
")); } + /** A nested kept element does not override a literal element's text gate. */ + @Test + void testNestedElementInsideRenamedLiteralContentUsesOuterTextGate() { + HtmlPolicyBuilder divToStyle = new HtmlPolicyBuilder() + .allowElements((name, attrs) -> "style", "div") + .allowElements("b", "style"); + String html = "
aboldc
after"; + + assertEquals("after", apply(divToStyle, html)); + assertEquals( + "after", + apply(divToStyle.allowTextIn("style"), html)); + } + /** * A void element renamed to one that is not void is closed at once (#450). * The lexer never produces a close tag for {@code br}, and the balancer,