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 <[email protected]>
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

Reply via email to