Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to avoid using xalan TransformerFactory?

Tags:

java

xml

xalan

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.

like image 687
Roman Puchkovskiy Avatar asked Apr 24 '18 11:04

Roman Puchkovskiy


1 Answers

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.

like image 199
Roman Puchkovskiy Avatar answered Oct 23 '22 16:10

Roman Puchkovskiy