Rework exception propagation when securing sources - #60
Merged
garydgregory merged 4 commits intoAug 29, 2026
Conversation
Source-securing failures used to make round trips between the SAX and TrAX exception hierarchies: a SAXException raised while provisioning a secure reader was wrapped into a TransformerConfigurationException and back into a SAXException on the validation and schema paths, and SecureXMLFilter.parse buried the parent reader's SAXParseException under SAXException(TransformerException(...)) for a downstream transformer to wrap yet again. Each hierarchy conversion now happens exactly once, at the API boundary whose checked signature demands it: - SecureSAXParserFactory.newXMLReader (formerly newSecureXMLReader) and secure(Source, boolean) declare their natural ParserConfigurationException/SAXException instead of a never-thrown TransformerConfigurationException. - SecureTransformerFactory.secure(Source, boolean) is the TrAX flavor, converting once to TransformerConfigurationException for the TrAX wrappers. - FallbackIgnoreURIResolver converts locally to TransformerException, keeping the TrAX wrappers out of the XPath shading closure. - SecureXMLFilter.parse rethrows a SAXException or IOException cause of the transform's TransformerException directly, TrAX-filter style, so the original SAXParseException or handler exception surfaces as-is. Assisted-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RZSVucNBf5fsyd1uqamLuk
ppkarwasz
force-pushed
the
feat/source-exception-propagation
branch
from
August 29, 2026 13:14
5558395 to
94ea207
Compare
garydgregory
requested changes
Aug 29, 2026
newXMLReader (formerly newSecureXMLReader) does not normally throw: every supported implementation provides a reader as a routine capability, so a ParserConfigurationException or SAXException there signals a broken environment, not a per-parse condition. Wrap it in the unchecked SecureException instead of TransformerConfigurationException and drop the checked-exception plumbing this branch had introduced: the secure/secureTraX split, the SecureTransformerFactory.secure(Source) wrapper and the per-caller try/catch in SecureValidator, SecureSchemaFactory and FallbackIgnoreURIResolver all revert to plain calls. SecureXMLFilter now performs the XMLFilterImpl.setupParse wiring for the resolver, DTD and error callbacks (the transformer owns the parent's ContentHandler), implements ErrorListener to forward TrAX error reports to the caller-set ErrorHandler, and rethrows the SAXException or IOException cause of a transform failure instead of nesting the hierarchies, so the original SAXParseException surfaces as-is and no implementation can end the parse silently. Assisted-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RZSVucNBf5fsyd1uqamLuk
Assisted-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RZSVucNBf5fsyd1uqamLuk
A recording parent reader asserts parse wires the filter as the parent's EntityResolver, DTDHandler and ErrorHandler (the wiring calls themselves: which of them the TrAX implementation later consults or overwrites varies, so delivery cannot be asserted uniformly). A second test proves the EntityResolver route end-to-end: a caller-set resolver opts an external entity in through the parent's floor. The cause-chain walks now follow SAXException.getException(), which Android's SAXException does not link into Throwable.getCause(). Assisted-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RZSVucNBf5fsyd1uqamLuk
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.
Exceptions raised while provisioning a secure reader for a
Sourceused to make round trips between the SAX and TrAX exception hierarchies:SecureSAXParserFactory.secure(Source, boolean)wrappedParserConfigurationException/SAXExceptioninto aTransformerConfigurationException, whichSecureValidator.validateandSecureSchemaFactorythen wrapped back into aSAXException— a three-deep chain for what began as aSAXException.SecureXMLFilter.parseburied the parent reader'sSAXParseExceptionunderSAXException(TransformerException(...)), which a downstream transformer consuming the filter wraps into yet anotherTransformerException.newSecureXMLReaderdeclared (and documented) aTransformerConfigurationExceptionit never threw.Each hierarchy conversion now happens exactly once, at the API boundary whose checked signature demands it:
SecureSAXParserFactory.newXMLReader(formerlynewSecureXMLReader) andsecure(Source, boolean)declare their naturalParserConfigurationException, SAXException; the validation and schema paths let aSAXExceptionpropagate untouched and wrap onlyParserConfigurationException.SecureTransformerFactory.secure(Source, boolean)is the TrAX flavor, converting once to theTransformerConfigurationExceptionthe TrAX signatures demand.FallbackIgnoreURIResolverconverts locally toTransformerException(the accurate type for a resolution-time failure), which also keeps the TrAX wrappers out of the XPath shading closure (ShadingFootprintTest).SecureXMLFilter.parseapplies the standard TrAX-filter unwrap idiom: aSAXExceptionorIOExceptioncause of the transform'sTransformerExceptionis rethrown directly, so the originalSAXParseException(with locator info) or a handler's own exception surfaces as-is.Two new
XMLFilterTestcases pin the unwrap down in the project's dual-contract style, tolerating implementations that swallow the error (Xalan) or hide it in an internal wrapper (XSLTC) while asserting the original exception is never re-wrapped where it does surface.Full surefire matrix passes.
🤖 Generated with Claude Code
https://claude.ai/code/session_01RZSVucNBf5fsyd1uqamLuk