LibURL/Pattern: Ignore extra RegExp captures in match result

execResult may contain additional captures from nested groups
in user-provided regexp parts, exceeding the number of
URLPattern groups.

Fixes a crash in the updated WPT test.

See: https://github.com/whatwg/urlpattern/commit/203d435c32
This commit is contained in:
Shannon Booth 2026-03-20 02:06:46 +01:00 committed by Jelle Raaijmakers
parent e817b13c2a
commit f4f6aefe32
3 changed files with 182 additions and 6 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2025, Shannon Booth <shannon@serenityos.org>
* Copyright (c) 2025-2026, Shannon Booth <shannon@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -285,8 +285,8 @@ Component::Result Component::create_match_result(String const& input, regex::Reg
OrderedHashMap<String, Variant<String, Empty>> groups;
// 4. Let index be 1.
// 5. While index is less than Get(execResult, "length"):
for (size_t index = 1; index <= exec_result.n_capture_groups; ++index) {
// 5. While index is less than or equal to components group name lists size:
for (size_t index = 1; index <= group_name_list.size(); ++index) {
auto const& capture = exec_result.capture_group_matches[0][index - 1];
// 1. Let name be components group name list[index 1].

View file

@ -1,8 +1,8 @@
Harness status: OK
Found 354 tests
Found 367 tests
354 Pass
367 Pass
Pass Loading data...
Pass Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/bar"}]
Pass Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/ba"}]
@ -356,4 +356,17 @@ Pass Pattern: ["#foo","https://example.com/?q=*&v=?&hmm={}&umm=()"] Inputs: ["ht
Pass Pattern: [{"pathname":"/([[a-z]--a])"}] Inputs: [{"pathname":"/a"}]
Pass Pattern: [{"pathname":"/([[a-z]--a])"}] Inputs: [{"pathname":"/z"}]
Pass Pattern: [{"pathname":"/([\\d&&[0-1]])"}] Inputs: [{"pathname":"/0"}]
Pass Pattern: [{"pathname":"/([\\d&&[0-1]])"}] Inputs: [{"pathname":"/3"}]
Pass Pattern: [{"pathname":"/([\\d&&[0-1]])"}] Inputs: [{"pathname":"/3"}]
Pass Pattern: [{"protocol":"http","hostname":"example.com/ignoredpath"}] Inputs: ["http://example.com/"]
Pass Pattern: [{"protocol":"http","hostname":"example.com\\?ignoredsearch"}] Inputs: ["http://example.com/"]
Pass Pattern: [{"protocol":"http","hostname":"example.com#ignoredhash"}] Inputs: ["http://example.com/"]
Pass Pattern: ["https://www.example.com/*"] Inputs: ["https://www.example.com/x"]
Pass Pattern: ["https://www.example.com/*"] Inputs: ["https://www.example.com/xyz"]
Pass Pattern: ["https://www.example.com/*"] Inputs: ["https://www.example.com/example"]
Pass Pattern: ["https://www.example.com/*"] Inputs: ["https://www.example.com/text"]
Pass Pattern: ["https://www.example.com/*"] Inputs: ["https://www.example.com/path/with/x"]
Pass Pattern: [{"hostname":":domain(.*)"}] Inputs: [{"hostname":"localhost"}]
Pass Pattern: ["((?R)):"] Inputs: undefined
Pass Pattern: ["(\\H):"] Inputs: undefined
Pass Pattern: [{"pathname":"/:foo((?<x>a))"}] Inputs: [{"pathname":"/a"}]
Pass Pattern: [{"pathname":"/foo/(bar(?<x>baz))"}] Inputs: [{"pathname":"/foo/barbaz"}]

View file

@ -2991,5 +2991,168 @@
"pattern": [{ "pathname": "/([\\d&&[0-1]])" }],
"inputs": [{ "pathname": "/3" }],
"expected_match": null
},
{
"pattern": [{ "protocol": "http", "hostname": "example.com/ignoredpath" }],
"inputs": ["http://example.com/"],
"expected_obj": {
"protocol": "http",
"hostname": "example.com",
"pathname": "*"
},
"expected_match": {
"protocol": { "input": "http", "groups": {} },
"hostname": { "input": "example.com", "groups": {} },
"pathname": { "input": "/", "groups": { "0": "/" } }
}
},
{
"pattern": [{ "protocol": "http", "hostname": "example.com\\?ignoredsearch" }],
"inputs": ["http://example.com/"],
"expected_obj": {
"protocol": "http",
"hostname": "example.com",
"search": "*"
},
"expected_match": {
"protocol": { "input": "http", "groups": {} },
"hostname": { "input": "example.com", "groups": {} },
"pathname": { "input": "/", "groups": { "0": "/" } }
}
},
{
"pattern": [{ "protocol": "http", "hostname": "example.com#ignoredhash" }],
"inputs": ["http://example.com/"],
"expected_obj": {
"protocol": "http",
"hostname": "example.com",
"hash": "*"
},
"expected_match": {
"protocol": { "input": "http", "groups": {} },
"hostname": { "input": "example.com", "groups": {} },
"pathname": { "input": "/", "groups": { "0": "/" } }
}
},
{
"pattern": ["https://www.example.com/*"],
"inputs": ["https://www.example.com/x"],
"exactly_empty_components": ["port"],
"expected_obj": {
"protocol": "https",
"hostname": "www.example.com",
"pathname": "/*"
},
"expected_match": {
"protocol": { "input": "https", "groups": {} },
"hostname": { "input": "www.example.com", "groups": {} },
"pathname": { "input": "/x", "groups": { "0": "x" } }
}
},
{
"pattern": ["https://www.example.com/*"],
"inputs": ["https://www.example.com/xyz"],
"exactly_empty_components": ["port"],
"expected_obj": {
"protocol": "https",
"hostname": "www.example.com",
"pathname": "/*"
},
"expected_match": {
"protocol": { "input": "https", "groups": {} },
"hostname": { "input": "www.example.com", "groups": {} },
"pathname": { "input": "/xyz", "groups": { "0": "xyz" } }
}
},
{
"pattern": ["https://www.example.com/*"],
"inputs": ["https://www.example.com/example"],
"exactly_empty_components": ["port"],
"expected_obj": {
"protocol": "https",
"hostname": "www.example.com",
"pathname": "/*"
},
"expected_match": {
"protocol": { "input": "https", "groups": {} },
"hostname": { "input": "www.example.com", "groups": {} },
"pathname": { "input": "/example", "groups": { "0": "example" } }
}
},
{
"pattern": ["https://www.example.com/*"],
"inputs": ["https://www.example.com/text"],
"exactly_empty_components": ["port"],
"expected_obj": {
"protocol": "https",
"hostname": "www.example.com",
"pathname": "/*"
},
"expected_match": {
"protocol": { "input": "https", "groups": {} },
"hostname": { "input": "www.example.com", "groups": {} },
"pathname": { "input": "/text", "groups": { "0": "text" } }
}
},
{
"pattern": ["https://www.example.com/*"],
"inputs": ["https://www.example.com/path/with/x"],
"exactly_empty_components": ["port"],
"expected_obj": {
"protocol": "https",
"hostname": "www.example.com",
"pathname": "/*"
},
"expected_match": {
"protocol": { "input": "https", "groups": {} },
"hostname": { "input": "www.example.com", "groups": {} },
"pathname": { "input": "/path/with/x", "groups": { "0": "path/with/x" } }
}
},
{
"pattern": [{ "hostname": ":domain(.*)" }],
"inputs": [{ "hostname": "localhost" }],
"expected_obj": {
"hostname": ":domain(.*)"
},
"expected_match": {
"hostname": { "input": "localhost", "groups": { "domain" : "localhost"} }
}
},
{
"pattern": ["((?R)):"],
"expected_obj": "error"
},
{
"pattern": ["(\\H):"],
"expected_obj": "error"
},
{
"pattern": [
{"pathname": "/:foo((?<x>a))"}
],
"inputs": [
{"pathname": "/a"}
],
"expected_match": {
"pathname": {
"input": "/a",
"groups": {"foo": "a"}
}
}
},
{
"pattern": [
{"pathname": "/foo/(bar(?<x>baz))"}
],
"inputs": [
{"pathname": "/foo/barbaz"}
],
"expected_match": {
"pathname": {
"input": "/foo/barbaz",
"groups": {"0": "barbaz"}
}
}
}
]