This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch coheigea/threat-model in repository https://gitbox.apache.org/repos/asf/ws-xmlschema.git
commit a2196fc3cec45ea169a2fa2e21f41d51353e9eae Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Fri Jul 10 12:13:55 2026 +0100 Reviewed threat model, removed draft --- draft-THREAT-MODEL.md | 748 -------------------------------------------------- 1 file changed, 748 deletions(-) diff --git a/draft-THREAT-MODEL.md b/draft-THREAT-MODEL.md deleted file mode 100644 index ee675a41..00000000 --- a/draft-THREAT-MODEL.md +++ /dev/null @@ -1,748 +0,0 @@ -<!-- - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. ---> - -# Apache XMLSchema Security Threat Model (draft) - -## §1 Header - -- **Project**: Apache XMLSchema — *"a lightweight schema object model - that can be used to manipulate and generate XML schema - representations"* *(documented: `README.txt`)*. -- **Repository**: `apache/ws-xmlschema`. -- **Version / commit**: this model is drafted against the default branch - at clone time. A report against project release *N* should be triaged - against the model as it stood at *N*, not at HEAD. Latest release - documented in `RELEASE-NOTE.txt`: 2.3.0. -- **Date**: 2026-05-30. -- **Authors**: ASF Security team draft, awaiting XMLSchema / Webservices - PMC review. -- **Status**: draft — under maintainer review. -- **Reporting**: vulnerabilities that fall under §8 (claimed - properties) should be reported per the Apache Security Team disclosure - channel (<https://www.apache.org/security/>); reports that fall under - §3 (out of scope) or §9 (properties not provided) will be closed by - XMLSchema triagers citing this document. The project does not ship - an in-repo `SECURITY.md` at draft time *(inferred — §14 Q1)*. -- **Provenance legend** — - *(documented)* = drawn from in-repo docs / source comments / project - website with citation; - *(maintainer)* = stated by an XMLSchema maintainer in response to this - draft; - *(inferred)* = synthesized by the producer from code structure or - domain knowledge, awaiting PMC ratification (every *(inferred)* tag has - a matching §14 question). -- **Draft confidence**: 21 documented / 0 maintainer / 24 inferred. - -XMLSchema is a Java library that parses, models, walks, and serializes -W3C XML Schema documents (`.xsd` files). It is *not* a document -validator at its core (despite the `xmlschema-walker` providing some -validation helpers): the primary product is the in-memory object model -of a schema, and the schema-imports / schema-includes resolver that lets -a `<xs:include>` or `<xs:import>` pull in additional schemas from a -URI. XMLSchema is consumed primarily by SOAP stacks (Apache CXF, Apache -Axis) and other code-generation / data-binding tooling. It is bundled -into runtimes that ingest WSDLs and policy documents. - -## §2 Scope and intended use - -### Intended use - -- In-process parsing and manipulation of W3C XML Schema documents in - Java, primarily as part of a tooling pipeline (WSDL import, - code generation, data-binding) or as part of a SOAP stack at runtime - *(documented: `README.txt`)*. -- Three modules ship: `xmlschema-core` (the object model + parser + - serializer + URI resolver), `xmlschema-walker` (visitor-style schema - walking + a `XmlSchemaElementValidator` used for tooling), and - `xmlschema-bundle-test` (an OSGi bundle integration test). The - `w3c-testcases` directory holds the W3C test suite for regression - testing only. - -### Deployment shape - -XMLSchema is an in-process Java library. There is no daemon, no -network listener, no CLI. The threat model is therefore that of an -**XML-parsing library that performs caller-directed network or -filesystem IO** when it follows `<xs:include>` / `<xs:import>` / -`schemaLocation` references *(inferred — §14 Q2)*. - -### Caller roles - -| Role | Trust level | Notes | -| --- | --- | --- | -| **Embedding Java application** | trusted | Calls `XmlSchemaCollection.read(...)`, decides whether the input stream came from disk, classpath, or wire; decides which `URIResolver` to plug in. | -| **`URIResolver` implementation** | trusted | Either the bundled `DefaultURIResolver` (which constructs a `URL` from the parent schema's base URI + the `schemaLocation` and returns an `InputSource` pointing at it) or a caller-supplied resolver *(documented: `xmlschema-core/src/main/java/.../resolver/DefaultURIResolver.java`)*. A hostile resolver is out of model. | -| **`ExtensionRegistry` implementation** | trusted | Pluggable via system property `org.apache.ws.commons.schema.extension_registry` *(documented: `xmlschema-core/src/main/java/org/apache/ws/commons/schema/XmlSchemaCollection.java` line 361)*. | -| **Producer of the schema bytes** (`Reader`, `InputStream`, `InputSource`, `Source`, `Document`, `Element`) | **variable** — see §6 trust table | The *only* attacker-controllable input position; in many embeddings the schema bytes come from a WSDL fetched off the wire. | -| **Producer of imported / included schemas** (resolved by the `URIResolver`) | **variable** — typically as untrusted as the parent schema, but can be a *different* origin if the parent's `<xs:import schemaLocation="http://attacker/evil.xsd">` points elsewhere | Following an `xs:import` is a **second, possibly cross-origin, fetch**. This is the principal SSRF surface. | -| **JDK XML platform** (`DocumentBuilderFactory`, `TransformerFactory`, `SchemaFactory`) | trusted upstream | XMLSchema sets `FEATURE_SECURE_PROCESSING=true` on the factories it constructs but does **not** set `disallow-doctype-decl` or unset external-entity properties explicitly *(documented: `XmlSchemaCollection.java` line 713, `XmlSchema.java` line 886, `DomBuilderFromSax.java` line 81)*. | - -### Component-family table - -| Family | Representative entry point | Touches outside the process? | In-model? | -| --- | --- | --- | --- | -| `xmlschema-core` object model — `XmlSchema*` types, `XmlSchemaCollection`, `SchemaBuilder` | `XmlSchemaCollection.read(InputSource, ...)` and overloads | **no** for in-memory model; **yes** when the URI resolver follows `xs:import`/`xs:include` | **yes** | -| `xmlschema-core` URI resolver — `DefaultURIResolver`, `CollectionURIResolver`, `URIResolver` | `XmlSchemaCollection.setSchemaResolver()` | **yes** — by design, fetches imported schemas | **yes** | -| `xmlschema-core` serializer — `XmlSchemaSerializer` | `XmlSchema.write(OutputStream)` | **no** | **yes** | -| `xmlschema-core` resource loader (internal) — `DocumentBuilderFactory` and `TransformerFactory` instances configured with `FEATURE_SECURE_PROCESSING=true` | invoked by `XmlSchemaCollection.read(InputSource, ...)` and `XmlSchemaSerializer` | **no** | **yes** | -| `xmlschema-walker` visitor — `XmlSchemaWalker`, `XmlSchemaVisitor`, `XmlSchemaScope` | walked by a caller-supplied `XmlSchemaVisitor` | **no** | **yes** | -| `xmlschema-walker` element validator — `XmlSchemaElementValidator` | walked at SAX-event time; consults the schema model | **no** (operates on caller-supplied SAX events) | **yes** | -| `xmlschema-walker` DOM-from-SAX helper — `DomBuilderFromSax` | builds a DOM out of SAX events for validator inspection | **no** | **yes** | -| `xmlschema-bundle-test` | OSGi packaging regression test | n/a | **out of model** *(§3)* | -| `w3c-testcases/` | W3C schema test data | n/a | **out of model** *(§3)* | - -A finding is in-model only if it reaches a row marked **yes**. - -## §3 Out of scope (explicit non-goals) - -1. **XML *document* validation against a schema.** XMLSchema is a - *schema* model; the only validation-like artifact is - `xmlschema-walker`'s `XmlSchemaElementValidator` used in tooling - contexts. Findings that depend on the JDK `Validator` / - `SchemaFactory` behavior or on third-party validators built on - XMLSchema's model are out of model *(inferred — §14 Q3)*. → - `OUT-OF-MODEL: out-of-layer`. -2. **A SOAP / WSDL parser.** XMLSchema parses the *embedded* `<schema>` - element of a WSDL, but the WSDL parser itself is upstream (e.g. - Apache WSDL4J, Apache CXF). → `OUT-OF-MODEL: out-of-layer`. -3. **`SchemaFactory` of the JDK.** XMLSchema does not wrap or replace - the JDK's `javax.xml.validation.SchemaFactory`. The two are different - abstractions *(inferred — §14 Q3)*. → `OUT-OF-MODEL: out-of-layer`. -4. **`xmlschema-bundle-test` and `w3c-testcases/`.** Integration test - harness and W3C regression data. → `OUT-OF-MODEL: unsupported-component`. -5. **The XML parser bytes-to-DOM step when the caller provides a - pre-parsed `Document` / `Element`.** `XmlSchemaCollection.read(Document)` - and `.read(Element)` accept a caller-parsed DOM; if the caller's - `DocumentBuilderFactory` is not hardened, XMLSchema does not - retroactively secure it *(inferred — §14 Q4)*. → - `OUT-OF-MODEL: trusted-input`. -6. **The remote host reached when following an `xs:include` / - `xs:import`.** The URI is in the input schema; XMLSchema follows it. - What the remote host returns is data XMLSchema then parses, but the - *integrity* of the remote host is not WSS4J's concern *(inferred — - §14 Q5)*. → `OUT-OF-MODEL: adversary-not-in-scope` for malicious - *remote* peer; `VALID` if XMLSchema is tricked into the fetch in the - first place by a path the §7 model permits. -7. **Replacement `URIResolver` / `ExtensionRegistry` implementations.** - Caller-pluggable; a hostile resolver is out of model. -8. **Code shipped in `w3c-testcases/`, `*/src/test/`, `etc/`, - `xmlschema-bundle-test/`.** Test data and supporting fixtures. - → `OUT-OF-MODEL: unsupported-component`. - -## §4 Trust boundaries and data flow - -| # | Transition | Authentication | Authorization | -| --- | --- | --- | --- | -| B1 | Caller → `XmlSchemaCollection.read(InputSource | InputStream | Reader | URL | Document | Element)` | none — caller is trusted | none | -| B2 | `XmlSchemaCollection.read(InputSource, ...)` → JDK `DocumentBuilder` (with `FEATURE_SECURE_PROCESSING=true`) | none | none | -| B3 | Schema parser → `URIResolver.resolveEntity(namespace, schemaLocation, baseUri)` | none | bundled `DefaultURIResolver` does **no host filtering**: it constructs `new URL(new URL(baseUri), schemaLocation)` and hands back an `InputSource` pointing at it | -| B4 | Resolved `InputSource` → `XmlSchemaCollection.read(InputSource, ...)` (recursive) | none | none | -| B5 | `XmlSchemaSerializer.serializeSchema(...)` → JDK `TransformerFactory` (with `FEATURE_SECURE_PROCESSING=true`) | none | none | -| B6 | `XmlSchemaCollection` ctor → `System.getProperty("org.apache.ws.commons.schema.extension_registry")` → `Class.forName()` | none | trusts system properties to be operator-controlled | - -### Reachability preconditions per family - -- **`xmlschema-core` parser** (`XmlSchemaCollection.read(InputSource)`, - `.read(InputStream)`, `.read(Reader)`): in-model when the bytes are - attacker-controllable. XMLSchema sets `FEATURE_SECURE_PROCESSING=true` - on its internal `DocumentBuilderFactory`, but does **not** explicitly - call `setFeature("http://apache.org/xml/features/disallow-doctype-decl", - true)` or `setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, - false)` *(documented: `XmlSchemaCollection.java` line 713)*. - `FEATURE_SECURE_PROCESSING` mitigates XML Schema 1.0's most expensive - expansion cases on Xerces but does not fully disable DTD processing - on every JDK / parser combination *(inferred — §14 Q6)*. -- **`xmlschema-core` URI resolver** (`DefaultURIResolver`): in-model - for SSRF / cross-origin fetch when the input schema is attacker- - controlled and contains an `xs:import schemaLocation="…"`. The - bundled resolver constructs `new URL(...)` and returns an `InputSource`; - the JDK then fetches it on `parse()`. -- **`xmlschema-core` parser fed a pre-parsed DOM** (`read(Document)`, - `read(Element)`): out of model for XXE; the caller's - `DocumentBuilderFactory` decided that. In-model for whatever the - schema-model semantics imply about the DOM contents. -- **`xmlschema-walker`**: in-model when the visitor walks an - attacker-controlled schema. The walker is purely in-memory. -- **`XmlSchemaElementValidator` / `DomBuilderFromSax`**: in-model only - insofar as they parse SAX events the caller hands in. `DomBuilderFromSax` - sets `FEATURE_SECURE_PROCESSING=true` on its - `DocumentBuilderFactory` *(documented: - `xmlschema-walker/src/main/java/.../docpath/DomBuilderFromSax.java` - line 81)*. - -## §5 Assumptions about the environment - -- **JDK**: minimum Java 17 in the 2.3.0 release; earlier releases - supported Java 7 *(documented: `RELEASE-NOTE.txt`)*. -- **JDK XML platform**: assumes a conformant `DocumentBuilderFactory`, - `TransformerFactory`, and SAX parser. The behavior of - `FEATURE_SECURE_PROCESSING` depends on the provider in use; XMLSchema - does not bundle Xerces *(inferred — §14 Q6)*. -- **Network**: the bundled `DefaultURIResolver` will issue HTTP / HTTPS - / `file://` / `jar:` fetches via the JDK URL handlers when an `xs:include` / - `xs:import schemaLocation` is followed. Whether the JVM has a proxy - configured, a `SecurityManager`, or any URL-scheme restriction is - out of XMLSchema's control *(documented: `DefaultURIResolver.java`)*. -- **Filesystem**: caller-supplied paths; XMLSchema does no path - sanitization of `schemaLocation` values that begin with `file://` - *(inferred — §14 Q7)*. -- **Memory**: schemas are held in memory; XMLSchema has no built-in - ceiling on schema-document size, number of imports, or import-graph - depth *(inferred — §14 Q8)*. -- **System properties**: `org.apache.ws.commons.schema.extension_registry` - is consulted at `XmlSchemaCollection` construction time, and the - named class is loaded via `Class.forName()` *(documented: - `XmlSchemaCollection.java` line 361)*. This is a startup-time - privilege escalation surface if the system property can be set by - an untrusted actor *(inferred — §14 Q9)*. -- **Wrap in `AccessController.doPrivileged`**: XMLSchema brackets the - schema parse and the system-property read in - `AccessController.doPrivileged` to be JDK-SecurityManager-compatible - on pre-17 JDKs *(documented: `XmlSchemaCollection.java` lines 376, - 745)*. On JDK 17+ the SecurityManager is deprecated. - -### What XMLSchema does *not* do to its host (negative claims, awaiting maintainer ratification) - -- Opens **no** listening sockets *(inferred — §14 Q10)*. -- Spawns **no** child processes *(inferred — §14 Q10)*. -- Installs **no** signal handlers *(inferred — §14 Q10)*. -- Reads only the documented system property - `org.apache.ws.commons.schema.extension_registry` for - security-relevant decisions; does **not** consume `LD_*`-style - envvars *(inferred — §14 Q10)*. -- Writes **nothing** to the filesystem of its own initiative; the - `XmlSchemaSerializer` writes to the `OutputStream` the caller hands - in *(inferred — §14 Q10)*. - -## §5a Build-time and configuration variants - -XMLSchema is a single Maven artifact set (`xmlschema-core`, -`xmlschema-walker`, `xmlschema-bundle-test`). There are **no -compile-time feature toggles** *(inferred — §14 Q11)*. The runtime -security envelope is shaped by a small set of *runtime extension -points*: - -| Knob | Default | Maintainer stance | Effect | -| --- | --- | --- | --- | -| `org.apache.ws.commons.schema.extension_registry` system property | unset *(documented: `XmlSchemaCollection.java` line 361)* | dev-time customization; if set by an untrusted actor the named class is loaded into the JVM | extension-registry class is `Class.forName`-loaded at `XmlSchemaCollection` ctor time | -| `XmlSchemaCollection.setSchemaResolver(URIResolver)` | `DefaultURIResolver` *(documented: `DefaultURIResolver.java`)* | **maintainer ruling required** — is the documented expectation that production deployments install a *restricted* resolver that refuses untrusted hosts (proposed: **yes, §10**), or is the default resolver supported as production-safe? *(inferred — §14 Q12)* | controls whether `xs:include`/`xs:import` may reach the network | -| `XmlSchemaCollection.setBaseUri(String)` | unset *(documented)* | caller-supplied | base URI against which relative `schemaLocation` values resolve | -| `DocumentBuilderFactory` provider | JDK default (typically Xerces fork) *(inferred — §14 Q6)* | depends on the JDK | shape of XML parsing for `read(InputSource)` / `read(InputStream)` paths | - -### The insecure-default case - -The bundled `DefaultURIResolver` **does follow remote URLs by default**. -The maintainer ruling captured in §14 Q12 will determine whether -"a schema with `<xs:import schemaLocation='http://attacker/'/>` fetched -the URL during parse" is a `VALID` report (production deployments -should be protected by the default) or an `OUT-OF-MODEL: -non-default-build` report (production deployments are *documented* as -required to install a restricting resolver per §10). - -XMLSchema sets `FEATURE_SECURE_PROCESSING=true` on its -`DocumentBuilderFactory` and `TransformerFactory` instances -*(documented: `XmlSchemaCollection.java` line 713, -`XmlSchema.java` line 886)* but does **not** call -`setFeature("http://apache.org/xml/features/disallow-doctype-decl", -true)`. The maintainer ruling on whether XXE / billion-laughs reports -against a callable `read(InputSource)` are `VALID` (the secure-processing -feature is the intended defense and is sufficient) or `MODEL-GAP` -(stricter feature toggles should be set) is captured in §14 Q6. - -## §6 Assumptions about inputs - -### Per-entry-point trust table - -| Entry point | Parameter | Attacker-controllable? | Caller must enforce | -| --- | --- | --- | --- | -| `XmlSchemaCollection.read(InputSource is)` | `is` bytes | **yes** | nothing — XMLSchema sets `FEATURE_SECURE_PROCESSING=true` on its `DocumentBuilderFactory`; caller may need to install a restricting `URIResolver` if the source contains untrusted `xs:include`/`xs:import` | -| `XmlSchemaCollection.read(InputStream in)` | `in` bytes | **yes** | same as above | -| `XmlSchemaCollection.read(Reader r)` | `r` characters | **yes** | same as above | -| `XmlSchemaCollection.read(URL url)` | `url` | caller-supplied | caller controls; XMLSchema fetches via JDK URL handlers | -| `XmlSchemaCollection.read(Document doc)` | `doc` | **yes if doc was parsed from untrusted bytes** | caller's `DocumentBuilderFactory` is responsible for XXE / DTD posture; XMLSchema does not re-parse | -| `XmlSchemaCollection.read(Element el)` | `el` | same as `read(Document)` | same as above | -| `XmlSchemaCollection.setSchemaResolver(URIResolver)` | resolver | caller-supplied | replacing the default is the documented path for production hardening *(inferred — §14 Q12)* | -| `XmlSchemaCollection.setBaseUri(String)` | `baseUri` | **caller-supplied trusted string** | not validated; if attacker can set this they can pivot the import-resolver origin | -| `XmlSchemaCollection.setExtReg(ExtensionRegistry)` | registry | caller-supplied | caller's choice | -| `XmlSchema.write(OutputStream)` / `XmlSchema.write(Writer)` | output sink | caller-supplied | caller's choice; `FEATURE_SECURE_PROCESSING=true` is set on the internal `TransformerFactory` *(documented: `XmlSchema.java` line 886)* | -| `XmlSchemaWalker.walk(XmlSchemaElement)` | walked schema | as untrusted as the schema | none — pure in-memory walking | -| `XmlSchemaElementValidator` (walker module) | SAX events + schema model | as untrusted as both | caller validates / sanitizes outside | -| `DomBuilderFromSax` | SAX events | as untrusted as the source of the events | `FEATURE_SECURE_PROCESSING=true` is set *(documented: `DomBuilderFromSax.java` line 81)* | -| System property `org.apache.ws.commons.schema.extension_registry` | class name | **trusted by §3 item 7** | operator must lock down property setting in shared-JVM deployments | - -### Size / shape / rate - -- No documented limit on schema-document size *(inferred — §14 Q8)*. -- No documented limit on the import-graph depth or breadth - *(inferred — §14 Q8)*. -- No rate limit on URL fetches when following `xs:import` - *(inferred — §14 Q12)*. - -## §7 Adversary model - -### Actors - -| Actor | In scope? | Capabilities | -| --- | --- | --- | -| **Producer of the input schema bytes** | **yes** | full byte control of the schema; can include `<xs:import schemaLocation="…"/>` pointing anywhere | -| **Producer of an imported schema** at a URL the parent schema references | **yes** | returns a malicious downstream `.xsd` after the parent triggers the fetch | -| **Network attacker on the path between XMLSchema and an HTTP `schemaLocation`** | **partial** *(inferred — §14 Q13)* — TLS is the JDK URL handler's concern; XMLSchema does no certificate pinning; if the operator allowed plain `http://` fetches a network attacker can substitute | -| **In-process callers (the embedding application)** | **out of scope** | trivially full control | -| **Owner of the host filesystem** | **out of scope** | by construction | -| **Author of a hostile `URIResolver` / `ExtensionRegistry`** | **out of scope** *(§3 item 7)* | -| **Author of system-property setting on JVM startup** | **out of scope** if operator-controlled; in scope only when an untrusted actor can set `org.apache.ws.commons.schema.extension_registry` *(inferred — §14 Q9)* | -| **Co-tenant on the same JVM** (multi-app servlet container) | **out of scope** *(inferred — §14 Q14)* | -| **Side-channel observer** | **out of scope** *(inferred — §14 Q14)* | -| **Quantum adversary** | **out of scope** | - -## §8 Security properties the project provides - -### P1 — Memory-safety on the schema parse, given a JDK conformant to §5 - -- **Condition**: bytes arrive through one of the `read(...)` paths; the - JDK platform supplies a working `DocumentBuilder`; XMLSchema sets - `FEATURE_SECURE_PROCESSING=true` on its factories. -- **Violation symptom**: a crafted `.xsd` causes XMLSchema's *own* - code (not the JDK XML parser) to throw an unhandled exception that - is **not** a `XmlSchemaException` / `ParserConfigurationException` / - `IOException` / `SAXException` — i.e. crashes outside the documented - failure mode. Memory corruption is JVM-level and not a Java-side - defect. -- **Severity**: typically **correctness-only** (Java does not have - memory corruption); `VALID-HARDENING` if the symptom is an - unhandled `RuntimeException` that the caller cannot catch through - the documented surface *(inferred — §14 Q15)*. -- *(inferred — §14 Q15)* - -### P2 — Some mitigation of "expensive" schema expansion via `FEATURE_SECURE_PROCESSING=true` on the internal `DocumentBuilderFactory` and `TransformerFactory` - -- **Condition**: the JDK XML provider honors `FEATURE_SECURE_PROCESSING`; - XMLSchema's `read(InputSource | InputStream | Reader)` paths take the - internal `DocumentBuilderFactory`. -- **Violation symptom**: a billion-laughs / quadratic-expansion attack - parses to completion (no `SAXException`, no `XmlSchemaException`), - the host JVM runs out of memory or pegs the CPU. -- **Severity**: **maintainer ruling required** — see §14 Q6. -- *(documented: `XmlSchemaCollection.java` line 713, - `XmlSchema.java` line 886, `DomBuilderFromSax.java` line 81)* - -### P3 — Round-trip parse → model → serialize consistency - -- **Condition**: a well-formed schema is parsed and serialized; the - serializer's `TransformerFactory` is configured with - `FEATURE_SECURE_PROCESSING=true`. -- **Violation symptom**: a serialized schema is not a valid XSD, or - differs semantically from the parsed one *(inferred — §14 Q16)*. -- **Severity**: **correctness-only**; not security-relevant unless the - divergence creates a security-meaningful misinterpretation downstream - *(inferred — §14 Q16)*. -- *(inferred — §14 Q16)* - -### P4 — `xmlschema-walker` deterministic visit order - -- **Condition**: visitor pattern is invoked on the in-memory model. -- **Violation symptom**: visit order varies across invocations on the - same model, breaking caller assumptions. -- **Severity**: **correctness-only**. -- *(inferred — §14 Q17)* - -## §9 Security properties the project does *not* provide - -State each plainly so a triager can route an inbound report to the -matching disclaimer. - -- **No SSRF defense on `xs:import`/`xs:include schemaLocation` URLs.** - The bundled `DefaultURIResolver` constructs a `URL` from the parent - schema's base URI plus the schema-location value and returns an - `InputSource` pointing at it. The JDK then fetches it on parse. - XMLSchema applies *no* allowlist, *no* protocol restriction, and *no* - host filtering of any kind. The caller is responsible for installing - a restricting `URIResolver` if the input schema is attacker-controlled - *(documented: `DefaultURIResolver.java`)*. -- **No XXE / DTD defense beyond `FEATURE_SECURE_PROCESSING=true`.** - XMLSchema does **not** call - `setFeature("http://apache.org/xml/features/disallow-doctype-decl", - true)`, does **not** set - `setFeature("http://xml.org/sax/features/external-general-entities", - false)`, and does **not** unset the analogous `XMLInputFactory` - properties. Whether this is sufficient depends on the JDK XML - provider in use *(inferred — §14 Q6)*. -- **No defense when the caller passes in a pre-parsed `Document` or - `Element`.** The hardening on the internal `DocumentBuilderFactory` - is moot — the caller's parser produced the DOM *(documented: - `XmlSchemaCollection.read(Document)` / `.read(Element)`)*. -- **No bound on schema-document size, import-graph depth, or import - fan-out.** A schema that includes thousands of imports, or imports - recursively, will be processed to completion or until JVM resources - are exhausted *(inferred — §14 Q8)*. -- **No protection of imported schemas at rest.** Schemas pulled from - HTTP are fetched in cleartext if the URL is `http://`. The caller - must use TLS-protected URLs or install a restricting resolver - *(documented: `DefaultURIResolver.java`)*. -- **No defense against the system-property-controlled - `ExtensionRegistry` being a malicious class.** The named class is - loaded into the running JVM and instantiated. -- **No data-at-rest protection.** Schemas serialized by - `XmlSchemaSerializer` are plain XML on whatever sink the caller - provided. -- **No constant-time guarantees.** XMLSchema does not deal with - secrets. -- **No defense against side-channel observation.** -- **No quantum resistance.** - -### False-friend properties (call out separately) - -- **`FEATURE_SECURE_PROCESSING=true` looks like full XXE defense, - but it is not.** On many JDK XML providers it imposes safe limits on - entity expansion (XML-1.0 quadratic-blow-up bounds) but does **not** - disable external entities or DTD processing entirely. Strict XXE - defense in Java requires the explicit - `setFeature("http://apache.org/xml/features/disallow-doctype-decl", - true)` plus `setFeature("http://xml.org/sax/features/external-general-entities", - false)` and `setFeature("http://xml.org/sax/features/external-parameter-entities", - false)`. XMLSchema does the *FEATURE_SECURE_PROCESSING* hardening - but not the explicit XXE flags *(inferred — §14 Q6)*. -- **`DefaultURIResolver` looks like a sandbox, but it isn't.** It is - the *bundled* resolver; its job is to resolve `xs:include`/`xs:import`, - not to filter destinations. -- **`XmlSchemaCollection.setBaseUri(String)` looks like a chroot for - imports, but it isn't.** If the importing schema sets an absolute - URL in `schemaLocation`, the base URI is irrelevant — the absolute - URL wins *(inferred — §14 Q18)*. -- **`xmlschema-walker.XmlSchemaElementValidator` looks like a - document validator, but it is a tooling helper.** It is not a - drop-in replacement for `javax.xml.validation.Validator` and does - not promise XSD-1.0 / 1.1 conformance *(inferred — §14 Q3)*. - -### Well-known attack classes XMLSchema does not single-handedly defend against - -- **XXE / external-entity disclosure** via DTD-enabled JDK XML - providers — depends on the JDK XML factory and on caller-side - hardening when `read(Document)` / `read(Element)` is used. -- **SSRF via `xs:import schemaLocation`** — see §9 first bullet. -- **Billion-laughs / quadratic blowup** — partially mitigated by - `FEATURE_SECURE_PROCESSING=true`, but not universally. -- **Schema-amplification DoS** — a deeply nested or heavily-recursive - schema can exhaust memory. -- **Confused-deputy fetch via untrusted `baseUri` + relative - `schemaLocation`** — the operator-supplied base URI is trusted. - -## §10 Downstream responsibilities - -The embedding Java application **must**: - -1. Decide whether the schema bytes being parsed are - attacker-controllable. If yes, install a restricting - `URIResolver` via `XmlSchemaCollection.setSchemaResolver(...)` that - refuses arbitrary `http://` / `https://` / `file://` / `jar:` / - `ftp:` URLs. The bundled `DefaultURIResolver` does not filter - *(documented: `DefaultURIResolver.java`)*. -2. When passing a pre-parsed `Document` / `Element` into - `XmlSchemaCollection.read(...)`, use a `DocumentBuilderFactory` - hardened against XXE — specifically with `disallow-doctype-decl=true` - and external-entity processing disabled. -3. When using the `read(InputSource | InputStream | Reader)` path - against attacker-controlled bytes, verify the JDK XML provider in - use treats `FEATURE_SECURE_PROCESSING=true` as sufficient defense - *(inferred — §14 Q6)*. -4. Bound the maximum allowable schema size and the maximum import-graph - depth at the *caller* level. XMLSchema imposes no such limit - *(inferred — §14 Q8)*. -5. Set `org.apache.ws.commons.schema.extension_registry` only at JVM - startup from a trusted source; do not allow untrusted actors to set - it. -6. Set `XmlSchemaCollection.setBaseUri(...)` from an operator-trusted - string, not from anywhere an attacker can influence. -7. Run on a release-supported branch (currently 2.3.0 line) - *(documented: `RELEASE-NOTE.txt`)*. - -## §11 Known misuse patterns - -- **Passing an attacker-controlled `.xsd` to - `XmlSchemaCollection.read(InputSource)` with the - `DefaultURIResolver` in place.** The schema's `xs:include` / - `xs:import` are followed unrestricted, leading to SSRF / cross-origin - fetch / `file://` disclosure *(documented: - `DefaultURIResolver.java`)*. The fix is per §10 item 1. -- **Trusting `read(Document)` / `read(Element)` to inherit XXE - defenses from XMLSchema.** XMLSchema does not re-parse; the caller's - factory chose the posture. -- **Setting `org.apache.ws.commons.schema.extension_registry` from a - servlet-context init parameter or a config file an end user can - influence.** The named class is loaded and instantiated at - `XmlSchemaCollection` construction. -- **Treating `XmlSchemaElementValidator` (walker module) as a - conformant XSD validator.** It is a tooling helper; conformant - document validation against an XSD requires the JDK - `javax.xml.validation.Validator` *(inferred — §14 Q3)*. -- **Using the same `XmlSchemaCollection` instance across thread - boundaries without external synchronization.** XMLSchema does not - document its threading model *(inferred — §14 Q19)*. -- **Fetching imported schemas over plain `http://` from a remote - registry.** The operator should restrict to `https://` or pin to a - trusted mirror. -- **Letting the parsed schema's base URI be derived from the - attacker-controlled bytes** (e.g. an absolute `xmlns:tns` URL the - attacker provided) and then walking the URI graph from that base. - -## §11a Known non-findings (recurring false positives) - -This section is the highest-leverage input for automated agentic -security scans. Each entry: tool symptom, why it is safe under the -model, the section that licenses the call. - -- **"`new URL(baseUri, schemaLocation).openConnection()` — SSRF risk in - `DefaultURIResolver`."** Bundled behavior, explicitly documented as - defaulted unrestricted; operator must install a restricting resolver - per §10 item 1. → `OUT-OF-MODEL: trusted-input` *(if the maintainer - rules at Q12 that the default is dev/test)*, or `VALID-HARDENING` - *(if the maintainer rules the default is supported)*. -- **"`DocumentBuilderFactory.newInstance()` not setting - `disallow-doctype-decl`."** Investigated under §14 Q6 — XMLSchema - *does* set `FEATURE_SECURE_PROCESSING=true` which is the - XSLT-conventional hardening lever. → `KNOWN-NON-FINDING` if the Q6 - ruling lands "secure-processing is sufficient", else `VALID-HARDENING`. -- **"`Class.forName(System.getProperty(...))` is dynamic-class-loading."** - Documented extension point; the system property is the trust gate - *(documented: `XmlSchemaCollection.java` line 361)*. → `OUT-OF-MODEL: - trusted-input`. -- **"`AccessController.doPrivileged` deprecated in JDK 17."** JDK - compatibility wrapper; deprecation is informational on JDK 17 and - required on pre-17 JDKs. → `KNOWN-NON-FINDING`. -- **"Hardcoded password in test resources."** No such resource exists - in this repo; if a tool flags `w3c-testcases/` data, it is W3C - conformance suite data *(documented: top-level `w3c-testcases/`)*. → - `OUT-OF-MODEL: unsupported-component`. -- **"`XmlSchemaSerializer` uses identity Transformer — could be - exploited via XSLT injection."** Identity transform; no stylesheet - reachable from input *(documented: `XmlSchema.java` line 886)*. → - `KNOWN-NON-FINDING`. -- **"`URLConnection.getInputStream()` without timeout."** True; - XMLSchema does no read-timeout on fetched imports - *(inferred — §14 Q12)*. → `VALID-HARDENING` if Q12 rules the - default-resolver is production-safe; otherwise documented as a §10 - responsibility. -- **"Path traversal via `XmlSchemaCollection.setBaseUri()`."** Caller- - supplied trusted string per §6. → `OUT-OF-MODEL: trusted-input`. -- **"Schemas in `w3c-testcases/` contain wide-open DTDs."** W3C - conformance suite, used as input to unit tests *(documented: - top-level `w3c-testcases/`)*. → `OUT-OF-MODEL: unsupported-component`. -- **"`ExtensionRegistry` is `Class.forName`-loaded — RCE."** Trusted - system property per §3 item 7. → `OUT-OF-MODEL: trusted-input`. -- **"`XmlSchemaCollection.read(URL)` follows `file://` to read - arbitrary local files."** Caller passed the URL; trusted entry point. - → `OUT-OF-MODEL: trusted-input`. - -## §12 Conditions that would change this model - -Revise this document when any of the following lands: - -- A change in the default `URIResolver` behavior — e.g. adding host - filtering, refusing non-HTTPS, or adding read/connect timeouts. -- A change in the default `FEATURE_SECURE_PROCESSING=true` hardening — - e.g. adding explicit `disallow-doctype-decl=true`. -- A new public entry point on `XmlSchemaCollection` that accepts new - input shapes. -- A new built-in resource limit on schema size or import-graph depth. -- A new feature flag in `xmlschema-walker`'s validator that turns it - into a claimed-conformant document validator. -- An upgrade of the supported JDK minimum that changes the JDK XML - provider behavior. -- A change in the system-property contract for `ExtensionRegistry`. -- A vulnerability report that cannot be cleanly routed to one of the - §13 dispositions — evidence the model has a gap. - -## §13 Triage dispositions - -A report against XMLSchema receives exactly one of the following: - -| Disposition | Meaning | Licensed by | -| --- | --- | --- | -| `VALID` | Violates a §8 property via an in-scope §7 adversary using an in-scope §6 input. | §8, §6, §7 | -| `VALID-HARDENING` | No §8 property violated, but a §11 misuse pattern can be made harder to fall into by code change. Typically no CVE. | §11 | -| `OUT-OF-MODEL: trusted-input` | Requires attacker control of a §6 parameter the model marks trusted (caller-supplied `Document` / `Element`, caller-supplied `baseUri`, caller-supplied `URIResolver`, JVM system property, etc.). | §6 | -| `OUT-OF-MODEL: adversary-not-in-scope` | Requires a §7 actor the model excludes (in-process caller, hostile `URIResolver`, operator). | §7 | -| `OUT-OF-MODEL: unsupported-component` | Lands in `w3c-testcases/`, `*/src/test/`, `etc/`, `xmlschema-bundle-test/`. | §3 items 4, 8 | -| `OUT-OF-MODEL: non-default-build` | Only manifests under a §5a configuration the maintainer rules dev/test (e.g. an unsafe custom `URIResolver`). | §5a | -| `OUT-OF-MODEL: out-of-layer` | Concerns a *document* validation step delegated to `javax.xml.validation.Validator`, or a WSDL parser upstream. | §3 items 1–3 | -| `BY-DESIGN: property-disclaimed` | Concerns a §9 property the project explicitly does not provide (no SSRF defense, no XXE defense beyond secure-processing, no resource ceiling). | §9 | -| `KNOWN-NON-FINDING` | Matches a §11a recurring false positive. | §11a | -| `MODEL-GAP` | Cannot be cleanly routed to any of the above — triggers §12 model revision. | §12 | - -## §14 Open questions for the maintainers - -Every *(inferred)* tag in the body maps to one of these. Proposed -answers are inline; please confirm, correct, or strike. - -### Wave 1 — security policy + meta - -**Q1.** XMLSchema does not currently ship an in-repo `SECURITY.md`. -The de facto policy is "report via `https://www.apache.org/security/`". -Should the project (a) adopt a `SECURITY.md` that names a supported- -branch matrix (proposed), (b) leave reporting to the foundation page -only, or (c) defer to the Webservices PMC's umbrella policy? *(meta)* - -**Q2.** Confirm that XMLSchema's threat model treats the project as -**a schema-modeling library that performs network IO when resolving -imports**, not a generic XML parser (proposed: **yes**). *(maps to §2)* - -**Q3.** `xmlschema-walker.XmlSchemaElementValidator` — proposed -position is "tooling helper, not a claimed-conformant document -validator". Confirm? *(maps to §3 item 1, §9 false-friend, §11)* - -### Wave 2 — XXE / DTD posture (highest leverage) - -**Q4.** When `read(Document)` / `read(Element)` is the entry point, -proposed position is "the caller's DOM was already parsed; XMLSchema -makes no XXE claim about it" (`OUT-OF-MODEL: trusted-input`). Confirm? -*(maps to §3 item 5, §13)* - -**Q5.** When the URI resolver follows an `xs:import schemaLocation`, -proposed position is "what the remote host returns is parsed by the -*caller-installed* parser path; the *fetch* itself is the -attacker-influenced action and §9 disclaims SSRF defense" (`§9`). -Confirm? *(maps to §3 item 6, §9)* - -**Q6.** **The big XXE question.** XMLSchema sets -`FEATURE_SECURE_PROCESSING=true` on its internal -`DocumentBuilderFactory` and `TransformerFactory`. It does **not** set -`disallow-doctype-decl=true`, `external-general-entities=false`, or -`external-parameter-entities=false`. The maintainer ruling is required -on: - -- (a) Is `FEATURE_SECURE_PROCESSING=true` considered the sufficient - defense (so XXE reports against - `XmlSchemaCollection.read(InputSource)` are `KNOWN-NON-FINDING`)? -- (b) Or are explicit `disallow-doctype-decl=true` and external-entity - toggles the supported posture, with the current state being a - `VALID-HARDENING` to be addressed? - -Proposed answer: **(b), with a future-PR to add the explicit toggles -and document `FEATURE_SECURE_PROCESSING=true` as the present -inheritance from JDK Xerces.** *(maps to §5a, §8 P2, §9, §11a)* - -### Wave 3 — URI resolver / SSRF - -**Q7.** Confirm that XMLSchema does *not* sanitize `schemaLocation` -when it begins with `file://`, `jar:`, etc. (proposed: no sanitization; -operator's `URIResolver` is the gate). *(maps to §5, §11a)* - -**Q8.** No documented bound on schema-document size or -import-graph depth (proposed: confirm "no bound, operator's -responsibility to cap"). Are there *de facto* bounds inside XMLSchema? -*(maps to §5, §9, §10 item 4)* - -**Q9.** `org.apache.ws.commons.schema.extension_registry` system -property: confirm that production deployments are expected to set -it (if at all) at JVM startup from a trusted source — i.e. an -untrusted-actor-set value is `OUT-OF-MODEL: trusted-input` (proposed). -*(maps to §5a, §11)* - -**Q10.** Negative-side inventory in §5: XMLSchema opens **no** -sockets *other than what the JDK URL handler does when following an -import*; spawns **no** processes; installs **no** signal handlers; -reads **only** the documented system property; writes **nothing** of -its own initiative. Confirm? *(maps to §5)* - -**Q11.** Build-time variants: confirm there are no compile-time feature -toggles; the security envelope is shaped only by runtime extension -points (proposed). *(maps to §5a)* - -**Q12.** **The big URI-resolver question.** The bundled -`DefaultURIResolver` follows `http://` / `https://` / `file://` / -`jar:` URLs without filtering. Is this: - -- (a) "Supported production posture" — a report that an attacker - schema's `<xs:import schemaLocation='http://attacker/'/>` triggered - a fetch is `VALID`? -- (b) "Dev/test default; operators are documented as required to - install a restricting resolver per §10" — same report is - `OUT-OF-MODEL: non-default-build`? - -Proposed: **(b)** with a clarification in `README.txt` and/or -`SECURITY.md` that production deployments handling untrusted schema -bytes must install a restricting `URIResolver`. *(maps to §5a, §9, -§10 item 1, §11a, §13)* - -### Wave 4 — adversary model, edge cases - -**Q13.** Network attacker on `http://` import fetches: proposed -position is "operator is documented as required to use `https://` -or a restricting resolver; TLS validity is the JDK URL handler's -concern". Confirm? *(maps to §7)* - -**Q14.** Co-tenant in shared JVM and side-channel observers: out of -scope (proposed)? *(maps to §7)* - -**Q15.** §8 P1 (memory safety on parse): proposed wording "any -`RuntimeException` outside the documented `XmlSchemaException`/ -`ParserConfigurationException`/`IOException`/`SAXException` family is -`VALID-HARDENING`". Confirm? *(maps to §8 P1)* - -**Q16.** §8 P3 (round-trip parse → model → serialize consistency): -do you claim this as a security property, or only as a correctness -one? Proposed: correctness-only unless the divergence creates a -security-meaningful downstream misinterpretation, in which case it is -a `VALID-HARDENING`. *(maps to §8 P3)* - -**Q17.** §8 P4 (walker deterministic visit order): is the visit order -documented? Proposed: correctness-only. *(maps to §8 P4)* - -**Q18.** `setBaseUri(...)` semantics: if an `<xs:import -schemaLocation='http://absolute/'/>` is encountered, the base URI is -ignored. Confirm? *(maps to §9 false-friend)* - -**Q19.** Threading model: is `XmlSchemaCollection` documented as -thread-safe? Proposed: not thread-safe; callers synchronize. *(maps to -§11)* - -### Wave 5 — coexistence & publication - -**Q20.** This document should be hosted in-repo at -`docs/security/threat-model.md` (proposed) or on -`ws.apache.org/xmlschema/`? *(meta)* - -**Q21.** §11a known-non-findings is thin (~11 patterns). Could the -XMLSchema PMC populate from the JIRA "not a bug" / "wontfix" -closures (`XMLSCHEMA-*` tickets)? Concrete asks: 3–5 patterns the PMC -sees recur in inbound reports. *(meta — §11a)* - -**Q22.** What kind of change to XMLSchema should trigger a revision -(proposed list in §12 — confirm or correct)? *(meta — §12)* - ---- - -## Appendix: SECURITY.md / website → §x back-map - -XMLSchema does not ship a `SECURITY.md`. The threat-model sources are -the in-repo `README.txt`, the `RELEASE-NOTE.txt`, and the JavaDoc / -source comments. The project website is -<https://ws.apache.org/xmlschema/>. - -| Source | Claim | Lands in | -| --- | --- | --- | -| `README.txt` | "lightweight schema object model that can be used to manipulate and generate XML schema representations" | §1, §2 intended use | -| `RELEASE-NOTE.txt` (2.3.0) | Java 17 minimum, Java 7 dropped | §5 environment | -| `xmlschema-core/src/main/java/org/apache/ws/commons/schema/XmlSchemaCollection.java` line 361 | `org.apache.ws.commons.schema.extension_registry` system property loaded via `Class.forName` | §5a, §6, §11 | -| `XmlSchemaCollection.java` line 713 | `docFac.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, TRUE)` | §5a, §8 P2 | -| `XmlSchemaCollection.java` line 745 | `AccessController.doPrivileged` wrapper for the SAX parse | §5 | -| `XmlSchema.java` line 886 | `trFac.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, TRUE)` for serializer | §5a, §8 P2 | -| `xmlschema-core/src/main/java/.../resolver/DefaultURIResolver.java` | URL composed from `baseUri` + `schemaLocation`; no filtering | §3 item 7, §9 SSRF disclaim, §10 item 1, §11 first bullet | -| `xmlschema-core/src/main/java/.../resolver/URIResolver.java` | Resolver interface — caller-pluggable | §2 caller-roles, §10 item 1 | -| `xmlschema-walker/src/main/java/.../docpath/DomBuilderFromSax.java` line 81 | `factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, TRUE)` | §5a, §8 P2 |
