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

lukaszlenart pushed a commit to branch 
feature/WW-5695-html5-constraint-validation
in repository https://gitbox.apache.org/repos/asf/struts.git

commit 07dbf7b35121d77cf35d5600eaf372fa392a10fb
Author: Lukasz Lenart <[email protected]>
AuthorDate: Mon Aug 24 20:26:31 2026 +0200

    WW-5695 fix(validation): drop \s and \S from the ECMAScript-safe escape 
allowlist
    
    A task reviewer found a real semantic divergence the design had certified as
    portable. Java's \s is ASCII-only by default; ECMAScript's is always the 
wider
    Unicode set. For a rule as ordinary as ^\S+$, a value containing NBSP 
satisfies
    Java's \S and fails the browser's — so the server accepts input the browser
    silently refuses to submit, which is exactly the failure this allowlist 
exists
    to prevent.
    
    \d and \w are safe: both engines are ASCII-only for those by default and
    JavaScript never widens them, so the fix is scoped to s/S.
    
    The spec listed \s among the safe escapes and the plan's ALLOWED_ESCAPES
    transcribed it. Both corrected, plus a regression test. The governing rule
    outranks its own example list.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
---
 .../plans/2026-08-24-html5-constraint-validation.md | 21 ++++++++++++++++++---
 ...2026-08-24-html5-constraint-validation-design.md | 10 +++++++++-
 2 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/docs/superpowers/plans/2026-08-24-html5-constraint-validation.md 
b/docs/superpowers/plans/2026-08-24-html5-constraint-validation.md
index aa8619535..f174acaa5 100644
--- a/docs/superpowers/plans/2026-08-24-html5-constraint-validation.md
+++ b/docs/superpowers/plans/2026-08-24-html5-constraint-validation.md
@@ -345,6 +345,14 @@ public class EcmaScriptSafeRegexTest {
         assertThat(EcmaScriptSafeRegex.isSafe("\\h+")).isFalse();
     }
 
+    @Test
+    public void rejectsWhitespaceClassesWhoseMeaningDiffersBetweenEngines() {
+        // Java's \s is ASCII-only by default; ECMAScript's includes NBSP and 
friends, so
+        // ^\S+$ accepts a value containing NBSP on the server and rejects it 
in the browser
+        assertThat(EcmaScriptSafeRegex.isSafe("^\\S+$")).isFalse();
+        assertThat(EcmaScriptSafeRegex.isSafe("\\s*")).isFalse();
+    }
+
     @Test
     public void rejectsPossessiveQuantifiers() {
         assertThat(EcmaScriptSafeRegex.isSafe("\\d++")).isFalse();
@@ -402,8 +410,15 @@ package org.apache.struts2.components;
  */
 public final class EcmaScriptSafeRegex {
 
-    /** Escapes with identical meaning in both engines. */
-    private static final String ALLOWED_ESCAPES = 
"dDwWsSbBnrtf\\.*+?()[]{}|^$/-";
+    /**
+     * Escapes with identical meaning in both engines.
+     * <p>
+     * {@code \s} and {@code \S} are deliberately absent. Java's {@code \s} is 
ASCII-only by default
+     * while ECMAScript's is the wider Unicode set, so {@code ^\S+$} accepts a 
value containing NBSP
+     * on the server and rejects it in the browser. {@code \d} and {@code \w} 
are safe — both engines
+     * are ASCII-only for those, and JavaScript never widens them.
+     */
+    private static final String ALLOWED_ESCAPES = 
"dDwWbBnrtf\\.*+?()[]{}|^$/-";
 
     private EcmaScriptSafeRegex() {
     }
@@ -471,7 +486,7 @@ public final class EcmaScriptSafeRegex {
 - [ ] **Step 4: Run the test to verify it passes**
 
 Run: `mvn test -DskipAssembly -pl core -Dtest=EcmaScriptSafeRegexTest`
-Expected: PASS, 6 tests.
+Expected: PASS, 7 tests.
 
 - [ ] **Step 5: Commit**
 
diff --git 
a/docs/superpowers/specs/2026-08-24-html5-constraint-validation-design.md 
b/docs/superpowers/specs/2026-08-24-html5-constraint-validation-design.md
index 234999b0b..8e7c5c7e3 100644
--- a/docs/superpowers/specs/2026-08-24-html5-constraint-validation-design.md
+++ b/docs/superpowers/specs/2026-08-24-html5-constraint-validation-design.md
@@ -119,10 +119,18 @@ This is the least-solved part of the design and the 
strongest argument for the p
 
 A denylist of Java-only constructs (`\p{Alpha}`, possessive quantifiers, 
`\A`/`\z`, lookbehind) violates the
 never-false-reject rule the first time it misses one: a missed construct 
becomes a `pattern` the browser
-interprets differently. So detection is an **allowlist** — literals, `\d \w 
\s` and their negations,
+interprets differently. So detection is an **allowlist** — literals, `\d` and 
`\w` and their negations,
 character classes without POSIX or Unicode property syntax, grouping, 
alternation, anchors, and bounded
 quantifiers. Anything outside it emits no `pattern`.
 
+**`\s` and `\S` are excluded, and this is the rule's first real test.** Java's 
`\s` is ASCII-only by default
+(`[ \t\n\x0B\f\r]`); ECMAScript's is always the wider Unicode set — NBSP, ``, 
`
`, the ` `
+range. For a rule as ordinary as `^\S+$`, a value containing NBSP satisfies 
Java's `\S` and fails the
+browser's, so the server would accept input the browser silently refuses to 
submit. `\d` and `\w` are safe:
+both engines are ASCII-only for those by default, and JavaScript never widens 
them. An earlier draft of this
+spec listed `\s` among the safe escapes — that was wrong, and the governing 
rule outranks its own example
+list.
+
 This is conservative to the point that some legitimate regexes will silently 
get no client-side check. That
 is the correct failure direction under the agreed rule, and it is the piece 
most likely to need tuning after
 real use.

Reply via email to