Skip to content

Commit d97a368

Browse files
Reject non-ASCII whitespace in email format (#1267)
The email format accepted values containing a non-breaking space (U+00A0) or other non-ASCII whitespace because the underlying validator's regex only excludes ASCII whitespace. As a result an email with a leading U+00A0 was treated as valid, while the same address with a regular leading space was correctly rejected. EmailFormat now rejects any value containing a non-ASCII whitespace character before delegating to the validator. Character.isWhitespace deliberately excludes U+00A0, so Character.isSpaceChar is also checked. All-ASCII emails (including quoted local parts with spaces such as "joe bloggs"@example.com) are unaffected. Adds EmailFormatTest, following the existing per-format test convention. Closes #1164 Co-authored-by: el-psy-kongroo-d <307969302+el-psy-kongroo-d@users.noreply.github.com>
1 parent 12b85e4 commit d97a368

2 files changed

Lines changed: 85 additions & 0 deletions

File tree

src/main/java/com/networknt/schema/format/EmailFormat.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,26 @@ public EmailFormat(EmailValidator emailValidator) {
3535

3636
@Override
3737
public boolean matches(ExecutionContext executionContext, String value) {
38+
if (containsNonAsciiWhitespace(value)) {
39+
return false;
40+
}
3841
return this.emailValidator.isValid(value);
3942
}
43+
44+
/**
45+
* Whether {@code value} contains a non-ASCII whitespace character such as
46+
* {@code U+00A0} NON-BREAKING SPACE. The underlying validator only excludes
47+
* ASCII whitespace, so such characters would otherwise be accepted in an
48+
* email address. Note that {@link Character#isWhitespace(char)} deliberately
49+
* excludes {@code U+00A0}, hence the additional
50+
* {@link Character#isSpaceChar(char)} check.
51+
*/
52+
private static boolean containsNonAsciiWhitespace(String value) {
53+
if (value == null) {
54+
return false;
55+
}
56+
return value.codePoints().anyMatch(ch -> ch > 0x7F && (Character.isWhitespace(ch) || Character.isSpaceChar(ch)));
57+
}
4058

4159
@Override
4260
public String getName() {
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright (c) 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.networknt.schema.format;
17+
18+
import static org.junit.jupiter.api.Assertions.assertFalse;
19+
import static org.junit.jupiter.api.Assertions.assertTrue;
20+
21+
import java.util.List;
22+
23+
import org.junit.jupiter.api.Test;
24+
25+
import com.networknt.schema.Error;
26+
import com.networknt.schema.InputFormat;
27+
import com.networknt.schema.Schema;
28+
import com.networknt.schema.SchemaRegistry;
29+
import com.networknt.schema.SpecificationVersion;
30+
31+
class EmailFormatTest {
32+
33+
/** U+00A0 NON-BREAKING SPACE, built from its code point so no invisible character sits in the source. */
34+
private static final String NBSP = new String(Character.toChars(0x00A0));
35+
36+
/**
37+
* Validates {@code email} against a {@code {"format": "email"}} schema with
38+
* format assertions turned on, and returns the validation errors.
39+
*/
40+
private List<Error> validateEmail(String email) {
41+
String schemaData = "{\r\n"
42+
+ " \"format\": \"email\"\r\n"
43+
+ "}";
44+
String inputData = "\"" + email + "\"";
45+
Schema schema = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2020_12).getSchema(schemaData);
46+
return schema.validate(inputData, InputFormat.JSON,
47+
executionContext -> executionContext.executionConfig(executionConfig -> executionConfig.formatAssertionsEnabled(true)));
48+
}
49+
50+
/** Sanity check that the test harness accepts a plainly valid email. */
51+
@Test
52+
void validEmailShouldPass() {
53+
List<Error> messages = validateEmail("name@email.com");
54+
assertTrue(messages.isEmpty(), "a plainly valid email should pass");
55+
}
56+
57+
/**
58+
* Reproduces issue #1164: a leading non-breaking space (U+00A0) should make
59+
* the email invalid, just like a regular leading space does, but it is
60+
* currently accepted because the validator only excludes ASCII whitespace.
61+
*/
62+
@Test
63+
void emailWithLeadingNbspShouldFail() {
64+
List<Error> messages = validateEmail(NBSP + "name@email.com");
65+
assertFalse(messages.isEmpty(), "email with a leading non-breaking space (U+00A0) should be invalid");
66+
}
67+
}

0 commit comments

Comments
 (0)