This is an automated email from the ASF dual-hosted git repository. ppkarwasz pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/commons-xml.git
commit 4522e06c66fcd5e2a48c3e15868a9bc9c426c199 Author: Piotr P. Karwasz <[email protected]> AuthorDate: Sat Apr 25 14:46:10 2026 +0200 fix: make ExternalStylesheetTest more reliable --- .../xml/factory/attacks/AttackTestSupport.java | 10 --- .../factory/attacks/ExternalStylesheetTest.java | 77 ++++++++++++++-------- .../resources/ExternalStylesheetTest/included.xsl | 6 ++ .../ExternalStylesheetTest/referenced.xml | 3 + .../ExternalStylesheetTest/with-document.xsl | 6 ++ .../ExternalStylesheetTest/with-import.xsl | 4 ++ .../ExternalStylesheetTest/with-include.xsl | 4 ++ 7 files changed, 71 insertions(+), 39 deletions(-) diff --git a/src/test/java/org/apache/commons/xml/factory/attacks/AttackTestSupport.java b/src/test/java/org/apache/commons/xml/factory/attacks/AttackTestSupport.java index 86f22a6..fa965bc 100644 --- a/src/test/java/org/apache/commons/xml/factory/attacks/AttackTestSupport.java +++ b/src/test/java/org/apache/commons/xml/factory/attacks/AttackTestSupport.java @@ -172,16 +172,6 @@ static void assertTransformerBlocks(final String payload) { assertAttackBlocked(() -> XmlFactories.newTransformerFactory().newTransformer().transform(source(payload), new StreamResult(new StringWriter()))); } - /** - * Asserts that compiling the given stylesheet and then transforming the given input through it is blocked by the hardening layer. Used for attacks whose - * external fetch is triggered at {@code Transformer.transform(...)} time rather than compile time (for example {@code document()} in an XSLT template). - */ - static void assertTransformerBlocksWithStylesheet(final String xslt, final String input) { - assertAttackBlocked(() -> XmlFactories.newTransformerFactory() - .newTransformer(source(xslt)) - .transform(source(input), new StreamResult(new StringWriter()))); - } - /** * Asserts that validating the given XML instance through a {@link javax.xml.validation.Validator} obtained from {@link Payloads#BENIGN_SCHEMA} is blocked * by the hardening layer. The schema itself is trusted and benign; the attack lives in the instance document. diff --git a/src/test/java/org/apache/commons/xml/factory/attacks/ExternalStylesheetTest.java b/src/test/java/org/apache/commons/xml/factory/attacks/ExternalStylesheetTest.java index f35842e..7815a04 100644 --- a/src/test/java/org/apache/commons/xml/factory/attacks/ExternalStylesheetTest.java +++ b/src/test/java/org/apache/commons/xml/factory/attacks/ExternalStylesheetTest.java @@ -16,51 +16,70 @@ */ package org.apache.commons.xml.factory.attacks; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.io.StringReader; +import java.io.StringWriter; +import java.net.URL; + +import javax.xml.transform.Templates; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.apache.commons.xml.factory.XmlFactories; import org.junit.jupiter.api.Test; /** - * Exercises {@link javax.xml.XMLConstants#ACCESS_EXTERNAL_STYLESHEET ACCESS_EXTERNAL_STYLESHEET} via the three XSLT vectors it guards: - * {@code xsl:include} and {@code xsl:import} at compile time, and the {@code document()} function at transform time. + * Checks whether stylesheets can access external resources. + * + * <p>Each fixture under {@code src/test/resources/ExternalStylesheetTest/} references a sibling file that, if hardening fails to block, would emit the + * {@link #MARKER} string into the transform output. The assertion is deterministic: hardening either throws, or the call returns and the marker does not + * appear in the output.</p> + * + * <p>Cases covered:</p> + * + * <ul> + * <li>{@code xsl:include} of a sibling stylesheet, resolved at compile time.</li> + * <li>{@code xsl:import} of a sibling stylesheet, resolved at compile time.</li> + * <li>{@code document()} call referencing a sibling XML file, resolved at transform time.</li> + * </ul> */ class ExternalStylesheetTest { private static final String INPUT = "<root/>"; - private static String xsltWithDocumentCall(final String uri) { - return "<?xml version=\"1.0\"?>\n" - + "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n" - + " <xsl:template match=\"/\">\n" - + " <xsl:copy-of select=\"document('" + uri + "')\"/>\n" - + " </xsl:template>\n" - + "</xsl:stylesheet>\n"; - } - - private static String xsltWithImport(final String href) { - return "<?xml version=\"1.0\"?>\n" - + "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n" - + " <xsl:import href=\"" + href + "\"/>\n" - + "</xsl:stylesheet>\n"; - } - - private static String xsltWithInclude(final String href) { - return "<?xml version=\"1.0\"?>\n" - + "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n" - + " <xsl:include href=\"" + href + "\"/>\n" - + "</xsl:stylesheet>\n"; - } + private static final String MARKER = "All your base are belong to us"; @Test - void transformerBlocksDocument() { - AttackTestSupport.assertTransformerBlocksWithStylesheet(xsltWithDocumentCall(Payloads.UNREACHABLE_HTTP), INPUT); + void transformerStylesheetBlocksInclude() { + assertStylesheetExcludesMarker("with-include.xsl"); } @Test void transformerStylesheetBlocksImport() { - AttackTestSupport.assertStylesheetCompilationBlocks(xsltWithImport(Payloads.UNREACHABLE_HTTP)); + assertStylesheetExcludesMarker("with-import.xsl"); } @Test - void transformerStylesheetBlocksInclude() { - AttackTestSupport.assertStylesheetCompilationBlocks(xsltWithInclude(Payloads.UNREACHABLE_HTTP)); + void transformerBlocksDocument() { + assertStylesheetExcludesMarker("with-document.xsl"); + } + + private static void assertStylesheetExcludesMarker(final String resource) { + final URL url = ExternalStylesheetTest.class.getResource("/ExternalStylesheetTest/" + resource); + assertNotNull(url, "test resource not found: " + resource); + final String output; + try { + final StreamSource src = new StreamSource(url.openStream(), url.toString()); + final Templates templates = XmlFactories.newTransformerFactory().newTemplates(src); + final StringWriter sink = new StringWriter(); + templates.newTransformer().transform(new StreamSource(new StringReader(INPUT)), new StreamResult(sink)); + output = sink.toString(); + } catch (final Throwable t) { + return; // hardening blocked at compile or transform; acceptable outcome. + } + assertFalse(output.contains(MARKER), + "Hardening did not block the external reference; output contained marker '" + MARKER + "'.\nFull output:\n" + output); } } diff --git a/src/test/resources/ExternalStylesheetTest/included.xsl b/src/test/resources/ExternalStylesheetTest/included.xsl new file mode 100644 index 0000000..706c8a1 --- /dev/null +++ b/src/test/resources/ExternalStylesheetTest/included.xsl @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + <xsl:template match="/"> + <leaked>All your base are belong to us</leaked> + </xsl:template> +</xsl:stylesheet> diff --git a/src/test/resources/ExternalStylesheetTest/referenced.xml b/src/test/resources/ExternalStylesheetTest/referenced.xml new file mode 100644 index 0000000..dc114bb --- /dev/null +++ b/src/test/resources/ExternalStylesheetTest/referenced.xml @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- SPDX-License-Identifier: Apache-2.0 --> +<leaked>All your base are belong to us</leaked> diff --git a/src/test/resources/ExternalStylesheetTest/with-document.xsl b/src/test/resources/ExternalStylesheetTest/with-document.xsl new file mode 100644 index 0000000..b2bb88a --- /dev/null +++ b/src/test/resources/ExternalStylesheetTest/with-document.xsl @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + <xsl:template match="/"> + <xsl:copy-of select="document('referenced.xml')"/> + </xsl:template> +</xsl:stylesheet> diff --git a/src/test/resources/ExternalStylesheetTest/with-import.xsl b/src/test/resources/ExternalStylesheetTest/with-import.xsl new file mode 100644 index 0000000..014975a --- /dev/null +++ b/src/test/resources/ExternalStylesheetTest/with-import.xsl @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + <xsl:import href="included.xsl"/> +</xsl:stylesheet> diff --git a/src/test/resources/ExternalStylesheetTest/with-include.xsl b/src/test/resources/ExternalStylesheetTest/with-include.xsl new file mode 100644 index 0000000..a656025 --- /dev/null +++ b/src/test/resources/ExternalStylesheetTest/with-include.xsl @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + <xsl:include href="included.xsl"/> +</xsl:stylesheet>
