From bfcd1a3831b48282facaa4482e5ba7860a5ae896 Mon Sep 17 00:00:00 2001 From: Simon Hornby Date: Wed, 25 Feb 2026 12:07:53 +0200 Subject: [PATCH 1/4] fix: unnests the user id in the regex suite, this is technically invalid and a lot of SDKs do not like it --- .../21-regex-constraint-operators.json | 140 +++++------------- 1 file changed, 35 insertions(+), 105 deletions(-) diff --git a/specifications/21-regex-constraint-operators.json b/specifications/21-regex-constraint-operators.json index d7cae88..156d691 100644 --- a/specifications/21-regex-constraint-operators.json +++ b/specifications/21-regex-constraint-operators.json @@ -317,9 +317,7 @@ { "description": "Basic email match should be enabled when regex matches", "context": { - "properties": { - "userId": "test@example.com" - } + "userId": "test@example.com" }, "toggleName": "R01.literal_and_anchors", "expectedResult": true @@ -327,9 +325,7 @@ { "description": "Broken regex should not match anything", "context": { - "properties": { - "userId": "not-an-email" - } + "userId": "not-an-email" }, "toggleName": "R01.literal_and_anchors", "expectedResult": false @@ -337,9 +333,7 @@ { "description": "Invalid regex should result in toggle being disabled", "context": { - "properties": { - "userId": "any-value" - } + "userId": "any-value" }, "toggleName": "invalid-regex-defaults-to-false", "expectedResult": false @@ -347,9 +341,7 @@ { "description": "Null value should result in toggle being disabled", "context": { - "properties": { - "userId": null - } + "userId": null }, "toggleName": "R01.literal_and_anchors", "expectedResult": false @@ -363,9 +355,7 @@ { "description": "R01 matches basic email-ish", "context": { - "properties": { - "userId": "test@example.com" - } + "userId": "test@example.com" }, "toggleName": "R01.literal_and_anchors", "expectedResult": true @@ -373,9 +363,7 @@ { "description": "R01 rejects missing @", "context": { - "properties": { - "userId": "not-an-email" - } + "userId": "not-an-email" }, "toggleName": "R01.literal_and_anchors", "expectedResult": false @@ -383,9 +371,7 @@ { "description": "R02 matches user + 3 digits", "context": { - "properties": { - "userId": "user123" - } + "userId": "user123" }, "toggleName": "R02.perl_digit_class", "expectedResult": true @@ -393,9 +379,7 @@ { "description": "R02 rejects wrong digit count", "context": { - "properties": { - "userId": "user12" - } + "userId": "user12" }, "toggleName": "R02.perl_digit_class", "expectedResult": false @@ -403,9 +387,7 @@ { "description": "R03 matches ASCII alpha only", "context": { - "properties": { - "userId": "AlphaBeta" - } + "userId": "AlphaBeta" }, "toggleName": "R03.ascii_class", "expectedResult": true @@ -413,9 +395,7 @@ { "description": "R03 rejects digits", "context": { - "properties": { - "userId": "Alpha1" - } + "userId": "Alpha1" }, "toggleName": "R03.ascii_class", "expectedResult": false @@ -423,9 +403,7 @@ { "description": "R04 matches whole word cat", "context": { - "properties": { - "userId": "a cat b" - } + "userId": "a cat b" }, "toggleName": "R04.word_boundary_ascii", "expectedResult": true @@ -433,9 +411,7 @@ { "description": "R04 does not match inside 'concatenate'", "context": { - "properties": { - "userId": "concatenate" - } + "userId": "concatenate" }, "toggleName": "R04.word_boundary_ascii", "expectedResult": false @@ -443,9 +419,7 @@ { "description": "R05 matches foo", "context": { - "properties": { - "userId": "foo" - } + "userId": "foo" }, "toggleName": "R05.alternation_prefer_left", "expectedResult": true @@ -453,9 +427,7 @@ { "description": "R05 matches fo", "context": { - "properties": { - "userId": "fo" - } + "userId": "fo" }, "toggleName": "R05.alternation_prefer_left", "expectedResult": true @@ -463,9 +435,7 @@ { "description": "R05 rejects f", "context": { - "properties": { - "userId": "f" - } + "userId": "f" }, "toggleName": "R05.alternation_prefer_left", "expectedResult": false @@ -473,9 +443,7 @@ { "description": "R06 matches abc (optional 'ab')", "context": { - "properties": { - "userId": "abc" - } + "userId": "abc" }, "toggleName": "R06.grouping_and_optional", "expectedResult": true @@ -483,9 +451,7 @@ { "description": "R06 matches c", "context": { - "properties": { - "userId": "c" - } + "userId": "c" }, "toggleName": "R06.grouping_and_optional", "expectedResult": true @@ -493,9 +459,7 @@ { "description": "R06 rejects ac (missing b)", "context": { - "properties": { - "userId": "ac" - } + "userId": "ac" }, "toggleName": "R06.grouping_and_optional", "expectedResult": false @@ -503,9 +467,7 @@ { "description": "R07 matches aaaa (4 a's)", "context": { - "properties": { - "userId": "aaaa" - } + "userId": "aaaa" }, "toggleName": "R07.repetition_range_limits", "expectedResult": true @@ -513,9 +475,7 @@ { "description": "R07 rejects a (too short)", "context": { - "properties": { - "userId": "a" - } + "userId": "a" }, "toggleName": "R07.repetition_range_limits", "expectedResult": false @@ -523,9 +483,7 @@ { "description": "R07 rejects aaaaa (too long)", "context": { - "properties": { - "userId": "aaaaa" - } + "userId": "aaaaa" }, "toggleName": "R07.repetition_range_limits", "expectedResult": false @@ -533,9 +491,7 @@ { "description": "R08 non-greedy still matches full string ending in b", "context": { - "properties": { - "userId": "a___b___b" - } + "userId": "a___b___b" }, "toggleName": "R08.greedy_vs_nongreedy", "expectedResult": true @@ -543,9 +499,7 @@ { "description": "R08 rejects missing ending b", "context": { - "properties": { - "userId": "a___b___c" - } + "userId": "a___b___c" }, "toggleName": "R08.greedy_vs_nongreedy", "expectedResult": false @@ -553,9 +507,7 @@ { "description": "R09 (?i) matches different case", "context": { - "properties": { - "userId": "AbC" - } + "userId": "AbC" }, "toggleName": "R09.inline_flag_i", "expectedResult": true @@ -563,9 +515,7 @@ { "description": "R09 (?i) rejects extra chars", "context": { - "properties": { - "userId": "zAbCz" - } + "userId": "zAbCz" }, "toggleName": "R09.inline_flag_i", "expectedResult": false @@ -573,9 +523,7 @@ { "description": "Global case-insensitive matches different case", "context": { - "properties": { - "userId": "AbC" - } + "userId": "AbC" }, "toggleName": "Global.case.insensitive", "expectedResult": true @@ -583,9 +531,7 @@ { "description": "Regex constraint is case sensitive by default", "context": { - "properties": { - "userId": "Cat" - } + "userId": "Cat" }, "toggleName": "regex.case.sensitive", "expectedResult": false @@ -593,9 +539,7 @@ { "description": "Regex constraint is case sensitive by default (validate)", "context": { - "properties": { - "userId": "cat" - } + "userId": "cat" }, "toggleName": "regex.case.sensitive", "expectedResult": true @@ -603,9 +547,7 @@ { "description": "Inverted constraint should enable when regex does not match", "context": { - "properties": { - "userId": "xyz" - } + "userId": "xyz" }, "toggleName": "Inverted.constraint", "expectedResult": true @@ -613,9 +555,7 @@ { "description": "Inverted constraint should disable when regex matches", "context": { - "properties": { - "userId": "abc" - } + "userId": "abc" }, "toggleName": "Inverted.constraint", "expectedResult": false @@ -623,9 +563,7 @@ { "description": "R10 (?m) matches line 'foo' inside multiline string", "context": { - "properties": { - "userId": "x\nfoo\ny" - } + "userId": "x\nfoo\ny" }, "toggleName": "R10.inline_flag_m", "expectedResult": true @@ -633,9 +571,7 @@ { "description": "R10 without foo line would fail", "context": { - "properties": { - "userId": "x\nfo\ny" - } + "userId": "x\nfo\ny" }, "toggleName": "R10.inline_flag_m", "expectedResult": false @@ -643,9 +579,7 @@ { "description": "R11 (?s) dot matches newline", "context": { - "properties": { - "userId": "a\nb" - } + "userId": "a\nb" }, "toggleName": "R11.inline_flag_s_dotall", "expectedResult": true @@ -653,9 +587,7 @@ { "description": "R90 lookahead should be rejected upstream; if it leaks through, it must not enable", "context": { - "properties": { - "userId": "ab" - } + "userId": "ab" }, "toggleName": "R90.reject_lookahead", "expectedResult": false @@ -663,9 +595,7 @@ { "description": "R91 backreference should be rejected upstream; if it leaks through, it must not enable", "context": { - "properties": { - "userId": "aa" - } + "userId": "aa" }, "toggleName": "R91.reject_backreference", "expectedResult": false From 6d66241d2db29b98c653bad7cb7de12f901a28cd Mon Sep 17 00:00:00 2001 From: Simon Hornby Date: Wed, 25 Feb 2026 12:14:14 +0200 Subject: [PATCH 2/4] move null test to properties --- .../21-regex-constraint-operators.json | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/specifications/21-regex-constraint-operators.json b/specifications/21-regex-constraint-operators.json index 156d691..37b8a7d 100644 --- a/specifications/21-regex-constraint-operators.json +++ b/specifications/21-regex-constraint-operators.json @@ -57,6 +57,24 @@ } ] }, + { + "name": "R01.literal_and_anchors.referencing.property", + "description": "Anchors ^ $ basic email-ish check", + "enabled": true, + "strategies": [ + { + "name": "default", + "parameters": {}, + "constraints": [ + { + "contextName": "nested", + "operator": "REGEX", + "value": "^[^@]+@[^@]+$" + } + ] + } + ] + }, { "name": "R02.perl_digit_class", "description": "\\d and quantifiers", @@ -341,7 +359,9 @@ { "description": "Null value should result in toggle being disabled", "context": { - "userId": null + "properties": { + "nested": null + } }, "toggleName": "R01.literal_and_anchors", "expectedResult": false From e2f8acbfc60a8b4dc47adedc0ad3aec0e1f0fba7 Mon Sep 17 00:00:00 2001 From: Simon Hornby Date: Wed, 25 Feb 2026 12:18:12 +0200 Subject: [PATCH 3/4] chore: add sanity check --- specifications/21-regex-constraint-operators.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/specifications/21-regex-constraint-operators.json b/specifications/21-regex-constraint-operators.json index 37b8a7d..1d4ff3e 100644 --- a/specifications/21-regex-constraint-operators.json +++ b/specifications/21-regex-constraint-operators.json @@ -363,7 +363,17 @@ "nested": null } }, - "toggleName": "R01.literal_and_anchors", + "toggleName": "R01.literal_and_anchors.referencing.property", + "expectedResult": false + }, + { + "description": "Sanity check for 'Null value should result in toggle being disabled'", + "context": { + "properties": { + "nested": null + } + }, + "toggleName": "R01.literal_and_anchors.referencing.property", "expectedResult": false }, { From 2e89cf54bae85bb59bcb7bde9362cc9f0f595d71 Mon Sep 17 00:00:00 2001 From: Simon Hornby Date: Wed, 25 Feb 2026 12:23:21 +0200 Subject: [PATCH 4/4] chore: add sanity check --- specifications/21-regex-constraint-operators.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/specifications/21-regex-constraint-operators.json b/specifications/21-regex-constraint-operators.json index 37b8a7d..dd4112a 100644 --- a/specifications/21-regex-constraint-operators.json +++ b/specifications/21-regex-constraint-operators.json @@ -363,9 +363,19 @@ "nested": null } }, - "toggleName": "R01.literal_and_anchors", + "toggleName": "R01.literal_and_anchors.referencing.property", "expectedResult": false }, + { + "description": "Sanity check for 'Null value should result in toggle being disabled'", + "context": { + "properties": { + "nested": "test@example.com" + } + }, + "toggleName": "R01.literal_and_anchors.referencing.property", + "expectedResult": true + }, { "description": "Undefined value should result in toggle being disabled", "context": {},