From ca149cdfcc249ddd86ec4a13f54fc2ae613e74de Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Mon, 27 Jul 2026 10:10:04 -0300 Subject: [PATCH] Update email schema to use `format-assertion` Signed-off-by: Juan Cruz Viotti --- schemas/2020-12/ietf/email/address.json | 50 +++---------------------- test/ietf/email/address.test.json | 20 +++++----- 2 files changed, 15 insertions(+), 55 deletions(-) diff --git a/schemas/2020-12/ietf/email/address.json b/schemas/2020-12/ietf/email/address.json index cb124882..c52d072f 100644 --- a/schemas/2020-12/ietf/email/address.json +++ b/schemas/2020-12/ietf/email/address.json @@ -1,56 +1,16 @@ { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "title": "RFC 5322 Email Address (Addr-Spec)", + "$schema": "urn:sourcemeta:std:ietf:jsonschema:2020-12:dialect:format-assertion", + "title": "RFC 5321 Email Address (Addr-Spec)", "description": "A specific Internet identifier that represents an e-mail box to which messages are delivered", "examples": [ "simple@example.com", + "first.last@sub.example.com", "\"very.unusual.@.unusual.com\"@example.com", - "user@[IPv6:2001:db8::1]", - "(comment)john.smith@(comment)example.com(comment)", - "jo(comment)hn@example.com", - "user@[abc.def]" + "user@[IPv6:2001:db8::1]" ], "x-license": "https://github.com/sourcemeta/std/blob/main/LICENSE", - "x-links": [ "https://www.rfc-editor.org/rfc/rfc5322#section-3.4.1" ], + "x-links": [ "https://www.rfc-editor.org/rfc/rfc5321#section-4.1.2" ], "type": "string", - "anyOf": [ - { - "$comment": "Unquoted (dot-atom) local-part with dot-atom domain — both allow inline single-level comments", - "pattern": "^(?:(?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*)(?:[A-Za-z0-9!#$%&'*+\\/=?^_`{|}~-]+(?:(?:[ \\t]*\\((?:[^()\\\\]|\\\\.)*\\)[ \\t]*[A-Za-z0-9!#$%&'*+\\/=?^_`{|}~-]+)|(?:\\.[A-Za-z0-9!#$%&'*+\\/=?^_`{|}~-]+))*)(?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*@(?:(?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?))(?:((?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*\\.(?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)))*(?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*$" - }, - { - "$comment": "Quoted local-part (with escapes) + dot-atom domain. Comments allowed around the quoted string and around domain labels", - "pattern": "^(?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*(?:\"(?:[^\"\\\\\\r]|\\\\.)*\")(?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*@(?:(?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?))(?:((?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*\\.(?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)))*(?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*$" - }, - { - "$comment": "Unquoted local-part + domain-literal (arbitrary dtext / quoted-pairs inside brackets). Comments allowed around tokens", - "pattern": "^(?:(?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*)(?:[A-Za-z0-9!#$%&'*+\\/=?^_`{|}~-]+(?:(?:[ \\t]*\\((?:[^()\\\\]|\\\\.)*\\)[ \\t]*[A-Za-z0-9!#$%&'*+\\/=?^_`{|}~-]+)|(?:\\.[A-Za-z0-9!#$%&'*+\\/=?^_`{|}~-]+))*)(?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*@(?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*\\[(?:[^\\[\\]\\\\\\r]|\\\\.)*\\](?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*$" - }, - { - "$comment": "Quoted local-part + domain-literal. Comments allowed around quoted local and around the literal", - "pattern": "^(?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*(?:\"(?:[^\"\\\\\\r]|\\\\.)*\")(?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*@(?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*\\[(?:[^\\[\\]\\\\\\r]|\\\\.)*\\](?:[ \\t]|\\((?:[^()\\\\]|\\\\.)*\\))*$" - } - ], - "not": { - "anyOf": [ - { - "$comment": "Disallow bare IPv4 address in domain (must be bracketed)", - "pattern": "@([0-9]{1,3}\\.){3}[0-9]{1,3}(?:$|\\s)" - }, - { - "$comment": "Disallow IPv6 literal without brackets", - "pattern": "@IPv6:" - }, - { - "$comment": "Disallow numeric-only final label (numeric TLDs like example.123)", - "pattern": "@[^@\\s]*\\.\\d+(?:$|\\s)" - }, - { - "$comment": "Reject addresses that start with a single-quote delimiter", - "pattern": "^'" - } - ] - }, "format": "email", "minLength": 1 } diff --git a/test/ietf/email/address.test.json b/test/ietf/email/address.test.json index 6788efbe..796eea9f 100644 --- a/test/ietf/email/address.test.json +++ b/test/ietf/email/address.test.json @@ -129,7 +129,7 @@ { "description": "Comments around local, domain, and address", "data": "(comment)john.smith@(comment)example.com(comment)", - "valid": true + "valid": false }, { "description": "Quoted local-part with space and letters", @@ -139,7 +139,7 @@ { "description": "Domain literal with letters and dot", "data": "user@[abc.def]", - "valid": true + "valid": false }, { "description": "All uppercase local and domain", @@ -169,12 +169,12 @@ { "description": "Email with comment in domain", "data": "john.smith@(comment)example.com", - "valid": true + "valid": false }, { "description": "Email with comment in local part", "data": "jo(comment)hn@example.com", - "valid": true + "valid": false }, { "description": "Complex quoted local-part with many allowed symbols", @@ -249,7 +249,7 @@ { "description": "Numeric TLD in domain", "data": "user@example.123", - "valid": false + "valid": true }, { "description": "Multiple @ symbols", @@ -327,14 +327,14 @@ "valid": false }, { - "description": "Uppercase numeric TLD (invalid as TLD numeric only)", + "description": "Uppercase numeric TLD", "data": "user@EXAMPLE.123", - "valid": false + "valid": true }, { "description": "IP address without brackets in domain", "data": "user@123.123.123.123", - "valid": false + "valid": true }, { "description": "IPv6 address without brackets", @@ -342,9 +342,9 @@ "valid": false }, { - "description": "Single quote used as delimiter (invalid)", + "description": "Single quote in local part", "data": "'test'@example.com", - "valid": false + "valid": true }, { "description": "Double quotes misplacement",