Skip to content

Keep the resolver floor on a reader obtained before reset() - #72

Merged
garydgregory merged 1 commit into
apache:mainfrom
ppkarwasz:fix/sax-parser-reset-floor
Sep 1, 2026
Merged

Keep the resolver floor on a reader obtained before reset()#72
garydgregory merged 1 commit into
apache:mainfrom
ppkarwasz:fix/sax-parser-reset-floor

Conversation

@ppkarwasz

Copy link
Copy Markdown
Member

A JAXP SAXParser hands out one XMLReader for its lifetime — both the stock JDK and Xerces hold it in a final field and return that instance from every getXMLReader() call — and reset() reverts it to its just-created state, restoring the initial (resolver-less) entity resolver captured before the floor existed.

SecureSAXParser.reset() responded by dropping its cached views, so the floor was re-installed on the next getXMLReader() call. A caller that kept the reader it had already obtained — which is the case reset() exists for, instance reuse — went on parsing through that same underlying reader with no floor on it. The existing ResetSecureTest case materialised a reader before the reset but then re-fetched it afterwards, so it exercised the re-secured view rather than the retained one.

The fix re-secures the reader in place, through the view already vended, matching the eager pattern SecureDocumentBuilder.reset(), SecureTransformer.reset() and SecureValidator.reset() already use. Keeping a single wrapper also avoids orphaning its floor: a resolver the caller sets on that view stays routed through the floor that is actually installed.

Tests:

  • ResetSecureTest.saxParserResetKeepsFloorOnReaderVendedBeforeReset — holds the reader across the reset and parses through it. It fails on both the test-stockjdk and test-xerces executions without the main-code change, and passes with it.
  • SecureSAXParserTest — the reset case now asserts the security property (the views survive the reset and the floor is back on the underlying reader) instead of the previous mechanism (that the views are recreated), and is renamed accordingly.

Verified with the full surefire matrix and the GraalVM -Pnative-xalan native suite. No changes.xml entry: the behaviour lands in the same release cycle that introduced it.

🤖 Generated with Claude Code

A JAXP parser hands out one reader for its lifetime, and reset() reverts
that reader to its just-created state, which removes the floor the
wrapper installed after creation. SecureSAXParser only dropped its
cached views, so the floor came back on the next getXMLReader() call
while a reader a caller already held kept parsing without one.

Re-secure the reader in place instead, through the view already vended,
as SecureDocumentBuilder.reset() has always done. Keeping one wrapper
also keeps a caller resolver set on that view routed through the floor
that is actually installed.

ResetSecureTest covers the retained reader; the SecureSAXParser unit
test now asserts the views survive a reset with the floor in place,
rather than that they are recreated.

Assisted-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CLnTBsvmYtxzNTWVGNyz33
@garydgregory
garydgregory merged commit 6fcb571 into apache:main Sep 1, 2026
17 checks passed
@ppkarwasz
ppkarwasz deleted the fix/sax-parser-reset-floor branch September 1, 2026 13: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