Piotr Karwasz created CXF-9248:
----------------------------------

             Summary: Delegate JAXP parser configuration to Apache Commons 
Secure XML
                 Key: CXF-9248
                 URL: https://issues.apache.org/jira/browse/CXF-9248
             Project: CXF
          Issue Type: Improvement
            Reporter: Piotr Karwasz


CXF configures JAXP factories at roughly 58 places in {{src/main/java}}. Each 
one re-implements the same hardening by hand: {{FEATURE_SECURE_PROCESSING}}, 
then {{ACCESS_EXTERNAL_DTD}} / {{ACCESS_EXTERNAL_SCHEMA}} / 
{{ACCESS_EXTERNAL_STYLESHEET}}, each in its own try/catch that logs a warning 
and carries on.

Against the JDK's own implementations that hardening is tight, and this issue 
is not a claim otherwise. The problem is the {{catch}} block.

{{ACCESS_EXTERNAL_\*}} are JAXP 1.5 *properties*. An implementation that does 
not recognise one throws an exception; CXF logs and continues with an 
unhardened factory. The posture therefore depends on which JAXP implementation 
wins on the classpath — a decision the integrator makes, not CXF. Applications 
built on CXF are large, and a transitively pulled Xerces, Xalan or Saxon is an 
ordinary occurrence rather than a hypothetical. Every one of those ~58 sites 
fails open in that case, silently.

There is a second gap that is not about foreign implementations at all: a 
registered {{LSResourceResolver}} or {{URIResolver}} takes precedence over 
{{ACCESS_EXTERNAL_*}} by design. Where CXF must resolve something and so 
installs a resolver, the properties it also set no longer decide anything. This 
is documented JAXP behavior, and it means the two mechanisms cannot be combined 
the way the current code assumes.

Apache Commons Secure XML installs its floor as a *resolver* instead — a 
mechanism every implementation is required to support — with inverted 
semantics: returning {{null}} denies, returning non-{{null}} allows that one 
reference. The floor cannot be removed by a caller-supplied resolver, and 
unresolved references are answered with empty content rather than fetched. The 
same posture then holds on whatever implementation the deployment actually has.

We have driven the migration through on a branch and fixed the sharp edges 
upstream rather than working around them in CXF; the 1.0.1 changelog records 
them:

[https://github.com/apache/commons-secure-xml/blob/main/src/changes/changes.xml]

The one worth naming here, because CXF depends on it: 
{{SAXTransformerFactory.newXMLFilter(Templates)}} in Commons Secure XML accepts 
foreign {{Templates}}, which most TrAX implementations reject. 
{{XSLTJaxbProvider}} needs exactly that.

The branch is 12 commits, 78 files, +2,457/-445, split as the two parts below. 
The additional lines are mostly due to OASIS Catalog files and don't increase 
the complexity of CXF:

https://github.com/ppkarwasz/cxf/tree/feat/use-commons-secure-xml

h2. Part 1 — Block external XML access by default, replacing the manual 
hardening

Replace the per-site hardening with factories from Commons Secure XML, so the 
floor holds regardless of the JAXP implementation on the classpath and cannot 
be dismantled by a caller-supplied resolver.

* {{SecureXxxFactory.newInstance()}} at the call site. No CXF wrapper class — 
adding one would put us back in the business of configuring parsers.
* CXF's own tightenings compose on top and are kept: {{disallow-doctype-decl}} 
in {{DOMUtils}}, and in {{StaxUtils}} {{SUPPORT_DTD=false}}, 
{{IS_SUPPORTING_EXTERNAL_ENTITIES=false}}, the throwing {{XMLResolver}} and the 
Woodstox depth/element limits. The library reserves its settings against 
*loosening* only, so tightening is sanctioned rather than tolerated.
* {{SAFE_INPUT_FACTORY}} and the {{createWoodstoxFactory()}} fallback chain in 
{{StaxUtils}} are untouched; the thread-safe-implementation fast path still 
works.

h2. Part 2 — Unblock the paths that legitimately need an external resource

Denying everything is only half the work: CXF has roughly ten places where 
resolving an external schema, stylesheet or import is the feature. These are 
opted back in *per reference*, through one resolver that already knows CXF's 
policy.

{{org.apache.cxf.catalog.XmlCatalogResolver}} — one class implementing all four 
JAXP resolver interfaces ({{EntityResolver}}, {{LSResourceResolver}}, 
{{javax.xml.transform.URIResolver}}, {{XMLResolver}}), answering from the bus 
{{OASISCatalogManager}} first and a local resource second. {{Remote.DENIED}} is 
the default; {{Remote.ALLOWED}} is the explicit opt-in for developer-trusted 
input (tools, codegen), which makes every remote-capable site greppable.

The resolver is load-bearing, not decoration: {{SecureSchemaFactory}} alone 
fails where {{SecureSchemaFactory}} + {{XmlCatalogResolver}} succeeds.

h3. 2a — URIResolver: coarse-grained policy and archive schemes

{{org.apache.cxf.resource.URIResolver}} is where scheme policy belongs, and it 
now owns it. Two parts, one done and one proposed:

*Done.* {{schemeOf(String)}} unwraps the archive schemes — {{jar:}}, 
{{wsjar:}}, {{zip:}} — down to the scheme that actually decides, because those 
are wrappers, not protocols. Only the innermost one counts. The previous check 
looked at the outermost, so {{jar:gopher://...}} passed as "jar"; it no longer 
does.

*Proposed, not implemented.* {{getAllowedSchemes()}} can currently only widen 
the allowlist: it seeds from {{DEFAULT_ALLOWED_URL_SCHEMES}} and only 
{{add()}}s the configured entries, so no configuration can take a scheme away. 
A coarse-grained selector — {{ALL}} / {{LOCAL}} / {{CLASSPATH_ONLY}} — would 
give integrators one comprehensible knob instead of a scheme list that cannot 
restrict anything.

h3. 2b — Declare the bundled schemas in catalogs

Most of what CXF resolves it already ships. The branch adds 23 
{{META-INF/jax-ws-catalog.xml}} files ({{core}} plus 22 modules) declaring each 
module's bundled schemas by namespace and by system id, so 
{{OASISCatalogManager}} answers an {{xs:import}} from the JAR instead of its 
canonical {{http://}} location. Catalogs chain, which matches how the modules 
depend on each other — {{wsrm.xsd}} importing {{addressing.xsd}} from a 
different JAR resolves through the other module's catalog.

Three prerequisites fell out of this and are on the branch:

* {{xml-resolver}} is no longer {{<optional>true</optional>}}. While optional 
it was absent at runtime in most modules, which made {{OASISCatalogManager}} 
silently inert and {{setCatalogLocation}} a no-op. Worth knowing independently 
of this issue.
* {{OASISCatalogManager}} references {{Catalog}} and {{CatalogResolver}} 
directly now that the dependency is guaranteed, instead of reflectively behind 
{{catch (Throwable)}}.
* {{loadCatalogs(ClassLoader, String)}} is used rather than {{loadCatalog}}, so 
all same-named catalogs on the classpath are picked up instead of whichever one 
happens to be first.

*Optional follow-up.* {{xml-resolver}} 1.2 is a 2006 artifact, still supported 
by the Xerces PMC. Swapping it for xmlresolver.org would be a separate, 
independently reviewable change; nothing above depends on it.




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to