This is an automated email from the ASF dual-hosted git repository.

ppkarwasz pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/commons-secure-xml.git


The following commit(s) were added to refs/heads/main by this push:
     new 1497239  Sharpen the threat model: honored JAXP contracts, XInclude, 
and expansion limits (#66)
1497239 is described below

commit 1497239b852693775fdc409b736ddeb82cf06d99
Author: Piotr P. Karwasz <[email protected]>
AuthorDate: Tue Sep 1 17:30:46 2026 +0200

    Sharpen the threat model: honored JAXP contracts, XInclude, and expansion 
limits (#66)
    
    * Document honored JAXP contracts in the threat model
    
    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 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CLnTBsvmYtxzNTWVGNyz33
    
    * Scope the XInclude guarantee to the Xerces convention
    
    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 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CLnTBsvmYtxzNTWVGNyz33
    
    * Correct a stale factory class name in the threat model
    
    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 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CLnTBsvmYtxzNTWVGNyz33
    
    * Require the schema resolver contract in the threat model
    
    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 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CLnTBsvmYtxzNTWVGNyz33
    
    * Separate expansion count from expansion volume in the threat model
    
    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 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CLnTBsvmYtxzNTWVGNyz33
    
    * Name the source of the entity-expansion bound in the package Javadoc
    
    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 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01CLnTBsvmYtxzNTWVGNyz33
    
    * fix: improve entity expansion disclaimer
    
    * fix: review threat model
    
    * fix: bundled -> external
---
 .../apache/commons/xml/secure/package-info.java    |  6 +-
 src/site/markdown/threat_model.md                  | 66 ++++++++++++++++++++--
 2 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/src/main/java/org/apache/commons/xml/secure/package-info.java 
b/src/main/java/org/apache/commons/xml/secure/package-info.java
index 771c011..0fe8a23 100644
--- a/src/main/java/org/apache/commons/xml/secure/package-info.java
+++ b/src/main/java/org/apache/commons/xml/secure/package-info.java
@@ -45,8 +45,10 @@
  * <ul>
  * <li><strong>External DTDs are not fetched.</strong></li>
  * <li><strong>External entities are not resolved.</strong></li>
- * <li><strong>Internal entity expansion is bounded</strong> by the platform's 
secure-processing limit, so DoS payloads such as Billion Laughs are rejected
- * before they exhaust resources.</li>
+ * <li><strong>Internal entity expansion is bounded</strong> by the processing 
limits the parser applies, so DoS payloads such as Billion Laughs are rejected
+ * before they exhaust resources. The library enables {@link 
javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} wherever the JAXP API 
mandates it, and the limits
+ * are then the implementation's secure defaults. The StAX API defines no such 
feature, and no Android parser supports it, so there the bound is whatever the
+ * implementation applies on its own, and this guarantee is 
<strong>best-effort</strong>.</li>
  * </ul>
  * <p>
  * These guarantees are defined on OpenJDK 8 or later (and JDK distributions 
built from it). No version of Android supports
diff --git a/src/site/markdown/threat_model.md 
b/src/site/markdown/threat_model.md
index 3307514..2d30ebc 100644
--- a/src/site/markdown/threat_model.md
+++ b/src/site/markdown/threat_model.md
@@ -33,7 +33,7 @@ If you have encountered an unlisted security vulnerability or 
other unexpected b
 
 ## Threat Model
 
-This is the threat model for the **0.1.x** release line.
+This is the threat model for the **1.0.x** release line.
 It is versioned with the library: a report against a released version is 
triaged against the model as it stood at that version, not at `HEAD`.
 A finding that breaks something listed under [What is in 
scope](#what-is-in-scope) should be reported through the channel above;
 a finding that falls under [What is out of scope](#what-is-out-of-scope) will 
be closed citing this section.
@@ -119,6 +119,48 @@ tested as complete starting with API level 33
 (see [Supported runtimes](index.html) on the main page),
 but a report demonstrated only on Android is [out of 
scope](#what-is-out-of-scope) on any API level.
 
+**Honored JAXP contracts**
+
+The in-scope requirement that an implementation respects the contract of the 
settings a recipe uses
+(see [What is in scope](#what-is-in-scope))
+extends to the JAXP API contracts themselves.
+In particular:
+
+- A method handed a `SAXSource` carrying an `XMLReader` parses with that 
reader,
+  as the [`SAXSource` 
contract](https://docs.oracle.com/en/java/javase/25/docs/api/java.xml/javax/xml/transform/sax/SAXSource.html#%3Cinit%3E(org.xml.sax.XMLReader,org.xml.sax.InputSource))
 requires.
+- A method handed a `StAXSource` reads from the stream or event reader it 
carries,
+  as the [`StAXSource` 
contract](https://docs.oracle.com/en/java/javase/25/docs/api/java.xml/javax/xml/transform/stax/StAXSource.html)
 implies:
+  the reader must arrive positioned on `START_DOCUMENT` or `START_ELEMENT`,
+  and the source is consumed during processing.
+- A `SchemaFactory`, `Validator` or `ValidatorHandler` locates the external 
resources it needs through the resolver installed on it,
+  as the [`setResourceResolver` 
contract](https://docs.oracle.com/en/java/javase/25/docs/api/java.xml/javax/xml/validation/SchemaFactory.html#setResourceResolver(org.w3c.dom.ls.LSResourceResolver))
 requires
+  ("uses a `LSResourceResolver` when it needs to locate external resources"),
+  leaving each schema language to define what counts as one.
+  The same contract states that a factory's resolver is *not* inherited by the 
`Schema`, `Validator` and `ValidatorHandler` objects it creates,
+  which is why the securing installs the floor on each of them rather than on 
the factory alone.
+
+The securing injects hardened readers and installs these resolver floors,
+so 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 such an implementation would be a valid *hardening* of this 
library,
+but the deviation itself is a contract violation in that implementation,
+not a vulnerability here:
+a report built on one is triaged `OUT-OF-SCOPE: foreign implementation`.
+
+**XInclude resolution**
+
+XInclude is the converse case: JAXP specifies no contract at all.
+`setXIncludeAware` turns the processor on,
+but no part of the API says which resolver — if any — a processor consults for 
an `xi:include` href.
+
+The XInclude guarantee is therefore restricted to implementations that follow 
the Xerces convention
+of routing an `xi:include` fetch through the `EntityResolver`.
+An implementation whose XInclude processor resolves a reference without 
consulting the entity resolver
+fetches outside the securing,
+and a report demonstrated only there is [out of scope](#what-is-out-of-scope).
+
 **System properties that modify behavior**
 
 The library reads a single system property of its own,
@@ -140,12 +182,16 @@ JDK version and the standard `jdk.xml.*` limit properties 
the JDK itself reads:
   default `2500` on JDK 25 and `64000` on JDK 8 through 21). These are trusted 
deployment configuration: an operator may
   set one to tighten (or loosen) a limit globally, but loosening through one 
is reconfiguration, treated like loosening
   any other reserved setting (see [What is out of 
scope](#what-is-out-of-scope)).
-- The bundled parsers apply their own hardcoded secure defaults instead (for 
example external Xerces and Woodstox cap
-  entity expansion at `100000`) and do not read `jdk.xml.*`.
+- External parsers apply their own hardcoded secure defaults instead
+  (for example, external Xerces and Woodstox cap the number of entity 
expansions at `100000`) and do not read `jdk.xml.*`.
 
 On the supported runtimes (see **Supported runtimes** above),
-every one of these defaults still bounds entity expansion tightly enough to 
reject entity-expansion denial of service
-such as Billion Laughs.
+every one of these defaults bounds the number of expansions,
+which is what rejects an exponential payload such as Billion Laughs.
+The volume those expansions produce is a separate limit,
+and implementations differ on whether they set one by default.
+Sizing it, or provisioning for the load it allows instead,
+is the operator's decision, like the other processing limits above.
 
 **Reserved Settings (must not be loosened)**
 
@@ -277,11 +323,15 @@ and reports against a factory reconfigured in any of the 
ways below are out of s
   or a `DOMSource` holding a document parsed elsewhere.
   Its settings are yours, including permissive ones.
   To parse with your own reader under the securing guarantees,
-  obtain it from `HardeningSAXParserFactory.newInstance()`
+  obtain it from `SecureSAXParserFactory.newInstance()`
   before wrapping it in a `SAXSource`.
 - The behavior of a JAXP implementation that does not respect the contract of 
the settings a securing recipe requires
   (the factory method throws rather than returning an unsecured factory),
   and any defect in the underlying JAXP implementation itself.
+- **XInclude outside the Xerces convention.**
+  JAXP does not specify which resolver an XInclude processor consults,
+  so the guarantee is restricted to implementations that route an `xi:include` 
fetch through the entity resolver
+  (see **XInclude resolution** under [Assumptions about the 
environment](#assumptions-about-the-environment)).
 - **Android, on any API level.**
   No version of Android supports `FEATURE_SECURE_PROCESSING`,
   so the securing there is best-effort and no guarantee is defined
@@ -315,6 +365,10 @@ are **not** vulnerabilities under this model:
 - XXE, external-entity, SSRF-through-external-reference, or entity-expansion 
(Billion Laughs) reports against
   a factory used as delivered. Blocking these is exactly what the securing 
does. A working proof against an
   unmodified instance is a `VALID` finding (see below); a scanner that 
pattern-matches on parser type is not.
+- A report that a recognized implementation bounds the number of entity 
expansions but not the volume they produce.
+  The library pins no processing limits at all; each is the implementation's, 
and a deployment that needs a volume bound
+  sets that implementation's own limit
+  (see **System properties that modify behavior** under [Assumptions about the 
environment](#assumptions-about-the-environment)).
 - A report demonstrated only on Android,
   where the securing is best-effort and no guarantee is defined
   (see **Supported runtimes** under [Assumptions about the 
environment](#assumptions-about-the-environment)).

Reply via email to