This is an automated email from the ASF dual-hosted git repository.
papegaaij pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/wicket.git
The following commit(s) were added to refs/heads/master by this push:
new 17714c5d43 Document that OriginResourceIsolationPolicy is out of scope
17714c5d43 is described below
commit 17714c5d437c7e4cdd21fad68aee956d5a1b2423
Author: Emond Papegaaij <[email protected]>
AuthorDate: Mon Aug 31 12:47:36 2026 +0200
Document that OriginResourceIsolationPolicy is out of scope
Wicket ships two IResourceIsolationPolicy implementations and only one of
them
carries the boundary the security model claims.
FetchMetadataResourceIsolationPolicy, added in 9.1.0, reads the Sec-Fetch-*
headers, which the browser sets and page content can neither forge nor
remove.
OriginResourceIsolationPolicy is the older mechanism, kept so the listener
still
has something to say about a client that sends no such headers, and its
limits
are inherent in the two headers it reads: Origin is absent on the plain GET
a
Link or an Ajax behaviour uses, so the check falls back to Referer, which
the
referring document suppresses at will; a missing source is UNKNOWN rather
than a
rejection, so the verdict passes to setUnknownOutcomeAction, which
deployments
relax because legitimate traffic arrives the same way; the policy ignores
the
RequestType, so it cannot separate a render from a listener invocation; and
it
compares against the container-reported host while addAcceptedOrigin matches
subdomains.
The scope section therefore says we neither assess nor harden a way past
that
policy, and names constructing the listener with the fetch-metadata policy
alone
as the way to leave the fallback out. The class is not deprecated because
it is
still a reasonable fallback for a legacy client, where the alternative is to
refuse the request outright.
The exclusion is drawn at the header rather than at the class, because the
origin
policy is in the default policy list. The policies are checked in order and
the
first outcome other than UNKNOWN decides, so a request carrying
Sec-Fetch-Site is
always settled by the fetch-metadata policy and never reaches the origin
policy,
and another document cannot make a browser omit that header. A request with
Sec-Fetch-Site present that is nonetheless decided by the origin policy
stays in
scope, as does ResourceIsolationRequestCycleListener itself.
Wicket 8.x has no supported resource isolation implementation. The
IResourceIsolationPolicy mechanism arrived in 9.1.0; the only cross-origin
check
8.x offers is CsrfPreventionRequestCycleListener, which reads the same two
headers with the same limits. 9.x deprecates it in favour of the resource
isolation listener, so it is already out of scope there under the
deprecated code
section, and it is not deprecated on 8.x only because that line has nothing
to
migrate to. An 8.x application needing anything stronger has to upgrade or
write
the check itself; the fetch-metadata policy will not be backported.
Two statements elsewhere in the document needed adjusting. The section on
the
trusted host said the container-reported host is an input to Wicket's own
request-forgery defences, which overstates it now that the only policy
consulting
the host is out of scope, and the section on invoking a listener from
another
origin did not say which policy carries the boundary it describes.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---
SECURITY.md | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 80 insertions(+), 4 deletions(-)
diff --git a/SECURITY.md b/SECURITY.md
index b0a8a5e2eb..221a09de91 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -65,8 +65,8 @@ guide on our wiki, which links the guides for the earlier
lines.
## Scope
-The table above says which release lines receive security fixes. Two categories
-of code inside those lines sit outside this process.
+The table above says which release lines receive security fixes. Three
+categories of code inside those lines sit outside this process.
### Deprecated code is out of scope
@@ -99,6 +99,74 @@ there is nothing to migrate to. Deprecation is the fix in
that case, and the cod
is out of scope on the same footing as any other deprecated code. The javadoc
says which of the two applies, so it is clear before reporting.
+### `OriginResourceIsolationPolicy` is out of scope
+
+Wicket ships two `IResourceIsolationPolicy` implementations, and only one of
them
+carries the boundary described in
+[Another origin may not invoke a
listener](#another-origin-may-not-invoke-a-listener).
+
+`FetchMetadataResourceIsolationPolicy`, added in 9.1.0, is the supported one.
It
+reads the `Sec-Fetch-*` request headers, which the browser sets and which page
+content can neither forge nor remove.
+
+`OriginResourceIsolationPolicy` is the older mechanism, kept so that
+`ResourceIsolationRequestCycleListener` still has something to say about a
client
+that does not send those headers. It compares the `Origin` and `Referer`
headers
+against the requested URL, and the limits of that approach are inherent in the
+headers rather than in the implementation:
+
+- **Neither header need arrive.** Browsers send `Origin` on a form submit, but
+ not on a plain GET — which is how a `Link` and most Ajax behaviours invoke
their
+ listener. There the check falls back to `Referer`, and `Referer` is
suppressed
+ by the referring page's own `Referrer-Policy`, by `rel="noreferrer"`, and by
an
+ HTTPS-to-HTTP downgrade. The document on the other origin chooses its own
+ referrer policy, so it is the party deciding whether a source header reaches
us
+ at all.
+- **A missing source is not a rejection.** With no usable header the outcome is
+ `UNKNOWN` and the request is settled by
+ `ResourceIsolationRequestCycleListener#setUnknownOutcomeAction`, which
defaults
+ to aborting — but which deployments relax precisely because legitimate
traffic
+ also arrives without the headers.
+- **It cannot express the boundary.** The policy ignores the `RequestType`, so
it
+ cannot distinguish a page render, which may legitimately be a top-level
+ navigation from another site, from a listener invocation, which may not.
+- **Its idea of the target is the trusted host.** It builds the URI it compares
+ against from the container-reported host, port and scheme (see
+ [Wicket trusts the container-reported host, port and
scheme](#wicket-trusts-the-container-reported-host-port-and-scheme)),
+ and `addAcceptedOrigin` matches subdomains, so accepting a domain accepts
every
+ host beneath it.
+
+We therefore do not assess a report that a request can get past this policy,
and
+we will not harden it. The remedy is `FetchMetadataResourceIsolationPolicy`,
which
+is what the framework claims; a deployment that does not want the fallback at
all
+can construct the listener without it:
+`new ResourceIsolationRequestCycleListener(new
FetchMetadataResourceIsolationPolicy())`.
+The class is not marked `@Deprecated` only because it remains a reasonable
+fallback for a legacy client, where the alternative is to refuse the request
+outright.
+
+What this exclusion does not cover is the chain around it. The default policy
list
+is `FetchMetadataResourceIsolationPolicy` followed by
+`OriginResourceIsolationPolicy`, checked in order, and the first policy to
return
+an outcome other than `UNKNOWN` decides. A request carrying `Sec-Fetch-Site` is
+consequently always settled by the fetch-metadata policy and never reaches the
+origin policy, and another document cannot make a browser omit that header. So
a
+report showing that a request *with* `Sec-Fetch-Site` present is nonetheless
+decided by the origin policy is in scope, as is anything else in
+`ResourceIsolationRequestCycleListener` itself.
+
+**8.x has no supported resource isolation implementation.** The
+`IResourceIsolationPolicy` mechanism arrived in 9.1.0. On 8.x the only
+cross-origin check Wicket offers is `CsrfPreventionRequestCycleListener`, which
+reads the same two headers with the same limits; 9.x deprecates it in favour of
+the resource isolation listener and it is therefore already out of scope there
+under [Deprecated code is out of scope](#deprecated-code-is-out-of-scope),
while
+on 8.x it is not deprecated only because that line has nothing to migrate to.
An
+8.x application that needs anything stronger has to upgrade or implement the
check
+itself: we will not backport the fetch-metadata policy, and reports against
+`CsrfPreventionRequestCycleListener` are out of scope on the same footing as
the
+origin policy.
+
### `wicket-examples` is sample code, not production code
`wicket-examples` exists to demonstrate framework features in as few lines as
@@ -220,8 +288,11 @@ There is no hostname allowlist in the framework and no
attempt to verify the
This is a deliberate design decision, not an oversight. Only the deployment
knows its own canonical hostnames; the framework cannot infer them. Note in
particular that the third item means the container-reported host is a trusted
-input to Wicket's own request-forgery defences — a deployment that lets
-arbitrary `Host` values through weakens more than URL rendering.
+input to a request-forgery defence — a deployment that lets arbitrary `Host`
+values through weakens more than URL rendering. That policy is the legacy one
and
+is [out of scope](#originresourceisolationpolicy-is-out-of-scope);
+`FetchMetadataResourceIsolationPolicy`, the supported one, does not consult the
+host at all.
**Therefore the deployment is responsible for ensuring that only expected
`Host` values reach the application.** Concretely:
@@ -378,6 +449,11 @@ Two things sit deliberately outside that boundary:
origin on its own site can allow it; sibling-origin actions are then that
deployment's decision rather than a framework vulnerability.
+The boundary is `FetchMetadataResourceIsolationPolicy`'s. The listener also
+consults `OriginResourceIsolationPolicy` by default, for clients that send no
+`Sec-Fetch-*` headers, and that policy is
+[out of scope](#originresourceisolationpolicy-is-out-of-scope).
+
This listener is opt-in and is not registered by default. Without it Wicket
enforces no cross-origin boundary on listener invocation at all. `CryptoMapper`
raises the cost of forging a URL but is not a substitute for it, for the reason