This is an automated email from the ASF dual-hosted git repository.
lukaszlenart pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/struts-site.git
The following commit(s) were added to refs/heads/main by this push:
new 224e0c2d29 WW-5846 docs: describe exemptedPaths matching and
cross-site form posts (#371)
224e0c2d29 is described below
commit 224e0c2d293c1d43e40b376cc2fd86f6ea7f985f
Author: Lukasz Lenart <[email protected]>
AuthorDate: Sat Nov 7 18:56:31 2026 +0100
WW-5846 docs: describe exemptedPaths matching and cross-site form posts
(#371)
The Fetch Metadata, COOP and COEP interceptor pages now state that
exemptedPaths entries are compared exactly with the request path inside the
application (servlet path plus path info, extension included) since 7.5.0 /
6.13.0, and that an entry equal to the context path no longer disables the
interceptor app-wide.
The Fetch Metadata page also documents the policy as implemented, notes the
interceptor is in defaultStack, and explains that cross-site POST
navigations
(SAML/OIDC form_post, payment returns) are rejected since 7.5.0 / 6.13.0
(WW-5753, WW-5754), with exemptedPaths and disabled as the opt-outs.
Examples move exemptedPaths to a stack-level configuration.
Co-authored-by: Claude Opus 5.5 <[email protected]>
---
source/core-developers/coep-interceptor.md | 30 ++++++++----
source/core-developers/coop-interceptor.md | 30 ++++++++----
.../core-developers/fetch-metadata-interceptor.md | 57 +++++++++++++++++++---
3 files changed, 91 insertions(+), 26 deletions(-)
diff --git a/source/core-developers/coep-interceptor.md
b/source/core-developers/coep-interceptor.md
index 00606ac159..68cb5e7bd1 100644
--- a/source/core-developers/coep-interceptor.md
+++ b/source/core-developers/coep-interceptor.md
@@ -25,21 +25,31 @@ COEP is now supported by all major browsers. [More
information about COEP](https
## Parameters
-- `exemptedPaths` - Set of opt out endpoints that are meant to serve
cross-site traffic. Paths should contain leading
- slashes and must be relative. This field is empty by default.
+- `exemptedPaths` - Comma-separated list of request paths the COEP header is
not sent on. Each entry is compared exactly
+ with the path of the request inside the web application: the servlet path
plus the path info, without the context path
+ and without the query string. Include the action extension, e.g.
`/embed/widget.action`; if an action is reachable
+ with several extensions, list each form. This field is empty by default.
- `enforcingMode` - Boolean variable allowing the user to let COEP operate in
`enforcing`, which blocks both resource
and reports violations, or `report-only` mode, which only reports
violations. Default value for field is `false`.
- `disabled` - Boolean variable disabling and enabling COEP. Default value for
field is `false`.
+Before Struts 7.5.0 and 6.13.0 `exemptedPaths` was compared with the context
path of the application, so an entry
+naming a request path had no effect and an entry equal to the context path
disabled the interceptor for the whole
+application. Use `disabled` if you relied on the latter.
+{:.alert .alert-warning}
+
## Examples
```xml
-<action name="someAction" class="com.examples.SomeAction">
- <interceptor-ref name="defaultStack">
- <param name="coep.exemptedPaths">/path1,/path2,/path3</param>
- <param name="coep.enforcingMode">false</param>
- <param name="coep.disabled">false</param>
- </interceptor-ref>
- <result name="success">good_result.ftl</result>
-</action>
+<package name="default" extends="struts-default">
+ <interceptors>
+ <interceptor-stack name="appDefaultStack">
+ <interceptor-ref name="defaultStack">
+ <param name="coep.exemptedPaths">/embed/widget.action</param>
+ <param name="coep.enforcingMode">false</param>
+ </interceptor-ref>
+ </interceptor-stack>
+ </interceptors>
+ <default-interceptor-ref name="appDefaultStack"/>
+</package>
```
diff --git a/source/core-developers/coop-interceptor.md
b/source/core-developers/coop-interceptor.md
index 212621faee..b68398dd57 100644
--- a/source/core-developers/coop-interceptor.md
+++ b/source/core-developers/coop-interceptor.md
@@ -36,19 +36,31 @@ COOP is now supported by all major browsers. [More
information about COOP](https
## Parameters
-- `exemptedPaths` - Set of opt out endpoints that are meant to serve
cross-site traffic. Paths should contain leading
- slashes and must be relative. This field is empty by default.
+- `exemptedPaths` - Comma-separated list of request paths the COOP header is
not sent on. Each entry is compared exactly
+ with the path of the request inside the web application: the servlet path
plus the path info, without the context path
+ and without the query string. Include the action extension, e.g.
`/popup/callback.action`; if an action is reachable
+ with several extensions, list each form. This field is empty by default.
- `mode` - The policy mode COOP should follow. Available modes are
`same-origin`, `same-origin-allow-popups`,
`unsafe-none`. Default mode is `same-origin`.
+- `disabled` - Set to `true` to skip the interceptor for an action. Default
value is `false`.
+
+Before Struts 7.5.0 and 6.13.0 `exemptedPaths` was compared with the context
path of the application, so an entry
+naming a request path had no effect and an entry equal to the context path
disabled the interceptor for the whole
+application. Use `disabled` if you relied on the latter.
+{:.alert .alert-warning}
## Examples
```xml
-<action name="someAction" class="com.examples.SomeAction">
- <interceptor-ref name="defaultStack">
- <param name="coop.exemptedPaths">/path1,/path2,/path3</param>
- <param name="coop.mode">same-origin</param>
- </interceptor-ref>
- <result name="success">good_result.ftl</result>
-</action>
+<package name="default" extends="struts-default">
+ <interceptors>
+ <interceptor-stack name="appDefaultStack">
+ <interceptor-ref name="defaultStack">
+ <param name="coop.exemptedPaths">/popup/callback.action</param>
+ <param name="coop.mode">same-origin</param>
+ </interceptor-ref>
+ </interceptor-stack>
+ </interceptors>
+ <default-interceptor-ref name="appDefaultStack"/>
+</package>
```
diff --git a/source/core-developers/fetch-metadata-interceptor.md
b/source/core-developers/fetch-metadata-interceptor.md
index e55013a3eb..2cc3bd40a9 100644
--- a/source/core-developers/fetch-metadata-interceptor.md
+++ b/source/core-developers/fetch-metadata-interceptor.md
@@ -14,32 +14,75 @@ An interceptor that implements Fetch Metadata on incoming
requests used to prote
information leaks. Uses a default Resource Isolation Policy to
programmatically reject cross-origin requests.
A Resource Isolation Policy is a strong defense in-depth mechanism that
prevents the resources on a server from being
-requested by external websites. This policy can be enabled either for all
endpoints of the application and endpoints
+requested by external websites. The interceptor is part of the `defaultStack`
and is enabled by default; endpoints
that are meant to be loaded in a cross-site context can be exempted from the
policy.
The browser provides information about the context of an HTTP request in a set
of `Sec-Fetch-*` headers. This allows
the server processing the request to make decisions on whether the request
should be accepted or rejected based
on the preferred resource isolation policy. Struts provides a default
_Resource Isolation Policy_ that rejects
-_cross-origin_ requests that aren't top level navigations.
+_cross-site_ requests that aren't simple top level navigations:
```
-Sec-Fetch-Site == 'cross-site' AND (Sec-Fetch-Mode !=
'navigate'/'nested-navigate' OR method NOT IN [GET, HEAD])
+Sec-Fetch-Site == 'cross-site' AND (Sec-Fetch-Mode != 'navigate' OR method NOT
IN [GET, HEAD] OR Sec-Fetch-Dest IN [object, embed])
```
+Requests without a `Sec-Fetch-Site` header and `same-origin`, `same-site` and
`none` requests are always accepted.
+
Refer to [Implementing a Resource Isolation
Policy](https://web.dev/fetch-metadata/#implementing-a-resource-isolation-policy)
for further information on implementing effective Resource Isolation Policies.
Fetch Metadata is supported in all major browsers.
+## Cross-site form posts
+
+Since Struts 7.5.0 and 6.13.0 cross-site `POST` navigations are rejected with
`403`.
+{:.alert .alert-info}
+
+Flows where another site posts the user's browser back to your application are
affected, for example:
+
+- a SAML assertion consumer service or an OpenID Connect `form_post` response,
+- a payment gateway returning the user to the shop.
+
+Exempt the endpoints receiving these requests, either by path with
`exemptedPaths` or per action with the `disabled`
+parameter, see the examples below.
+
## Parameters
-- `exemptedPaths` - Set of opt out endpoints that are meant to serve
cross-site traffic. Paths should contain leading
- slashes and must be relative. This field is empty by default.
+- `exemptedPaths` - Comma-separated list of request paths the policy is not
applied to. Each entry is compared exactly
+ with the path of the request inside the web application: the servlet path
plus the path info, without the context path
+ and without the query string. Include the action extension, e.g.
`/saml/acs.action`; if an action is reachable with
+ several extensions, list each form. This field is empty by default.
+- `disabled` - Set to `true` to skip the interceptor for an action. Default
value is `false`.
+
+Before Struts 7.5.0 and 6.13.0 `exemptedPaths` was compared with the context
path of the application, so an entry
+naming a request path had no effect and an entry equal to the context path
disabled the interceptor for the whole
+application. Use `disabled` if you relied on the latter.
+{:.alert .alert-warning}
+
+When `struts.mapper.action.prefix.enabled` is set to `true` (it is `false` by
default), a request to an exempted path
+can use the `action:` parameter prefix to execute another action in the same
namespace.
## Examples
+Exempt selected paths for every action using the stack:
+
+```xml
+<package name="default" extends="struts-default">
+ <interceptors>
+ <interceptor-stack name="appDefaultStack">
+ <interceptor-ref name="defaultStack">
+ <param
name="fetchMetadata.exemptedPaths">/saml/acs.action,/payment/return.action</param>
+ </interceptor-ref>
+ </interceptor-stack>
+ </interceptors>
+ <default-interceptor-ref name="appDefaultStack"/>
+</package>
+```
+
+Disable the interceptor for a single action:
+
```xml
-<action name="someAction" class="com.examples.SomeAction">
+<action name="acs" class="com.examples.SamlAssertionConsumerAction">
<interceptor-ref name="defaultStack">
- <param
name="fetchMetadata.exemptedPaths">/path1,/path2,/path3</param>
+ <param name="fetchMetadata.disabled">true</param>
</interceptor-ref>
<result name="success">good_result.ftl</result>
</action>