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

lukaszlenart pushed a commit to branch WW-5791-struts-boot
in repository https://gitbox.apache.org/repos/asf/struts.git

commit df38f43dbd22d5092d899e38a0fe0aba14cde4f8
Author: Lukasz Lenart <[email protected]>
AuthorDate: Fri Oct 23 13:41:35 2026 +0200

    WW-5791 docs: add Struts Boot to the threat model
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 THREAT_MODEL.md | 49 +++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 45 insertions(+), 4 deletions(-)

diff --git a/THREAT_MODEL.md b/THREAT_MODEL.md
index e4e7f2dc9..e2035008d 100644
--- a/THREAT_MODEL.md
+++ b/THREAT_MODEL.md
@@ -91,6 +91,14 @@ perimeter. Struts is **in-process** with the application; it 
has no daemon, no
 listening socket of its own, and no trust boundary against the application code
 it runs inside. *(documented — it is a framework, not a server.)*
 
+**Second deployment shape — Struts Boot (`struts2-boot-starter`, since 
7.5.0).** The
+application is a plain `main()` that calls `StrutsBoot.run(...)`; the starter 
starts an
+embedded Tomcat, registers the Struts filter itself, and opens one HTTP 
listening socket.
+Actions come from a Java `ActionRegistry` instead of (or alongside) 
`struts.xml`. The
+request pipeline behind the filter is unchanged, so everything in §4–§9 
applies as-is.
+What moves is the **container's defaults** (§5, §5a): with Boot they are 
chosen by the
+framework, not by the operator. *(documented — `boot/`, 
`apps/boot-hello/README.md`.)*
+
 **Caller roles.**
 
 - **Untrusted HTTP client** — sends requests (parameters, headers, cookies,
@@ -105,6 +113,9 @@ it runs inside. *(documented — it is a framework, not a 
server.)*
   section of the security guidance.)*
 - **Operator** — deploys the WAR, sets `devMode` off, restricts dev-only 
plugins,
   configures the container and JVM. **Trusted.** *(documented.)*
+  With Boot, the operator still owns the JVM, the host, the network perimeter 
and the
+  `struts.boot.*` settings (port, bind address, context path); the container 
defaults
+  the starter applies are Struts' (§5).
 
 **Component families.**
 
@@ -149,7 +160,8 @@ links to them and assigns each a triage disposition (§13):
 - **Findings that only manifest with a documented-insecure / non-default 
setting**
   (`devMode=true`, Config Browser Plugin exposed in production, DMI enabled, 
or a
   §5a hardening knob turned off) → `OUT-OF-MODEL: non-default-config`. 
*(documented.)*
-- **The servlet container, JVM, JDK, and OS**, and the application's own
+- **The servlet container, JVM, JDK, and OS** — except the embedded-container 
defaults
+  the Boot starter itself applies (§5), which are in model — and the 
application's own
   authentication, authorization, session management, CSRF token storage, and
   transport (TLS). Struts is "a pure web framework," not a security framework.
   *(documented / inferred — §14 Q-env.)*
@@ -210,8 +222,21 @@ documented anti-pattern is `OUT-OF-MODEL`. 
*(documented/inferred — §14 Q-defa
   Versions table.)*
 - The operator runs production with `devMode=false` and dev-only diagnostics 
(Config
   Browser Plugin) disabled or access-controlled. *(documented.)*
-- Struts opens no sockets and makes no outbound connections of its own; any 
network
-  egress is the application's. *(inferred — §14 Q-egress.)*
+- Struts makes no outbound connections of its own; any network egress is the
+  application's. In the WAR shape it opens no sockets. The Boot starter opens 
exactly
+  one inbound HTTP listener (§2); it makes no outbound connections either.
+  *(inferred — §14 Q-egress, Q-boot.)*
+- **Boot container defaults are in model.** When `struts2-boot-starter` starts 
Tomcat,
+  the framework owns these defaults:
+  - listens on all interfaces, port 8080 (override `struts.boot.address` /
+    `struts.boot.port`)
+  - error responses render without exception report or server version
+  - `DefaultServlet` serves only an empty per-run docBase
+  - Tomcat's base directory is a fresh owner-only temp directory, deleted on 
shutdown
+  - `devMode` is never switched on
+
+  A deviation from these defaults is a Struts finding. Anything the developer 
adds on
+  top (TLS, proxies, a different container) is theirs. *(documented — §14 
Q-boot.)*
 
 ## §5a Build-time and configuration variants — **the central knob set**
 
@@ -245,6 +270,12 @@ reproduced here.** Only the triage-load-bearing facts:
 - The **FetchMetadata / COOP / COEP** interceptors are in core's 
`defaultStack` and
   enabled since 6.0, COEP in report-only mode; an application opts *out*, per 
action or
   stack (§8.5).
+- **Registry-defined actions (Boot) are never broader than the `struts.xml`
+  equivalent.** They extend `struts-default`, keep Strict Method Invocation 
on, are
+  added to the OGNL allowlist exactly as XML-declared action classes are, and 
allow
+  only their registered method — narrower than XML, which also inherits the 
package's
+  global allowed methods. A registry action reachable where the XML equivalent 
would
+  not be is a finding. *(documented — WW-5791 spec.)*
 
 **Insecure-default question (wave 1).** Because the secure posture is the **7.0
 default set**, the triage rule needs ratifying: is "a finding that only works 
with a
@@ -386,7 +417,9 @@ Struts' security work.)*
   permanently disabled in JDK 24; see §5a). *(documented.)*
 - **Auto-generated error pages do not escape action names** (historical 
S2-006) — the
   app must define custom error pages; XSS in the default error page is a 
documented
-  hardening item, not a defended property. *(documented.)*
+  hardening item, not a defended property. *(documented.)* Boot apps have no
+  `web.xml`, so the starter suppresses Tomcat's exception report and server 
info itself
+  (§5); custom error *content* is still the application's.
 - **Well-known classes (framework):** OGNL/expression injection, 
multipart/file-upload
   parsing bugs, and parameter-pollution are the highest-severity recurring 
classes — but
   the recent record adds two more that are currently the *more frequent* ones:
@@ -464,6 +497,10 @@ authoritative list; §14 Q12.)*
 - **Dependency-tail CVEs** (a transitive jar, e.g. a logging or XML library) 
from an
   SCA scan — triage upstream unless Struts' own code reaches the vulnerable 
path with
   untrusted input.
+- **"Boot listens on 0.0.0.0."** That's the documented default, matching 
standalone
+  Tomcat and Spring Boot. `struts.boot.address` restricts it. →
+  `OUT-OF-MODEL: non-default-config` only if the operator set a wider 
exposure; the
+  default itself is by design.
 
 ## §12 Conditions that would change this model
 
@@ -533,6 +570,10 @@ so what remains is ratifying the *wording*, not the 
substance.
 - **Q-egress.** Confirm Struts opens no sockets and makes no outbound 
connections of
   its own, so any network egress (and the SSRF surface it implies) is the
   application's. (§5/§7.)
+- **Q-boot.** Confirm the Boot starter's embedded-container defaults (§5) are 
in model
+  — a framework-shipped container default that leaks or exposes is a Struts 
finding,
+  not `OUT-OF-MODEL: environment` — and that the example app `apps/boot-hello` 
stays
+  out of scope like the other showcase apps. (§2/§3/§5.)
 
 **Wave 2 — mechanism confirmations**
 

Reply via email to