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

ppkarwasz pushed a commit to branch feat/use-commons-xml
in repository https://gitbox.apache.org/repos/asf/commons-bsf.git

commit c2d880698f1dd7f0d11eee57853b8ae1901bcce4
Author: Piotr P. Karwasz <[email protected]>
AuthorDate: Mon Aug 31 15:23:07 2026 +0200

    Harden XSLT processing via commons-secure-xml
    
    Create the XSLT engine's transformer factory through
    org.apache.commons:commons-secure-xml. The secure factory enables
    FEATURE_SECURE_PROCESSING on every transformer it produces and bounds
    internal entity expansion, regardless of the JAXP implementation on the
    classpath.
    
    Changes:
    - Add the commons-secure-xml dependency (1.0.0-SNAPSHOT until its first
      release).
    - Route factory creation through SecureTransformerFactory in XSLTEngine.
    - Keep the documented "xslt:styleBaseURI" contract working: stylesheets
      may import, include and reference documents relative to the
      user-supplied base URI, so the engine installs a URI resolver that
      performs the standard resolution. Without it, the secure factory would
      resolve those lookups to empty content.
    - Run the CI and CodeQL builds with -Puse-apache-snapshots (inherited
      from the org.apache:apache parent POM) so the commons-secure-xml
      SNAPSHOT resolves; CodeQL's autobuild receives the profile through
      MAVEN_ARGS.
    
    Assisted-By: Claude Fable 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01MHgnMnGWHQoH2zD2jFdoMT
---
 .github/workflows/codeql-analysis.yml              |  2 ++
 .github/workflows/maven.yml                        |  2 +-
 pom.xml                                            |  5 +++++
 src/changes/changes.xml                            |  1 +
 .../org/apache/bsf/engines/xslt/XSLTEngine.java    | 22 +++++++++++++++++++++-
 5 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/codeql-analysis.yml 
b/.github/workflows/codeql-analysis.yml
index 38e6424..8874b24 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -70,6 +70,8 @@ jobs:
     # If this step fails, then you should remove it and run the build manually 
(see below)
     - name: Autobuild
       uses: 
github/codeql-action/autobuild@5595ccaf912efad79be6eef63a5619ff05969be3 # 4.37.6
+      env:
+        MAVEN_ARGS: -Puse-apache-snapshots
 
     # â„šī¸ Command-line programs to run using the OS shell.
     # 📚 https://git.io/JvXDl
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 77b2e88..b0b51bb 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -54,4 +54,4 @@ jobs:
         distribution: 'temurin'
         java-version: ${{ matrix.java }}
     - name: Build with Maven
-      run: mvn -V -Ddoclint=all --file pom.xml --no-transfer-progress 
-Ddoclint=none
+      run: mvn -V -Ddoclint=all --file pom.xml --no-transfer-progress 
-Ddoclint=none -Puse-apache-snapshots
diff --git a/pom.xml b/pom.xml
index 263832a..a46b75d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -168,6 +168,11 @@
     </plugins>
   </reporting>
   <dependencies>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-secure-xml</artifactId>
+      <version>1.0.0-SNAPSHOT</version>
+    </dependency>
     <dependency>
       <groupId>org.junit.jupiter</groupId>
       <artifactId>junit-jupiter</artifactId>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index e871e0b..e1b546c 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -27,6 +27,7 @@
   <body>
     <release version="2.5.0" date="YYY-MM-DD" description="TBD.">
       <!-- FIX -->
+      <action type="fix" dev="pkarwasz">Create XSLT transformer factories 
through org.apache.commons:commons-secure-xml, so transformations run with XML 
secure processing enabled.</action>
       <action type="fix" dev="ggregory" due-to="Gary Gregory">Replace internal 
StringBuffer with StringBuilder.</action>
       <action type="fix" dev="ggregory" due-to="Gary Gregory">Fix malformed 
Javadoc comments.</action>
       <!-- ADD -->
diff --git a/src/main/java/org/apache/bsf/engines/xslt/XSLTEngine.java 
b/src/main/java/org/apache/bsf/engines/xslt/XSLTEngine.java
index cea4b45..2d12884 100644
--- a/src/main/java/org/apache/bsf/engines/xslt/XSLTEngine.java
+++ b/src/main/java/org/apache/bsf/engines/xslt/XSLTEngine.java
@@ -20,11 +20,15 @@ package org.apache.bsf.engines.xslt;
 import java.io.File;
 import java.io.Reader;
 import java.io.StringReader;
+import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.Vector;
 
+import javax.xml.transform.Source;
 import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
 import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.URIResolver;
 import javax.xml.transform.dom.DOMResult;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stream.StreamSource;
@@ -36,6 +40,7 @@ import org.apache.bsf.BSF_Log;
 import org.apache.bsf.BSF_LogFactory;
 import org.apache.bsf.util.BSFEngineImpl;
 import org.apache.bsf.util.BSFFunctions;
+import org.apache.commons.xml.secure.SecureTransformerFactory;
 import org.apache.xpath.objects.XObject;
 import org.w3c.dom.Node;
 
@@ -167,7 +172,22 @@ public class XSLTEngine extends BSFEngineImpl {
     public void initialize(final BSFManager mgr, final String lang, final 
Vector declaredBeans) throws BSFException {
         super.initialize(mgr, lang, declaredBeans);
 
-        tFactory = TransformerFactory.newInstance();
+        tFactory = SecureTransformerFactory.newInstance();
+        // The secure factory resolves URIs that no resolver handles to empty 
content; imported and
+        // referenced documents are part of the script here, so restore the 
default resolution for them.
+        tFactory.setURIResolver(new URIResolver() {
+            public Source resolve(final String href, final String base) throws 
TransformerException {
+                try {
+                    URL baseUrl = new File("").toURI().toURL();
+                    if (base != null && !base.isEmpty()) {
+                        baseUrl = new URL(baseUrl, base);
+                    }
+                    return new StreamSource(new URL(baseUrl, 
href).toExternalForm());
+                } catch (final MalformedURLException e) {
+                    throw new TransformerException(e);
+                }
+            }
+        });
     }
 
     /**

Reply via email to