This is an automated email from the ASF dual-hosted git repository. papegaaij pushed a commit to branch wicket-8.x in repository https://gitbox.apache.org/repos/asf/wicket.git
commit 7f53ba4dff2f0041cc3c433c4b96cc4c4cfdf2e3 Author: Emond Papegaaij <[email protected]> AuthorDate: Wed Aug 19 22:24:56 2026 +0200 Escape the attribute values of a MetaDataHeaderItem as markup generateString escaped attribute names with Strings.escapeMarkup but ran the values through Strings.replaceAll(value, "\"", "\\\""). A backslash before a double quote means nothing in HTML, so a value holding a double quote ended its own attribute and whatever followed was read as further attributes. Values may come from an IModel, so they are not necessarily literals. WICKET-6178 set out to escape these values and this is what it left behind. They now go through Strings.escapeMarkup, like the names beside them and like every attribute ComponentTag writes. Two tests asserted the old output. testEscapeOnlyDoubleQuotes was named after the behaviour that was the problem and is renamed. HtmlImportHeaderItemTest expects an escaped ampersand in the href of a link tag, which is what a url holding two parameters has to carry, and is what ComponentTag writes for every other attribute. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> --- .../wicket/markup/head/MetaDataHeaderItem.java | 2 +- .../markup/head/HtmlImportHeaderItemTest.java | 7 +++-- .../wicket/markup/head/MetaDataHeaderItemTest.java | 35 ++++++++++++++++++++-- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/head/MetaDataHeaderItem.java b/wicket-core/src/main/java/org/apache/wicket/markup/head/MetaDataHeaderItem.java index 77fce4bea1..09d1d8c444 100644 --- a/wicket-core/src/main/java/org/apache/wicket/markup/head/MetaDataHeaderItem.java +++ b/wicket-core/src/main/java/org/apache/wicket/markup/head/MetaDataHeaderItem.java @@ -131,7 +131,7 @@ public class MetaDataHeaderItem extends HeaderItem { buffer.append('=') .append('"') - .append(Strings.replaceAll(value.toString(), "\"", "\\\"")) + .append(Strings.escapeMarkup(value.toString())) .append('"'); } } diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/head/HtmlImportHeaderItemTest.java b/wicket-core/src/test/java/org/apache/wicket/markup/head/HtmlImportHeaderItemTest.java index 79244b6f5e..b629da4c84 100644 --- a/wicket-core/src/test/java/org/apache/wicket/markup/head/HtmlImportHeaderItemTest.java +++ b/wicket-core/src/test/java/org/apache/wicket/markup/head/HtmlImportHeaderItemTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals; import org.apache.wicket.markup.html.basic.SimplePage; import org.apache.wicket.request.mapper.parameter.PageParameters; +import org.apache.wicket.util.string.Strings; import org.apache.wicket.util.tester.WicketTester; import org.junit.Test; @@ -53,7 +54,9 @@ public class HtmlImportHeaderItemTest MetaDataHeaderItem importLink = HtmlImportHeaderItem .forImportLinkTag(SimplePage.class, parameters, "monitor", true); - assertEquals("<link rel=\"import\" href=\"" + pageUrl + "\" media=\"monitor\" async />\n", - importLink.generateString()); + // the href is escaped as markup, like every other attribute Wicket writes. The url holds + // a literal & between its two parameters, which has to be written as an entity + assertEquals("<link rel=\"import\" href=\"" + Strings.escapeMarkup(pageUrl) + + "\" media=\"monitor\" async />\n", importLink.generateString()); } } diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/head/MetaDataHeaderItemTest.java b/wicket-core/src/test/java/org/apache/wicket/markup/head/MetaDataHeaderItemTest.java index 6cff0a5ab6..c8b5ea1699 100644 --- a/wicket-core/src/test/java/org/apache/wicket/markup/head/MetaDataHeaderItemTest.java +++ b/wicket-core/src/test/java/org/apache/wicket/markup/head/MetaDataHeaderItemTest.java @@ -43,12 +43,43 @@ public class MetaDataHeaderItemTest assertEquals(expectedString, metaTag.generateString()); } + /** + * Attribute values are escaped as markup. A backslash before a double quote means nothing in + * HTML, so escaping that way left the value able to close its own attribute. + */ @Test - public void testEscapeOnlyDoubleQuotes() throws Exception + public void testEscapesAttributeValuesAsMarkup() throws Exception { - String expectedString = "<link rel=\"single quote \' double quotes\\\"\" href=\"\" />\n"; + String expectedString = "<link rel=\"single quote ' double quotes"\" href=\"\" />\n"; MetaDataHeaderItem metaTag = MetaDataHeaderItem.forLinkTag("single quote \' double quotes\"", ""); assertEquals(expectedString, metaTag.generateString()); } + + /** + * A value cannot end its attribute and add another one. + */ + @Test + public void testAttributeValueCannotInjectAnotherAttribute() throws Exception + { + MetaDataHeaderItem metaTag = MetaDataHeaderItem.forMetaTag("description", + "x\" onload=\"x=1"); + + assertEquals("<meta name=\"description\" content=\"x" onload="x=1\" />\n", + metaTag.generateString()); + } + + /** + * The same for a value taken from a model. + */ + @Test + public void testAttributeValueFromModelIsEscaped() throws Exception + { + MetaDataHeaderItem metaTag = MetaDataHeaderItem.forLinkTag("stylesheet", ""); + metaTag.addTagAttribute("title", Model.of("<script>x=1</script>")); + + assertEquals( + "<link rel=\"stylesheet\" href=\"\" title=\"<script>x=1</script>\" />\n", + metaTag.generateString()); + } }
