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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With