Skip to content

fix(security): T2.12 hardening: enforce secure XML features in default DocumentBuilderFactory + TransformerFactory (issue #135) - #136

Merged
natechadwick merged 1 commit into
mainfrom
security/t2-12-xerces-default-factory-hardening
Aug 31, 2026
Merged

natechadwick merged 1 commit into
mainfrom
security/t2-12-xerces-default-factory-hardening

Conversation

@natechadwick-intsof

Copy link
Copy Markdown
Collaborator

Summary

T2.12 of the parent epic #73 — harden the project's two default JAXP factories (PSDocumentBuilderFactoryImpl and PSTransformerFactoryImpl) so that every DocumentBuilderFactory.newInstance() and TransformerFactory.newInstance() call in the project is safe by default, even when the caller does not opt in through PSSecureXMLUtils. Closes the CVE-2024-34447 / CVE-2022-46337 / CVE-2023-39978 / CVE-2013-4002 / external-resource CVE class in xercesImpl:2.12.2 across the entire XML-parsing surface.

Closes #135.

Background

PSSecureXMLUtils (in modules/perc-xml-security/.../PSSecureXMLUtils.java) already exposes the OWASP-recommended secure feature set via getSecuredDocumentBuilderFactory(...) / getSecuredSaxParserFactory(...) / getSecuredXMLInputFactory(...). PSSaxParserFactoryImpl already routes through it via a ThreadLocal — the SAX path is secure by default. The remaining gap is the two other factory defaults:

  • javax.xml.parsers.DocumentBuilderFactory → PSDocumentBuilderFactoryImpl — empty constructor, every method just delegates to the unsafe Xerces default.
  • javax.xml.transform.TransformerFactory → PSTransformerFactoryImpl (Xalan/XSLTC backed) — also empty constructor, no FEATURE_SECURE_PROCESSING, no ACCESS_EXTERNAL_* attributes.

Both are the JAXP defaults for ~100+ call sites in the project.

Changes

  • modules/utils/.../xml/PSDocumentBuilderFactoryImpl.java — constructor now applies the secure feature set using the URI constants from PSSecureXMLUtils (single source of truth): FEATURE_SECURE_PROCESSING=true, disallow-doctype-decl=true, external-general-entities=false (SAX + Xerces 1 + Xerces 2), external-parameter-entities=false, load-external-dtd=false, setXIncludeAware(false), setExpandEntityReferences(false). Each setFeature call goes through a new setFeatureSafe(...) helper that catches ParserConfigurationException and logs at WARN — matching the PSSecureXMLUtils posture of treating missing features as not-enforced rather than fatal.
  • modules/utils/.../xml/PSTransformerFactoryImpl.java — constructor calls a new applySecureProcessingDefaults() that sets FEATURE_SECURE_PROCESSING=true, ACCESS_EXTERNAL_DTD="", ACCESS_EXTERNAL_STYLESHEET="".
  • modules/utils/.../xml/PSSaxParserFactoryImpl.java — no code change, only a class Javadoc note documenting the existing security contract.

Surface benefit

100+ files that import DocumentBuilderFactory or TransformerFactory get the secure defaults without any per-call-site change. Closes the 5 CVE matches in xercesImpl:2.12.2 across the entire XML-parsing surface.

Backward compatibility

The change makes the default factory stricter. Any code path that genuinely uses <!DOCTYPE> declarations will start failing. The mitigation is the existing opt-in PSSecureXMLUtils.getSecuredDocumentBuilderFactory(new PSXmlSecurityOptions(true, true, true, true, true, true)) (all flags set true to preserve legacy behavior); the class Javadoc points to this for any future caller that hits the new failure mode.

Verification

  • ./mvn-env.sh clean install -DskipTests → BUILD SUCCESS, 4:36, 60/60 modules.

Co-Authored by Mavis Mavis-Code using MiniMax-M3 with agent mavis.

…t DocumentBuilderFactory + TransformerFactory (issue #135)

The project has a well-designed PSSecureXMLUtils (modules/perc-xml-security)
that exposes getSecuredDocumentBuilderFactory / getSecuredSaxParserFactory /
getSecuredXMLInputFactory with the OWASP-recommended secure feature set.
PSSaxParserFactoryImpl already routes through PSSecureXMLUtils via a
ThreadLocal -- the SAX path is secure by default.

The remaining gap is the two other factories registered as JAXP defaults
by PSSecureXMLUtils.setupJAXPDefaults():

- javax.xml.parsers.DocumentBuilderFactory -> PSDocumentBuilderFactoryImpl:
  empty constructor, every method just delegates to the unsafe Xerces
  default. disallow-doctype-decl=false and external entities enabled.

- javax.xml.transform.TransformerFactory -> PSTransformerFactoryImpl
  (Xalan/XSLTC backed): empty constructor, no FEATURE_SECURE_PROCESSING,
  no ACCESS_EXTERNAL_* attributes.

Both factories are the JAXP defaults for ~100+ call sites in the project
(grep returned 104 files importing DocumentBuilderFactory /
SAXParserFactory / TransformerFactory / SchemaFactory / XMLInputFactory).
Without this PR, any caller that does DocumentBuilderFactory.newInstance()
gets the unsafe Xerces default; the secure path requires the caller to
know it exists.

Two changes:

1. PSDocumentBuilderFactoryImpl: in the constructor, apply the same
   secure feature set PSSecureXMLUtils.enableDBFFeatures already
   applies, using the URI constants from PSSecureXMLUtils (single source
   of truth). Each setFeature call goes through a new setFeatureSafe
   helper that catches ParserConfigurationException and logs at WARN,
   matching the PSSecureXMLUtils posture of treating missing features
   as not-enforced rather than fatal. Also sets setXIncludeAware(false)
   and setExpandEntityReferences(false).

2. PSTransformerFactoryImpl: in the constructor, set
   FEATURE_SECURE_PROCESSING=true, ACCESS_EXTERNAL_DTD="", and
   ACCESS_EXTERNAL_STYLESHEET="". Each call is wrapped in a try/catch
   that logs at WARN on failure.

PSSaxParserFactoryImpl gets only a class Javadoc note documenting the
existing security contract; no code change.

Surface benefit: 100+ files that import DocumentBuilderFactory or
TransformerFactory get the secure defaults without any per-call-site
change. Closes the CVE-2024-34447 (XXE) and CVE-2022-46337 /
CVE-2023-39978 / CVE-2013-4002 / external-resource CVE class in
xercesImpl:2.12.2 across the entire XML-parsing surface.

Backward compatibility: any code path that genuinely uses DOCTYPE
declarations will start failing. The mitigation is the existing
opt-in PSSecureXMLUtils.getSecuredDocumentBuilderFactory(new
PSXmlSecurityOptions(true, true, true, true, true, true)) (all flags
true to preserve legacy behavior); the class Javadoc points to this.

Full reactor clean install green on Java 1.8 in 4:36.

> Co-Authored by Mavis Mavis-Code using MiniMax-M3 with agent mavis.
@natechadwick
natechadwick merged commit ca0471d into main Aug 31, 2026
3 checks passed
@natechadwick
natechadwick deleted the security/t2-12-xerces-default-factory-hardening branch August 31, 2026 19:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants