Sharpen the threat model: honored JAXP contracts, XInclude, and expansion limits - #66
Merged
Merged
Conversation
Make explicit, under "Assumptions about the environment", that the securing recipes rely on the JAXP implementation honoring the SAXSource and StAXSource contracts: the reader supplied inside the Source is the one that performs the parse. An implementation that substitutes an internal parser of its own violates that contract, so a report built on such a substitution is triaged OUT-OF-SCOPE: foreign implementation; defending against it would be hardening, not a fix. Also bump the modeled release line to 1.0.x. Assisted-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CLnTBsvmYtxzNTWVGNyz33
JAXP specifies no contract for XInclude resolution: setXIncludeAware turns the processor on, but nothing in the API says which resolver a processor consults for an xi:include href. The securing gates XInclude through the entity-resolver floor, so the guarantee holds on the implementations that follow the Xerces convention of routing the fetch through the EntityResolver, and a report demonstrated only on an implementation that resolves an href without consulting it is out of scope. Assisted-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CLnTBsvmYtxzNTWVGNyz33
The caller-supplied-parser exclusion pointed at HardeningSAXParserFactory, a class this library does not ship; the entry point that returns a secured reader is SecureSAXParserFactory. Assisted-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CLnTBsvmYtxzNTWVGNyz33
The schema recipe rests on the SchemaFactory, Validator and ValidatorHandler contracts for setResourceResolver: each locates the external resources it needs through the resolver installed on it, with the schema language defining what counts as one. Record that alongside the reader-injection contracts, together with the same contract's statement that a factory's resolver is not inherited by the objects it creates, which is why the floor is installed on each of them. An implementation that stores a resolver but reaches an external resource without consulting it works outside the securing, on the same terms as one that ignores an injected reader. Assisted-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CLnTBsvmYtxzNTWVGNyz33
The model claimed every implementation's secure default bounds entity expansion tightly enough to reject an entity-expansion denial of service. That holds for the exponential shape it names, Billion Laughs, because the defaults bound the number of expansions; it over-claims for the volume those expansions produce, which is a separate limit the stock JDK sets by default and Woodstox and external Xerces leave unset. State the two dimensions apart, and place the volume bound where the other processing limits already sit: with the operator, who sizes it or provisions for the load instead. A matching entry under Known Non-Findings routes a report of that shape. Assisted-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CLnTBsvmYtxzNTWVGNyz33
The guarantee said internal expansion is bounded "by the platform's secure-processing limit", which is inaccurate for StAX: JSR-173 defines no secure-processing feature, and the stock JDK's XMLInputFactory rejects it outright. The bound there comes from the implementation's own defaults, applied unconditionally, which is why the StAX recipe sets no limit of its own and has nothing to probe. Say where the limits come from instead. The other two guarantees are unchanged: the resolver floor is universal. Assisted-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CLnTBsvmYtxzNTWVGNyz33
garydgregory
requested changes
Sep 1, 2026
garydgregory
left a comment
Member
There was a problem hiding this comment.
Hi @ppkarwasz
A section needs clarification. See my comment please.
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.
Makes explicit several things the securing recipes already rely on, or already do, but that the threat model and package Javadoc either left unsaid or stated more broadly than the code delivers.
Honored JAXP contracts — the in-scope requirement that an implementation respect the contract of the settings a recipe uses extends to the JAXP API contracts themselves:
SAXSourcecarrying anXMLReaderparses with that reader, as theSAXSourcecontract requires;StAXSourcereads from the stream or event reader it carries, as theStAXSourcecontract implies (the reader must arrive positioned onSTART_DOCUMENTorSTART_ELEMENT, and the source is consumed during processing);SchemaFactory,ValidatororValidatorHandlerlocates the external resources it needs through the resolver installed on it, assetResourceResolverrequires — "uses aLSResourceResolverwhen it needs to locate external resources" — with each schema language defining what counts as one. The same contract states a factory's resolver is not inherited by theSchema,ValidatorandValidatorHandlerobjects it creates, which is why the securing installs the floor on each of them rather than on the factory alone.An implementation that ignores what it was handed — parsing with an internal parser of its own, or reaching an external resource without consulting the resolver — works outside the securing. Guarding against that would be a valid hardening, but the deviation is a contract violation in that implementation, not a vulnerability here: such a report is triaged
OUT-OF-SCOPE: foreign implementation.XInclude resolution — the converse case.
setXIncludeAwareturns the processor on, but no part of JAXP says which resolver, if any, a processor consults for anxi:includehref. The securing gates XInclude through the entity-resolver floor, so the guarantee is restricted to implementations following the Xerces convention of routing that fetch through theEntityResolver; an implementation that resolves an href without consulting it fetches outside the securing, and a report demonstrated only there is out of scope. A matching bullet is added to What is out of scope.Entity expansion: count versus volume — the model claimed every implementation's secure default "bounds entity expansion tightly enough to reject entity-expansion denial of service such as Billion Laughs". That holds for the exponential shape it names, because those defaults bound the number of expansions. It over-claims for the volume those expansions produce, which is a separate limit: the stock JDK sets one by default (
jdk.xml.totalEntitySizeLimit,jdk.xml.maxGeneralEntitySizeLimit), while Woodstox (com.ctc.wstx.maxCharacters,com.ctc.wstx.maxTextLength) and external Xerces leave the equivalent unset. The two dimensions are now stated apart, with the volume bound placed where the other processing limits already sit — with the operator, who sizes it or provisions for the load instead — and a Known Non-Findings entry routes a report of that shape.Where the expansion bound comes from (
package-info.java) — the same guarantee said expansion is bounded "by the platform's secure-processing limit". That is inaccurate for StAX: JSR-173 defines no secure-processing feature, and the stock JDK'sXMLInputFactoryrejectsFEATURE_SECURE_PROCESSINGoutright (Woodstox, inverting the expectation, accepts it). On StAX the bound comes from the implementation's own defaults, applied unconditionally — the JDK installs itsXMLSecurityManagerat construction, Woodstox caps entity count at 100000 — which is why the StAX recipe sets no limit of its own and has nothing to probe. The bullet now names the source of the limits. The other two guarantees are untouched: the resolver floor is universal.Two contract-hygiene fixes to the same document, so that every released version is covered by exactly one model version and every named entry point exists:
HardeningSAXParserFactory, a class this library does not ship — corrected toSecureSAXParserFactory.🤖 Generated with Claude Code