Is it possible to avoid using xalan TransformerFactory?
Asked Answered
K

3

12

I have the following code:

final TransformerFactory factory = TransformerFactory.newInstance();

factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");

The second line works fine in modern JDKs (I tried 1.8) with a default TransformerFactory. But when I add xalan (version 2.7.2, the most recent one) to classpath, I get the following on that second line:

Exception in thread "main" java.lang.IllegalArgumentException: Not supported: http://javax.xml.XMLConstants/property/accessExternalDTD
    at org.apache.xalan.processor.TransformerFactoryImpl.setAttribute(TransformerFactoryImpl.java:571)
    at Main.main(Main.java:11)

I guess this is because xalan's TransformerFactory does not support this attribute. Xalan's implementation gets picked up through ServiceLoader mechanism: it is specified in services/javax.xml.transform.TransfomerFactory in xalan jar.

It is possible to override the TransformerFactory implementation using javax.xml.transform.TransformerFactory system property or with $JRE/lib/jaxp.properties file, or pass class name directly in code. But to do it, I must supply a concrete class name. Right now, it is com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl, but it's a bit scary to hardcode it in a system property, as on JDK upgrade they can easily change the class name, and we will just get a runtime error.

Is there any way to instruct the TransformerFactory.newInstance() to just ignore that xalan-supplied implementation? Or tell it 'just use the system default'.

P.S. I cannot just remove xalan from classpath because a bunch of other libraries we use depend on it.

Khz answered 24/4, 2018 at 11:39 Comment(2)
Are you trying to disable this feature? If so, take a look here: #27129078Annatto
@Annatto Yes, I am trying to disable external DTD processing, so my question looks like an X/Y problem. But factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true) doesn't prevent Xalan from accessing an external DTD: when I feed an XML with an external DTD link (an http link), Xalan tries to load that DTD and gets a ConnectException.Khz
K
4

The only thing I could achieve here is to hardcode JDK default factory and use the normal discovery process as a fallback:

TransformerFactory factory;
try {
   //the open jdk implementation allows the disabling of the feature used for XXE
    factory = TransformerFactory.newInstance("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl", SecureXmlFactories.class.getClassLoader());
} catch (Exception | TransformerFactoryConfigurationError e) {
    //this part uses the default implementation of in xalan 2.7.2
   LOGGER.error("Cannot load default TransformerFactory, le's try the usual way", e);
   //not advisable if you dont want your application to be vulnerable. If needed you can put null here.
   factory = TransformerFactory.newInstance();

}

and then configure it under try/catch

// this works everywhere, but it does not disable accessing
// external DTDs... still enabling it just in case
try {
    factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
} catch (TransformerConfigurationException e) {
    LOGGER.error("Cannot enable secure processing", e);
}

// this does not work in Xalan 2.7.2
try {
    factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
} catch (Exception e) {
    LOGGER.error("Cannot disable external DTD access", e);
}
// this does not work in Xalan 2.7.2
try {
    factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
} catch (Exception e) {
    LOGGER.error("Cannot disable external stylesheet access", e);
}

And monitor the logs to see if/when the default JDK factory class name changes.

Khz answered 7/5, 2018 at 17:28 Comment(5)
Please note the implementation in catch block factory = TransformerFactory.newInstance(); . it is not XXE safe and this logic will fail in case of attack.Legg
Can you please let us know jar file name to use SecureXmlFactories.class file.Carma
@ParameshKorrakuti SecureXmlFactories is the name of the class that includes the code shown as a proposed solution. You could just use the snippets shown here, class name is not important.Khz
This answer matched my case 100%. However, I tried using this code tf = TransformerFactory.newInstance("javax.xml.transform.TransformerFactory", javax.xml.transform.TransformerFactory.class.getClassLoader()); to get the instance from JDK instead from org.apache.xalan.processor.TransformerFactoryImpl but it's not working. I tried also tf = TransformerFactory.newInstance("org.apache.xalan.processor.TransformerFactoryImpl", javax.xml.transform.TransformerFactory.class.getClassLoader()); and still coming from xalan please help!Haswell
Now I am able to wrap my head around this issue. Check my answer here: https://mcmap.net/q/1012195/-java-lang-illegalargumentexception-not-supported-http-javax-xml-xmlconstants-property-accessexternaldtdHaswell
A
0

Another way to define which TransformerFactory implementation to load is by setting the javax.xml.transform.TransformerFactory system property (docs).

In Ant you would do something like this inside your target:

<jvmarg value="-Djavax.xml.transform.TransformerFactory=com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"/>

In the above example, I'm setting the one from OpenJDK8 as default

Assemblage answered 24/9, 2020 at 13:16 Comment(0)
D
0

This does not work with JAVA9 onwards. pass FactoryClass and class loader to newInstance something like this

final TransformerFactory transformerFactory = TransformerFactory.newInstance(                "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl",
                CLASS_NAME.class.getClassLoader());
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
        
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
Decapolis answered 20/10, 2021 at 20:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.