From 62456f0d4fb3e14ad7006b17ebcf1b90f5d88835 Mon Sep 17 00:00:00 2001 From: Christoph Fabianek Date: Thu, 8 Oct 2026 22:04:34 +0200 Subject: [PATCH] Treat an escaped caret in JSONPath patterns as a literal (dpp-criteria issue 7) --- app/services/i_regexp.rb | 4 ++-- test/services/i_regexp_test.rb | 11 +++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/services/i_regexp.rb b/app/services/i_regexp.rb index ff3dcaa..baa9e52 100644 --- a/app/services/i_regexp.rb +++ b/app/services/i_regexp.rb @@ -7,7 +7,8 @@ # outside a character class (RFC 9485 lists them as ordinary characters, its # mappings in section 5 and the JSONPath Compliance Test Suite treat them as # anchors): such a pattern is unusable (Anchored), like an invalid one -# (Invalid); `\^` counts as well. Differences to Ruby and ECMA-262 that +# (Invalid). An escaped `\^` is a literal ^ (dpp-criteria issue #7); `\$` is +# not valid I-Regexp (use `[$]`). Differences to Ruby and ECMA-262 that # matter: `.` matches any character except LF and CR; only the escapes \( \) \* # \+ \- \. \? \[ \\ \] \n \r \t \{ \| \} and the category escapes \p{..} and \P{..} exist (no \d, # \w, \s); there are no lazy quantifiers, no {,m} and no non-capturing or other @@ -155,7 +156,6 @@ def escape_outside c = take return category(c) if %w[p P].include?(c) - raise Anchored, "\\^" if c == "^" char(single_escape(c)) end diff --git a/test/services/i_regexp_test.rb b/test/services/i_regexp_test.rb index 65dfefc..5b34b01 100644 --- a/test/services/i_regexp_test.rb +++ b/test/services/i_regexp_test.rb @@ -22,12 +22,19 @@ class IRegexpTest < ActiveSupport::TestCase end test "^ or $ outside a character class makes the pattern unusable (dpp-criteria 4b17bb8)" do - ["^ab.*", ".*bc$", "^[Bb]atter", "59040|PCDS$", "(^a)", "a\\^b"].each do |pattern| - assert_match(/\Acontains (\^|\$|\\\^) outside a character class/, IRegexp.problem(pattern), pattern) + ["^ab.*", ".*bc$", "^[Bb]atter", "59040|PCDS$", "(^a)"].each do |pattern| + assert_match(/\Acontains (\^|\$) outside a character class/, IRegexp.problem(pattern), pattern) refute IRegexp.search?(pattern, pattern.delete("^$\\")), pattern end end + test "an escaped \\^ is a literal ^, an escaped \\$ is not I-Regexp (dpp-criteria issue #7)" do + assert_nil IRegexp.problem("a\\^b") + assert IRegexp.match?("a\\^b", "a^b") + refute IRegexp.search?("a\\^b", "ab") + assert_match(/not an I-Regexp escape/, IRegexp.problem("a\\$")) + end + test "^ and $ inside a character class are allowed" do assert_nil IRegexp.problem("[^a]") assert_nil IRegexp.problem("[a^]")