You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by ad...@apache.org on 2026/08/12 08:29:36 UTC

(wicket) branch master updated: Harden XsltTransformer against XXE in the transformed document (#1537)

This is an automated email from the ASF dual-hosted git repository.

bitstorm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/wicket.git


The following commit(s) were added to refs/heads/master by this push:
     new 348725cf8a Harden XsltTransformer against XXE in the transformed document (#1537)
348725cf8a is described below

commit 348725cf8aebd82b5ab3127d474c965d7eb39851
Author: Nexory <St...@hotmail.de>
AuthorDate: Wed Aug 12 10:29:30 2026 +0200

    Harden XsltTransformer against XXE in the transformed document (#1537)
    
    * Harden XsltTransformer against XXE in the transformed document
    
    XsltTransformer.transform() built its TransformerFactory with a plain
    TransformerFactory.newInstance() and then transformed the component's rendered
    output as the XML source document. That source is attacker-influenceable (it is
    whatever the decorated component rendered, including user model data), so an
    external entity in it was resolved: a crafted document could read local files
    or reach internal URLs.
    
    Enable FEATURE_SECURE_PROCESSING on the factory, mirroring what
    XSLTResourceStream already does; on the JDK this also denies external DTD and
    stylesheet access, so the external entity is rejected instead of resolved.
    Legitimate stylesheet transforms are unaffected (the existing
    OutputTransformerContainer tests still pass).
    
    * Add XXE regression test for XsltTransformer
    
    Confirms that an external general entity in the transformed source document is
    not resolved. Reuses the identity anyName.xsl stylesheet from the
    outputTransformer tests; the test fails (the secret leaks into the output)
    without the FEATURE_SECURE_PROCESSING guard.
---
 .../markup/transformer/XsltTransformerXxeTest.java | 76 ++++++++++++++++++++++
 .../wicket/markup/transformer/XsltTransformer.java |  5 ++
 2 files changed, 81 insertions(+)

diff --git a/wicket-core-tests/src/test/java/org/apache/wicket/markup/transformer/XsltTransformerXxeTest.java b/wicket-core-tests/src/test/java/org/apache/wicket/markup/transformer/XsltTransformerXxeTest.java
new file mode 100644
index 0000000000..bfdd96b307
--- /dev/null
+++ b/wicket-core-tests/src/test/java/org/apache/wicket/markup/transformer/XsltTransformerXxeTest.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.markup.transformer;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+import java.io.File;
+import java.nio.file.Files;
+
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.util.tester.WicketTestCase;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests that {@link XsltTransformer} does not resolve external XML entities in the document it
+ * transforms. The transformed source document is the decorated component's rendered output, which
+ * can contain user data, so it must not be able to read external resources (XXE).
+ */
+class XsltTransformerXxeTest extends WicketTestCase
+{
+	@Test
+	void externalEntityInSourceDocumentIsNotResolved() throws Exception
+	{
+		File secret = File.createTempFile("wicket-xxe", ".txt");
+		String canary = "WICKET-XXE-CANARY-7f55";
+		Files.write(secret.toPath(), canary.getBytes("UTF-8"));
+		try
+		{
+			// The source document (would be the component's render output) carries an external
+			// general entity pointing at the local secret file.
+			String source = "<?xml version='1.0'?>"
+				+ "<!DOCTYPE r [ <!ENTITY xxe SYSTEM '" + secret.toURI() + "'> ]>"
+				+ "<r>&xxe;</r>";
+
+			// A component is only needed so getResourceStream() can resolve style/locale; the
+			// stylesheet is the existing identity-copy anyName.xsl from the outputTransformer tests.
+			Label component = new Label("id", "x");
+			tester.startComponentInPage(component);
+			XsltTransformer transformer = new XsltTransformer(
+				"org/apache/wicket/markup/outputTransformer/anyName.xsl");
+
+			String out;
+			try
+			{
+				out = transformer.transform(component, source).toString();
+			}
+			catch (Exception e)
+			{
+				// The parser is allowed to reject the DOCTYPE/entity outright; what matters is that
+				// the secret is never exposed.
+				out = "";
+			}
+
+			assertFalse(out.contains(canary),
+				"XsltTransformer resolved an external entity (XXE): " + out);
+		}
+		finally
+		{
+			secret.delete();
+		}
+	}
+}
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/transformer/XsltTransformer.java b/wicket-core/src/main/java/org/apache/wicket/markup/transformer/XsltTransformer.java
index 523b2c1cac..bae8cf91cc 100644
--- a/wicket-core/src/main/java/org/apache/wicket/markup/transformer/XsltTransformer.java
+++ b/wicket-core/src/main/java/org/apache/wicket/markup/transformer/XsltTransformer.java
@@ -20,6 +20,7 @@ import java.io.FileNotFoundException;
 import java.io.StringReader;
 import java.io.StringWriter;
 
+import javax.xml.XMLConstants;
 import javax.xml.transform.Transformer;
 import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.stream.StreamResult;
@@ -98,6 +99,10 @@ public class XsltTransformer implements ITransformer
 		{
 			// 1. Instantiate a TransformerFactory.
 			TransformerFactory tFactory = TransformerFactory.newInstance();
+			// Harden against XXE: the XML source transformed below is the component's
+			// rendered output and may embed user data, so disable external entity, DTD
+			// and stylesheet resolution (as XSLTResourceStream already does).
+			tFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
 
 			// 2. Use the TransformerFactory to process the stylesheet Source
 			// and