diff --git a/AGENTS.md b/AGENTS.md index 088a58dd87..947f929a73 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,8 +13,9 @@ Before drafting any report, opening an issue, posting publicly, or reaching a se - read the Struts security policy, - read the Struts security guidelines, - check previously disclosed vulnerabilities and Security Bulletins. -2. **Assess** the finding against the questions in [`SECURITY.md` § Assessment](SECURITY.md#assessment). If the answers do not still point to a likely new - framework vulnerability, stop and explain — do not draft a new report. +2. **Assess** the finding against the questions in [`SECURITY.md` § Assessment](SECURITY.md#assessment), and route it through the disposition guide in + [`THREAT_MODEL.md`](THREAT_MODEL.md) (scope, trust boundaries, in-model security properties, and the known-non-findings list). If the answers do not still + point to a likely new framework vulnerability, stop and explain — do not draft a new report. 3. **Report privately** to `security@struts.apache.org` following [`SECURITY.md` § Private Report Requirements](SECURITY.md#private-report-requirements) and [§ Report Quality Rules](SECURITY.md#report-quality-rules). diff --git a/SECURITY.md b/SECURITY.md index 8c69b08e82..3337c131cd 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,5 +1,14 @@ # Security Policy +## Threat Model + +A structured threat model for the Apache Struts framework — scope, adversary model, +the security properties the framework provides vs. leaves to the application, and a +triage-disposition guide for inbound reports and automated-scanner findings — is +maintained in [`THREAT_MODEL.md`](THREAT_MODEL.md). It is additive to this policy: +this `SECURITY.md` and the [security guidance](https://struts.apache.org/security/) +remain canonical for the reporting process and configuration details. + ## Supported Versions Please visit the [Releases](https://struts.apache.org/releases.html#prior-releases) page to see full information about each version diff --git a/THREAT_MODEL.md b/THREAT_MODEL.md new file mode 100644 index 0000000000..4e3e490f12 --- /dev/null +++ b/THREAT_MODEL.md @@ -0,0 +1,439 @@ + +# Apache Struts — Threat Model (v0 draft) + +## §1 Header + +- **Project:** Apache Struts (`apache/struts`), `main` @ HEAD (2026-06). Scope: the + Struts framework in `apache/struts` only (the core MVC framework, its + interceptors, tags, and the plugins shipped in this repo). +- **Date:** 2026-06-24. **Drafted for PMC review** via the threat-model-producer + rubric (Scovetta). This is an unratified proposal, not an ASF Security team or + PMC position; authorship and sponsorship are settled only once the PMC adopts it + (see Status below and §14). +- **Status:** DRAFT — not yet reviewed by the Struts PMC. Built as a strict + superset of the existing [`SECURITY.md`](SECURITY.md) and the published + [Struts security guidance](https://struts.apache.org/security/); every + load-bearing claim is tagged for provenance (see §14 for open questions). +- **Version binding:** versioned with the project; a report against version *N* + is triaged against the model as it stood at *N*. The security envelope changed + materially at **7.0** (several hardening knobs flipped to secure-by-default — + §5a), so the version is itself load-bearing. +- **Reporting cross-reference:** §8-property violations → report privately per + [`SECURITY.md`](SECURITY.md) (`security@struts.apache.org`); §3/§9/§11a findings + are closed citing this document and the existing `SECURITY.md` "Before + Reporting" checks. +- **Provenance legend:** *(documented)* = Struts' own docs/`SECURITY.md`/security + site; *(maintainer)* = confirmed by a Struts PMC member through this process; + *(inferred)* = reasoned from architecture/docs, not yet PMC-ratified — each has + a matching §14 open question. +- **Draft confidence:** the bulk is *(documented)* — Struts has an unusually rich + published security policy — with a handful of *(inferred)* scoping calls for the + PMC to ratify. + +**What Struts is.** Apache Struts 2 is a **Java MVC web framework** for building +server-side web applications. A request flows: servlet filter → action mapping → +**interceptor stack** (parameter population, validation, etc.) → **Action** → +**result** (typically a JSP/FreeMarker view). Request parameters are bound onto +action properties via setters, and view/configuration expressions are evaluated +through **OGNL (Object-Graph Navigation Language)** against the **ValueStack**. +*(documented — struts.apache.org)* + +**The framework's own security philosophy (load-bearing).** Struts +**"doesn't provide any security mechanism — it is just a pure web framework."** +*(documented — [security guidance](https://struts.apache.org/security/))* It is +not an authentication, authorization, session-security, or input-sanitisation +layer; those are the embedding application's responsibility (§3/§10). What Struts +*does* take an active stance on is **not letting its own machinery — chiefly OGNL +expression evaluation and request-parameter binding — become an injection vector**. +That single sentence shapes the whole model: most "Struts is insecure" reports are +either OGNL-injection-class (in model, §8) or application-responsibility (out of +model, §3/§11a). + +## §2 Scope and intended use + +Intended deployment: the Struts JARs are a **dependency embedded inside a web +application** (a WAR) that the application developer writes, configures, and +deploys into a servlet container (Tomcat, Jetty, …) behind the operator's +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.)* + +**Caller roles.** + +- **Untrusted HTTP client** — sends requests (parameters, headers, cookies, + multipart uploads) to a Struts-backed endpoint. **The primary untrusted boundary.** + Struts must treat all request-derived values as hostile. *(documented — the + parameter/OGNL hardening exists precisely for this actor.)* +- **Application developer** — writes the actions, JSPs, struts.xml/annotations, + and chooses the hardening settings (§5a). **Trusted by the framework** — their + code and configuration run with the application's privileges. A finding that + requires the developer to write unsafe code or disable a default protection is + the application's bug, not Struts' (§3). *(documented — the developer-responsibility + section of the security guidance.)* +- **Operator** — deploys the WAR, sets `devMode` off, restricts dev-only plugins, + configures the container and JVM. **Trusted.** *(documented.)* + +**Component families.** + +| Family | Entry point | Touches | In model? | +| --- | --- | --- | --- | +| OGNL evaluation + ValueStack | expression eval for params, tags, results | in-JVM code paths | **In — the central attack surface** *(documented)* | +| Parameter binding (`ParametersInterceptor`, `@StrutsParameter`) | request params → action setters | reflection into app objects | **In — primary boundary** *(documented)* | +| Interceptor stack (cookie, fileupload, fetch-metadata, COOP/COEP, …) | per-request processing | request data | **In** *(documented)* | +| Tag library / JSP & FreeMarker integration | view rendering, expression output | template eval | **In — output-side OGNL/EL** *(documented)* | +| File upload (Jakarta multipart) | multipart request parsing | temp files | **In — historical CVE surface** *(documented — S2 bulletins)* | +| Bundled plugins (REST, JSON, Convention, …) in this repo | extra mappers/result types | request data | **In — same request-trust surface** *(inferred — §14 Q-plugins)* | +| Config Browser Plugin | exposes internal config | dev-only diagnostic | **In as dev-only** — exposure in prod is operator misconfig (§3/§11a) *(documented)* | +| Embedding application's own actions/JSPs/config | the developer's code | as the app | **Out — application responsibility (§3)** *(documented)* | +| Examples / showcase / test apps | demo code | n/a | **Out** *(see §3)* | + +## §3 Out of scope (explicit non-goals) + +The detailed lists of developer anti-patterns and insecure configurations are +maintained in the project's own docs and are **not duplicated here** — this model +links to them and assigns each a triage disposition (§13): + +- **Anything the application developer is responsible for.** Struts provides no + security mechanism of its own *(documented)*. The full enumeration — + developer-exposed unsafe setters, request parameters used in localization or + forced OGNL evaluation, raw `${...}` JSP-EL over untrusted values, direct JSP + access, mixing security levels in one namespace — is in the + [security guidance](https://struts.apache.org/security/) and + [`SECURITY.md`](SECURITY.md). All are `OUT-OF-MODEL: application-responsibility`. +- **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 + 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.)* +- **Generic denial of service.** Per [`SECURITY.md`](SECURITY.md), generic flooding + or large-body streaming is not accepted; only *super-linear* amplification inside + framework code may be in model (§8 / §14 Q-dos). *(documented.)* +- **Already-disclosed S2-series vulnerabilities** — a duplicate of an existing + Security Bulletin/CVE is closed by reference (the + [`SECURITY.md` "Before Reporting"](SECURITY.md) checks), not re-triaged. +- **Examples, showcase, and test applications** shipped in the repo. *(inferred — §14 Q-scope.)* + +## §4 Trust boundaries and data flow + +``` +Untrusted HTTP request + │ params, headers, cookies, multipart + ▼ +Servlet filter ─► action mapping ─► Interceptor stack ─► Action ─► Result (JSP/FreeMarker) + │ │ + ParametersInterceptor tag/result OGNL eval + binds params to setters against ValueStack + │ │ + ▼ ▼ + OGNL evaluation against the ValueStack ◄── the trust boundary + (allowlist / excluded classes+packages / + expression length / @StrutsParameter) +``` + +- **HTTP client → framework** is the one boundary Struts owns. Every request-derived + string (parameter *names* as well as *values*, cookie names/values, header values, + multipart filenames) is untrusted and may carry an OGNL payload. The framework's + job at this boundary is to bind parameters and evaluate expressions **without + letting attacker input reach an OGNL evaluation that creates or changes executable + code**. *(documented.)* +- **Framework → application code** is *not* a trust boundary — Struts runs the + developer's actions and templates in-process, fully trusted. *(documented.)* + +**Reachability precondition (triager's test).** A finding is in-model only if it is +reachable by an **untrusted HTTP client against a Struts application that follows the +documented secure configuration** (current-version defaults, `devMode` off, dev-only +plugins restricted, no developer anti-patterns from §3). A finding that needs +`devMode`, a disabled default protection, a developer-introduced unsafe setter, or a +documented anti-pattern is `OUT-OF-MODEL`. *(documented/inferred — §14 Q-default.)* + +## §5 Assumptions about the environment + +- A servlet container and a JVM the operator maintains; Struts does not patch or + harden them. *(inferred — §14 Q-env.)* +- The application is deployed with the **current supported version** (7.x or 6.x per + `SECURITY.md`); 2.x is end-of-life and out of support. *(documented — Supported + 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.)* + +## §5a Build-time and configuration variants — **the central knob set** + +Struts' security envelope is set almost entirely by **runtime configuration**. The +**authoritative, current list of every hardening setting (purpose + secure default) +lives in the [security guidance](https://struts.apache.org/security/) and is not +reproduced here.** Only the triage-load-bearing facts: + +- The security posture **changed materially at 7.0**, where a cluster of + OGNL-injection and parameter-binding defences became **secure-by-default** — + notably the OGNL allowlist (`struts.allowlist.enable`), the `@StrutsParameter` + annotation requirement (`struts.parameters.requireAnnotations`), excluded + classes/packages, the expression-length cap (`struts.ognl.expressionMaxLength`, + default 256), and the static-field/proxy/default-package/custom-map disallows. +- `struts.devMode` (must be `false` in production) and Dynamic Method Invocation + (gated by Strict Method Invocation since 2.5) are the two settings whose *insecure* + value most often turns a non-finding into an apparent finding. +- The **FetchMetadata / COOP / COEP** interceptors (6.0+) are opt-in cross-origin + defences (§8.5). + +**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 +pre-7.0 default, or with a 7.0 hardening knob turned off" `OUT-OF-MODEL: +non-default-config`, with §10 carrying "deploy current version with defaults"? — §14 +Q-default. The OGNL **Java Security Manager sandbox** (`-Dognl.security.manager`) is a +separate, opt-in defence built on the JDK `SecurityManager`, which has been +**deprecated for removal since JDK 17 (JEP 411), disabled by default since JDK 18, +and permanently disabled in JDK 24 (JEP 486)** *(documented — JDK release notes)* — +so on modern JDKs the model cannot treat it as a relied-upon control (§14 Q-jsm). + +## §6 Assumptions about inputs + +| Surface | Input | Attacker-controllable? | Concern | +| --- | --- | --- | --- | +| Parameter binding | request parameter **names and values** | **yes** | OGNL injection via crafted names; binding to unsafe setters | +| Cookies | cookie names/values (Cookie Interceptor) | **yes** | same OGNL/parameter concerns; checked by accepted/excluded patterns | +| Headers | request headers | **yes** | header-driven expression/log paths | +| Multipart upload | file content, filename, content-type | **yes** | parser robustness, temp-file handling (S2 history) | +| Expression context | values that reach an OGNL eval (tags, results, forced eval) | **yes if developer feeds untrusted input in** | the core RCE channel | +| struts.xml / annotations / action code | framework + app configuration | **no — developer-trusted** | not an attacker surface (§3) | + +The accepted/excluded pattern checkers (`AcceptedPatternsChecker` / +`ExcludedPatternsChecker`, since 2.3.20) validate parameter names/values for the +Parameters and Cookie interceptors; a custom override that drops below the framework +defaults is a developer error, not a framework flaw. *(documented.)* + +## §7 Adversary model + +- **In scope:** an **untrusted remote HTTP client** with no credentials, able to send + arbitrary parameters, headers, cookies, and multipart uploads to any + Struts-handled endpoint. Capabilities: craft parameter names/values carrying OGNL, + attempt to reach executable-code creation through the ValueStack, pollute + parameter binding, exploit a file-upload or multipart parsing bug, or trigger a + super-linear resource path in framework code. Goal: **remote code execution via + OGNL** (the dominant Struts threat), and secondarily data disclosure, SSRF through + framework features, or DoS amplification. *(documented — the OGNL lineage is the + framework's stated central concern.)* +- **On-path network attacker** — only where the application/operator has not deployed + TLS; transport security is the app's, so this is largely out of model (§3). *(inferred — §14 Q-env.)* +- **Out of scope:** the application developer (writes trusted code/config); the + operator (deploys, sets devMode/plugins); anyone with container/host/JVM control; + and a developer who disables a default protection or follows a documented + anti-pattern (§3). *(documented.)* + +## §8 Security properties the framework provides + +*(In the current-version, default-hardening posture; each lists violation symptom + +severity. Most are documented controls — the OGNL-injection defences are the core of +Struts' security work.)* + +1. **OGNL injection containment.** Attacker-supplied request data (parameter names/ + values, cookies, headers) must not reach an OGNL evaluation that creates or alters + executable code. Enforced in depth by the default controls listed in §5a / the + [security guidance](https://struts.apache.org/security/) (allowlist, excluded + classes/packages, expression-length cap, static-field/proxy/default-package/ + custom-map disallows, excluded node types). *Violation:* a crafted request + achieving OGNL-driven code execution (or class-loader/member access beyond the + allowlist) on a default-configured current-version app. *Severity:* + security-critical (the S2-RCE class). *(documented.)* +2. **Parameter-binding safety (7.0).** Request parameters bind only to setters the + developer marked `@StrutsParameter` (to the declared depth); arbitrary deep/nested + property traversal is not reachable by default. *Violation:* parameters reaching + an unannotated setter, or nesting beyond the declared depth, on a default 7.0 app. + *Severity:* critical. *(documented.)* +3. **Method-invocation control.** Dynamic Method Invocation is gated by Strict Method + Invocation; a client cannot invoke arbitrary action methods by name when DMI is at + its recommended (off/strict) setting. *Violation:* arbitrary method invocation on a + default app. *Severity:* high–critical. *(documented.)* +4. **Expression-length and node-type bounds.** OGNL expressions over the configured + length (default 256) and forbidden node types are rejected before evaluation. + *Violation:* bypass of these bounds. *Severity:* high. *(documented.)* +5. **Cross-origin / fetch-metadata defences (opt-in).** When the FetchMetadata, COOP, + and COEP interceptors are enabled, the framework emits/enforces the corresponding + `Sec-Fetch-*` and cross-origin isolation behaviour. *Violation:* the interceptor + failing to enforce its documented behaviour when enabled. *Severity:* medium–high. + *(documented — opt-in since 6.0.)* + +## §9 Security properties the framework does *not* provide + +- **No security mechanism in the general sense.** Struts provides no authentication, + authorization, session security, CSRF token store, input sanitisation, or output + encoding *for the application's own data* — "it is just a pure web framework." + *(documented.)* + - *False friend:* "Struts has no built-in login/access control" is **by design**, + not a vulnerability. +- **No protection against developer anti-patterns or non-default config** — unsafe + setters, raw `${}` on user input, request params in localization/forced eval, + direct JSP access, `devMode` on, disabled hardening (§3/§5a). +- **No defence once OGNL evaluation is fed untrusted input by the application + itself** (forced expression evaluation on a request value) — that is the developer + handing OGNL the attacker's string. *(documented.)* +- **No hard anti-DoS guarantee** beyond the "avoid super-linear in input size" + philosophy; generic flooding/streaming DoS is the operator's to absorb. *(documented.)* +- **The OGNL Java Security Manager sandbox is not a relied-upon control on modern + JDKs** (the underlying `SecurityManager` is deprecated for removal since JDK 17 and + 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.)* +- **Well-known classes (framework):** OGNL/expression injection, multipart/file-upload + parsing bugs, and parameter-pollution are the framework's recurring risk classes; + reflected XSS, CSRF token management, and transport security are the application's. + +## §10 Downstream (developer + operator) responsibilities + +The full, authoritative how-to is the [security guidance](https://struts.apache.org/security/) +and [`SECURITY.md`](SECURITY.md); in one line: **deploy a current supported version +with the default hardening left on, `devMode` off, dev-only plugins restricted, +parameter setters annotated, JSPs hidden behind actions, and the application's own +authn/authz/CSRF/TLS supplied** (Struts provides none of those). The threat-model +value is only that a finding requiring the developer to *violate* one of these is +`OUT-OF-MODEL` (§3/§13), not that this list is novel. + +## §11 Known misuse patterns + +These are the §3 application-responsibility / non-default-config items viewed as +"things integrators get wrong" — running `devMode=true` in production or exposing the +Config Browser Plugin; disabling a default OGNL/binding protection "to make something +work"; exposing unsafe setters to binding; feeding request parameters into forced +OGNL evaluation or localization; allowing direct `*.jsp` access or raw `${}` EL on +untrusted values; relying on the OGNL Java Security Manager sandbox on modern JDKs. Each +is documented in the [security guidance](https://struts.apache.org/security/); the +disposition mapping is §11a/§13. + +## §11a Known non-findings (recurring false positives) + +*(Seeded directly from `SECURITY.md` "Before Reporting" — the PMC owns the +authoritative list; §14 Q12.)* + +- **"OGNL/RCE that only works with `devMode=true`."** `OUT-OF-MODEL: non-default-config` + — devMode is a development-only setting documented as unsafe for production. +- **"An action setter lets me inject a value / reach a dangerous method."** When the + setter is developer-exposed without `@StrutsParameter` (7.0), or performs an unsafe + side effect, this is `OUT-OF-MODEL: application-responsibility`. In-model only if it + bypasses the framework's *default* binding/OGNL protections. +- **"Direct JSP access discloses X / executes Y."** App-deployment misconfiguration — + JSPs must be hidden behind actions. `OUT-OF-MODEL: application-responsibility`. +- **"Raw `${}` EL / forced OGNL eval on my request parameter is exploitable."** The + application fed untrusted input to expression evaluation — documented anti-pattern, + not a framework flaw. +- **"Config Browser Plugin exposes internal configuration."** Dev-only diagnostic; + exposing it in production is operator misconfiguration. `OUT-OF-MODEL: non-default-config`. +- **"I can enumerate / pass arbitrary parameters."** Parameter binding is the point of + the framework; in-model only when it crosses the default annotation/allowlist + protections. +- **"Generic DoS: I streamed a huge body / hammered a URL."** Not accepted per + `SECURITY.md`; only super-linear amplification inside framework code is considered. +- **Duplicate of a disclosed S2-series bulletin/CVE** — closed by reference. +- **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. + +## §12 Conditions that would change this model + +- A change to the default-hardening set (e.g. a new secure-by-default knob, or a + default flipped) — re-baseline §5a/§8/§11a. +- A new request-facing surface, a new bundled plugin, or a new expression/templating + integration with its own trust surface. +- A change to how OGNL evaluation, the allowlist, or parameter binding works. +- A report that cannot be routed to a §13 disposition → revise §8/§9. + +## §13 Triage dispositions + +| Disposition | Meaning | Licensed by | +| --- | --- | --- | +| `VALID` | A §8 property breaks via an untrusted HTTP client on a current-version, default-hardened app. | §8, §6, §7 | +| `VALID-HARDENING` | A §11 misuse is too easy, or a default could be tightened. | §11/§5a | +| `OUT-OF-MODEL: application-responsibility` | Requires a developer anti-pattern (unsafe setter, raw EL, forced eval, direct JSP) or the app's own authn/authz. | §3/§10 | +| `OUT-OF-MODEL: non-default-config` | Only manifests with `devMode`, a dev-only plugin, DMI, or a disabled default protection. | §5a | +| `OUT-OF-MODEL: adversary-not-in-scope` | Requires container/host/JVM/developer control. | §7 | +| `OUT-OF-MODEL: unsupported-version` | Only affects an end-of-life (2.x) version. | §5 | +| `BY-DESIGN: property-disclaimed` | Concerns a property §9 disclaims (no built-in authn/authz/encoding; generic DoS; JSM on JDK21+). | §9 | +| `KNOWN-NON-FINDING` | Matches §11a. | §11a | +| `DUPLICATE` | Matches a disclosed S2-series bulletin/CVE. | §3 | +| `MODEL-GAP` | Unroutable. | triggers §12 | + +## §14 Open questions for the maintainers + +**Wave 1 — scope, defaults, intended use** + +- **Q-default.** Confirm the triage baseline is "current supported version (7.x/6.x) + with the documented default hardening on, `devMode` off, dev-only plugins + restricted" — and that a finding requiring a pre-7.0 default or a disabled hardening + knob is `OUT-OF-MODEL: non-default-config`. (§5a/§13.) +- **Q-scope.** Confirm the in-scope surface is the framework in `apache/struts` + (core + interceptors + tags + bundled plugins), with the embedding application's own + actions/JSPs/config, and examples/showcase, out of scope. (§2/§3.) +- **Q-philosophy.** Confirm the framing that Struts provides **no security mechanism + of its own** beyond OGNL/parameter-binding injection containment — i.e. authn, + authz, session security, CSRF token storage, output encoding, and transport are the + application's. (§9.) +- **Q-env.** Confirm the servlet container, JVM, JDK, and OS are out of scope — Struts + does not patch or harden them, and the operator maintains them. (§3/§5.) +- **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.) + +**Wave 2 — mechanism confirmations** + +- **Q-ognl.** Confirm the §8.1 list is the authoritative set of default OGNL-injection + defences (allowlist, excluded classes/packages/patterns, expression length, + static-field/proxy/default-package/custom-map disallows, excluded node types) and + that a bypass of any on a default app is `VALID`. (§8.) +- **Q-jsm.** Confirm the OGNL Java Security Manager sandbox is **not** a relied-upon + control (opt-in, and non-functional on modern JDKs — see §5a), so a report premised + on its absence is not a finding. (§5a/§9.) +- **Q-dos.** Where is the line between "generic DoS we don't accept" and "super-linear + amplification inside framework code we do"? Confirm the §3/§8 wording. (§3.) + +**Wave 3 — surfaces & false-friends** + +- **Q-plugins.** Which bundled plugins (REST, JSON, Convention, …) are in scope at the + same request-trust level, and are any (e.g. REST/XML) historically higher-risk and + worth their own §8 note? (§2.) +- **Q-upload.** Confirm the multipart/file-upload surface (Jakarta) and what the + framework guarantees vs. leaves to the container/app. (§2/§6.) +- **Q12.** Beyond the `SECURITY.md` "Before Reporting" list already folded into §11a, + what do scanners/researchers most often report against Struts that you consider a + non-finding? (Feeds §11a.) + +## §15 Appendix — existing-policy back-map + +This `THREAT_MODEL.md` is **additive** — it does not replace +[`SECURITY.md`](SECURITY.md) (reporting process, supported versions, "Before +Reporting" checks) or the published [security guidance](https://struts.apache.org/security/); +both are preserved and remain canonical for the reporting workflow. The discoverability +chain is `AGENTS.md` → `SECURITY.md` → this model. Mapping of existing-policy claims to +sections: + +| Existing-policy statement | Threat-model § | +| --- | --- | +| "Struts doesn't provide any security mechanism — pure web framework" | §1, §9, §13 (`BY-DESIGN`) | +| OGNL is the central historical vuln class | §1, §7, §8.1 | +| devMode / Config Browser Plugin are dev-only | §3, §5a, §11a | +| `@StrutsParameter` / unsafe setters | §6, §8.2, §10, §11a | +| Direct JSP access / raw `${}` EL / forced eval / localization | §3, §10, §11a | +| Allowlist / excluded classes/packages / expression length (7.0 defaults) | §5a, §8.1 | +| DMI / Strict Method Invocation | §5a, §8.3 | +| FetchMetadata / COOP / COEP | §5a, §8.5 | +| OGNL JSM sandbox (modern-JDK limitation) | §5a, §9 | +| Generic DoS not accepted; non-linear-in-input philosophy | §3, §8, §9 | +| "Before Reporting" duplicate/known-config checks | §3, §11a, §13 (`DUPLICATE`) | +| Supported versions (2.x EOL) | §5, §13 (`OUT-OF-MODEL: unsupported-version`) |