Skip to content

Harden XML parsing via commons-secure-xml - #393

Merged
garydgregory merged 7 commits into
masterfrom
feat/use-commons-xml
Sep 11, 2026
Merged

garydgregory merged 7 commits into
masterfrom
feat/use-commons-xml

Conversation

@ppkarwasz

@ppkarwasz ppkarwasz commented Aug 31, 2026

Copy link
Copy Markdown
Member

Creates XML stream readers, parsers, schema factories and transformers through org.apache.commons:commons-secure-xml (1.0.0-SNAPSHOT until its first release). The secure factories enable XML secure processing and install a non-removable entity-resolver floor: 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.

  • SCXMLReader goes through SecureXMLInputFactory (the Configuration-supplied XMLResolver still takes precedence, and the factoryId/factoryClassLoader override still selects the underlying implementation), plus SecureSchemaFactory and SecureDocumentBuilderFactory; SCXMLWriter and ContentParser go through SecureTransformerFactory.
  • ContentParser.parseXml now wraps its argument in an InputSource: DocumentBuilder.parse(String) interprets its argument as a URI, so the method never actually parsed the XML content it was documented to parse.
  • CI and CodeQL run with -Puse-apache-snapshots so the SNAPSHOT dependency resolves.

🤖 Generated with Claude Code

Create XML stream readers, parsers, schema factories and transformers
through org.apache.commons:commons-secure-xml. The secure 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.

Changes:
- Add the commons-secure-xml dependency (1.0.0-SNAPSHOT until its first
  release).
- Route factory creation through SecureXMLInputFactory,
  SecureSchemaFactory and SecureDocumentBuilderFactory in SCXMLReader,
  and through SecureTransformerFactory in SCXMLWriter and ContentParser.
  A Configuration-supplied XMLResolver still takes precedence: the floor
  only handles lookups the resolver leaves unresolved, and the
  factoryId/factoryClassLoader override still selects the underlying
  implementation.
- ContentParser.parseXml now wraps its argument in an InputSource:
  DocumentBuilder.parse(String) interprets its argument as a URI, so the
  method never actually parsed the XML content it was documented to
  parse.
- Run the CI and CodeQL builds with -Puse-apache-snapshots (inherited
  from the org.apache:apache parent POM) so the commons-secure-xml
  SNAPSHOT resolves; CodeQL's autobuild receives the profile through
  MAVEN_ARGS.

Assisted-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MHgnMnGWHQoH2zD2jFdoMT
@ppkarwasz
ppkarwasz force-pushed the feat/use-commons-xml branch from bb35dc4 to cb9672c Compare August 31, 2026 15:07
ppkarwasz and others added 2 commits September 3, 2026 07:04
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
Comment thread src/main/java/org/apache/commons/scxml2/io/ContentParser.java
@garydgregory

Copy link
Copy Markdown
Member

Hi @ppkarwasz
If you can't get to my comment, I'll merge the PR in a bit and add a test.

SCXMLWriter.getTransformer() logged the failure and returned null, so
the static XFORMER field silently became null and every writeNode() call
failed later with a NullPointerException far from the actual cause.

Throw an IllegalStateException instead, with a message that names the
reason: either the TrAX implementation found on the class path rejects
the output properties the writer needs, or no implementation could be
instantiated at all. Catch IllegalArgumentException from
setOutputProperties() as well, and keep TransformerFactoryConfigurationError
in the catch so a missing implementation is reported through the same
message rather than as a bare Error from the class initializer.

The failure can only be caused by the TrAX implementation in use, so
there is nothing for a caller to recover from.

Assisted-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0127cAFjN5DYE8pLAysQps4f
getReader() always called SecureXMLInputFactory.newInstance() and then
discarded the result when the Configuration supplied a factoryId and a
class loader, replacing it with the factory created by newFactory().
Create only the factory that is actually used, and make the local final.

Assisted-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0127cAFjN5DYE8pLAysQps4f
The class had no coverage for its XML methods. Assert that parseXml()
treats its argument as an XML document rather than as a URI, which is
what DocumentBuilder.parse(String) did before the method started
wrapping the string in an InputSource, and that parseContent() maps an
XML document to a NodeValue and toXml() round trips back into it.

Assisted-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0127cAFjN5DYE8pLAysQps4f
@ppkarwasz
ppkarwasz marked this pull request as ready for review September 11, 2026 08:25
@ppkarwasz

Copy link
Copy Markdown
Member Author

This should be correct now.

In #394 I fixed the test failure on JDK 27-ea.

@garydgregory
garydgregory merged commit 20c2efc into master Sep 11, 2026
11 checks passed
@garydgregory
garydgregory deleted the feat/use-commons-xml branch September 11, 2026 15:17
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