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.git
The following commit(s) were added to refs/heads/main by this push:
new 02116f72a docs: state the control test that separates hardening from a
vulnerability (#1964)
02116f72a is described below
commit 02116f72a7fef917b5d1093429f41ba1c7c5ac0e
Author: Lukasz Lenart <[email protected]>
AuthorDate: Fri Sep 18 06:33:46 2026 +0200
docs: state the control test that separates hardening from a vulnerability
(#1964)
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 |