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

lukaszlenart pushed a commit to branch 
docs/hardening-vs-vulnerability-control-test
in repository https://gitbox.apache.org/repos/asf/struts.git

commit 40427c356a0f22e86db44eedf4634308b6d0c6a8
Author: Lukasz Lenart <[email protected]>
AuthorDate: Fri Sep 18 05:55:43 2026 +0200

    docs: state the control test that separates hardening from a vulnerability
    
    SECURITY.md already says an unbounded read is in scope and THREAT_MODEL §8.5
    already says a control that fails to enforce its documented behaviour is a
    violation, but nothing states the rule that connects them: a control the
    framework claims and does not enforce is a vulnerability; the absence of a
    control the framework never claimed is hardening. Adding a control therefore
    moves the line, which is why the same input can be a hardening report 
before a
    limit exists and a vulnerability report once the limit can be bypassed.
    
    Adds that test to SECURITY.md with the public WW-5618 -> S2-072 sequence as
    the worked example, a `VALID: control-not-enforced` disposition to §13, and 
a
    §11a bullet so a repeat report is re-triaged against any control added since
    rather than closed as a duplicate.
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---
 SECURITY.md     | 21 +++++++++++++++++++++
 THREAT_MODEL.md |  5 +++++
 2 files changed, 26 insertions(+)

diff --git a/SECURITY.md b/SECURITY.md
index 8a89c21b7..08d0c53d2 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -53,6 +53,27 @@ exactly that. What stays out of scope is the generic case 
above — where the co
 proportionate, no framework limit is missing or ineffective, and the defence 
belongs to your servlet
 container, reverse proxy, or network.
 
+### Hardening or vulnerability: the control test
+
+The question we ask is not "can this input hurt a server" but "does the 
framework claim a control
+here, and does that control do what it says".
+
+- **No control claimed.** Where the framework has never promised a bound or a 
check and the defence is
+  documented as the operator's or the application's (request-body size at the 
container, authentication,
+  CSRF tokens), a report that the framework does not supply it is handled as 
public hardening — a Jira
+  ticket, no bulletin, no CVE. The reporter is credited on the ticket.
+- **Control claimed but not enforced.** Where the framework documents a limit, 
a filter, or a policy and it
+  does not hold for the inputs it targets, that is a vulnerability regardless 
of how the operator has
+  configured anything else, and it is handled privately per this policy.
+
+A consequence worth stating: **adding a control moves the line.** The same 
request that was a hardening
+report before a limit existed becomes a vulnerability report if the limit, 
once added, can be bypassed.
+The JSON plugin's input limits were added as hardening in
+[WW-5618](https://issues.apache.org/jira/browse/WW-5618) (6.9.0 / 7.2.1); the 
later finding that the
+length limit could be bypassed was CVE-2026-73633
+([S2-072](https://cwiki.apache.org/confluence/display/WW/S2-072)). Those are 
two findings, not one, and
+they were credited separately.
+
 The mailing address is: 
[[email protected]](mailto:[email protected])
 
 [General network server security 
tips](http://httpd.apache.org/docs/trunk/misc/security_tips.html)
diff --git a/THREAT_MODEL.md b/THREAT_MODEL.md
index c544d7fcb..ff9562d55 100644
--- a/THREAT_MODEL.md
+++ b/THREAT_MODEL.md
@@ -440,6 +440,10 @@ authoritative list; §14 Q12.)*
   bounded cache reaching its ceiling and evicting is working as designed, and 
a cache
   keyed on a validated finite set — a locale restricted to the runtime's 
available
   locales, for instance — is not unbounded. *(documented — S2-074.)*
+- **"This was reported before and closed as hardening."** Not a ground to 
close. Check
+  whether a control was added since; if it was and the new report shows it 
does not
+  enforce, the report is `VALID: control-not-enforced`, not `DUPLICATE`. 
*(documented —
+  WW-5618 → S2-072.)*
 - **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
@@ -464,6 +468,7 @@ authoritative list; §14 Q12.)*
 | 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: control-not-enforced` | A documented framework limit, filter, or 
policy does not hold for the inputs it targets. Independent of default-config 
arguments — the control's own documentation is the claim being broken. | §8 
(esp. §8.4, §8.5, §8.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 — i.e. a setting the 
project documents as *unsafe*. **Not** a feature the project documents as 
optional and an application has enabled (§2). | §5a, §2 |

Reply via email to