Harden XML parsing via commons-xml - #647
Merged
Merged
Conversation
ppkarwasz
force-pushed
the
feat/use-commons-xml
branch
from
August 30, 2026 20:33
18ec795 to
d650a2b
Compare
ppkarwasz
marked this pull request as draft
August 30, 2026 20:34
Replace all direct JAXP factory instantiations (DocumentBuilderFactory, SAXParserFactory, TransformerFactory) with the secure factories from org.apache.commons:commons-xml. These factories enable FEATURE_SECURE_PROCESSING and install a non-removable entity-resolver floor on every parser they produce: external DTD, entity, schema and XInclude lookups that a caller-set resolver does not resolve are resolved to empty content instead of being fetched, and internal entity expansion is bounded, regardless of the JAXP implementation on the classpath. Hardening the parsing of a configuration file is admittedly not necessary: configuration files are normally trusted. This limits the side-effects if a user (against advice) decides to parse untrusted configuration files. Changes: - Add the commons-xml dependency (1.0.0-SNAPSHOT until its first release). - Route factory creation through SecureDocumentBuilderFactory, SecureSAXParserFactory and SecureTransformerFactory in XMLConfiguration, XMLDocumentHelper, XMLPropertiesConfiguration and XMLPropertyListConfiguration, plus the affected tests. - No explicit hardening of the source passed to XMLDocumentHelper.transform is needed: transformers created by SecureTransformerFactory rewrite their sources on every transform call. - XMLConfiguration keeps its DefaultEntityResolver contract (return null for unknown entities): a null return no longer lets the parser fetch the external resource, because the resolver floor resolves it to empty content instead. - Keep resolver-based schema validation (for example via CatalogResolver) working under secure processing: disable EntityResolver2 handling (http://xml.org/sax/features/use-entity-resolver2), because the JDK does not mark schema documents supplied by an EntityResolver2 as resolver-created and its accessExternalSchema check then refuses them, and lift the accessExternalSchema restriction, because Java 8 applies it even to sources supplied by a plain resolver. Lifting the restriction does not reopen external fetches: the secure factory's resolver floor still resolves every unresolved lookup to empty content instead of fetching it. - Run the CI and CodeQL builds with -Puse-apache-snapshots (inherited from the org.apache:apache parent POM) so the commons-xml SNAPSHOT resolves; CodeQL's autobuild receives the profile through MAVEN_ARGS. Assisted-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Assisted-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NS6CoaDG2mfSNpy4Ukhvrn
The 2001 test configuration references its schema through an absolute https URI (xsi:noNamespaceSchemaLocation). The hardened parser does not fetch external resources, so testSchemaValidationError no longer reached the intended schema validation error; the entity resolver refused the remote fetch first. Register the local testMultiConfiguration.xsd for that system URI via an XML catalog (CatalogResolver pointing at the existing catalog.xml, which already rewrites https://commons.apache.org/ to the local test resources) and set it as the builder's entity resolver. The schema now loads locally, so the expected SAXParseException validation error is raised again. Assisted-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ppkarwasz
force-pushed
the
feat/use-commons-xml
branch
from
August 30, 2026 20:38
d650a2b to
071a0af
Compare
Disabling EntityResolver2 handling is unnecessary: the parser does use a schema document supplied by an EntityResolver2 once the accessExternalSchema check no longer refuses it, so lifting that restriction alone keeps resolver-based schema validation working on every JDK version. Also inline XMLDocumentHelper.createTransformerFactory into its only caller and drop its test. Assisted-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NS6CoaDG2mfSNpy4Ukhvrn
Assisted-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NS6CoaDG2mfSNpy4Ukhvrn
ppkarwasz
marked this pull request as ready for review
August 30, 2026 20:57
The org.apache.commons:commons-xml artifact was renamed to org.apache.commons:commons-secure-xml and its Java package moved from org.apache.commons.xml to org.apache.commons.xml.secure. Update the dependency and imports accordingly, and record the hardening in changes.xml. Assisted-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHgnMnGWHQoH2zD2jFdoMT
Bump org.apache.commons:commons-secure-xml from 1.0.0-SNAPSHOT to 1.0.0 and add the temporary staging repository https://repository.apache.org/content/repositories/orgapachecommons-1962/ after Central, so the vote gets downstream CI results. Drop the -Puse-apache-snapshots profile from the CI workflows, which the release version no longer needs. Remove the staging repository once 1.0.0 is released. Assisted-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0167e29ScPEdfzJnEFm95imK
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces all direct JAXP factory instantiations (
DocumentBuilderFactory,SAXParserFactory,TransformerFactory) with the secure factories fromorg.apache.commons:commons-xml. These enableFEATURE_SECURE_PROCESSINGand install a non-removable entity-resolver floor on every parser they produce: external DTD, entity, schema and XInclude lookups that a caller-set resolver does not resolve are resolved to empty content instead of being fetched, and internal entity expansion is bounded, regardless of the JAXP implementation on the classpath.Hardening the parsing of a configuration file is admittedly not necessary: configuration files are normally trusted. This limits the side-effects if a user (against advice) decides to parse untrusted configuration files.
Changes
commons-xmldependency (1.0.0-SNAPSHOTuntil its first release) and run the CI and CodeQL builds with-Puse-apache-snapshots(inherited from theorg.apache:apacheparent POM) so the SNAPSHOT resolves; CodeQL's autobuild receives the profile throughMAVEN_ARGS.SecureDocumentBuilderFactory,SecureSAXParserFactoryandSecureTransformerFactoryinXMLConfiguration,XMLDocumentHelper,XMLPropertiesConfigurationandXMLPropertyListConfiguration, plus the affected tests. Transformers created bySecureTransformerFactoryrewrite their sources on everytransformcall, so no explicit hardening of the source passed toXMLDocumentHelper.transformis needed.XMLConfigurationkeeps itsDefaultEntityResolvercontract (returnnullfor unknown entities): anullreturn no longer lets the parser fetch the external resource, because the resolver floor resolves it to empty content instead.XMLConfigurationlifts theaccessExternalSchemarestriction to keep resolver-based schema validation (such as viaCatalogResolver) working under secure processing: due to a JDK bug, schema documents supplied by an entity resolver are not marked as resolver-created (old JDK 8 versions never mark them; newer versions fail to mark only those supplied by anEntityResolver2), so the check would refuse them. Lifting the restriction does not reopen external fetches: the parser never retrieves a schema itself, because the secure factory's resolver floor resolves every lookup the caller's resolver leaves unresolved to empty content instead of fetching it.TestMultiFileConfigurationBuilder#testSchemaValidationErrorregisters the local test schema for its absolutehttpssystem URI via an XML catalog, since the hardened parser no longer fetches it; the test now reaches the intended schema validation error again.Full test suite passes with
-Puse-apache-snapshots(3051 tests); the affected test classes also pass on Java 8, where the JDK applies theaccessExternalSchemacheck even to resolver-supplied schema documents.🤖 Generated with Claude Code
https://claude.ai/code/session_01NS6CoaDG2mfSNpy4Ukhvrn