Summary
RelativeResourceTemplate::match() returns a false-positive match with wrong, internally-contradictory bindings when a template contains a ** path wildcard followed by wildcard variable segments, and the input path has fewer segments than required. Such paths should be rejected (the library's own test suite asserts this), but instead a match is returned and PHP emits "Undefined array key -N" warnings.
Steps to reproduce
<?php
require 'vendor/autoload.php';
use Google\ApiCore\PathTemplate;
// Template: a, **, {y=*}, {x=*}, f (5 segments)
$tpl = new PathTemplate('a/**/{y=*}/{x=*}/f');
// Path 'a/e/f' has only 3 slash-pieces. Minimum valid path needs 5
// (the '**' must consume at least one segment). This must be REJECTED.
var_dump($tpl->match('a/e/f'));
Actual output
array(3) {
["$0"] => string(1) "e"
["y"] => string(1) "a"
["x"] => string(1) "e"
}
The path a/e/f does not have enough segments to satisfy the template (minimum is 5), yet it "matches". Worse, the bindings are impossible: path-piece a is consumed both as the literal a and as variable y, and piece e is bound to both $0 (the **) and x.
PHP also logs these warnings during the match:
Warning: Undefined array key -1 in .../RelativeResourceTemplate.php on line ~196
Expected behavior
match() should throw ValidationException and matches() should return false, exactly as the library's own test suite already requires for the same class of input:
// tests/Unit/ResourceTemplate/RelativeResourceTemplateTest.php
['foo/**/bar', 'foo/bar', 'Missing middle wildcard'], // must NOT match
Root cause
In src/ResourceTemplate/RelativeResourceTemplate.php (~line 178):
$doubleWildcardPieceCount =
count($slashPathPieces) - $flattenedKeySegmentTuplesCount + 1;
When the path has fewer slash-pieces than the template's flattened segments, $doubleWildcardPieceCount becomes negative. This is then passed as the length to array_slice():
$doubleWildcardPathPieces = array_slice(
$slashPathPieces,
$pathPiecesIndex,
$doubleWildcardPieceCount
);
A negative length truncates the slice from the end, so ** consumes a wrong/empty piece set, $pathPiecesIndex is advanced by a negative amount, and subsequent variable segments re-read already-consumed literal prefix pieces. The result is a validated but semantically impossible match.
The trigger requires:
- a
** segment not in final position, followed by
- at least two variable/wildcard segments, and
- an input path exactly 2 pieces shorter than the minimum required length.
Impact
Low. ** mid-template is not used by real generated Google API clients today, so practical reachability is minimal. However this is a correctness bug in a public parsing API (PathTemplate::match/matches) — callers doing authorization or routing decisions based on parsed bindings could be affected, and the emitted "Undefined array key" warnings indicate the code path is genuinely broken, not merely permissive.
Environment
- google/gax v1.43.1 – v1.47.0 (also HEAD of main, release 0.338.0)
- PHP 8.4.23
- All releases are affected (code unchanged).
Suggested fix
Clamp the piece count / add a bounds check before slicing, and bail out (no match) when the path is shorter than the minimum required for the template:
if ($doubleWildcardPieceCount < 1) {
return $this->matchException($path);
}
Summary
RelativeResourceTemplate::match()returns a false-positive match with wrong, internally-contradictory bindings when a template contains a**path wildcard followed by wildcard variable segments, and the input path has fewer segments than required. Such paths should be rejected (the library's own test suite asserts this), but instead a match is returned and PHP emits "Undefined array key -N" warnings.Steps to reproduce
Actual output
The path
a/e/fdoes not have enough segments to satisfy the template (minimum is 5), yet it "matches". Worse, the bindings are impossible: path-pieceais consumed both as the literalaand as variabley, and pieceeis bound to both$0(the**) andx.PHP also logs these warnings during the match:
Expected behavior
match()should throwValidationExceptionandmatches()should returnfalse, exactly as the library's own test suite already requires for the same class of input:Root cause
In
src/ResourceTemplate/RelativeResourceTemplate.php(~line 178):When the path has fewer slash-pieces than the template's flattened segments,
$doubleWildcardPieceCountbecomes negative. This is then passed as the length toarray_slice():A negative length truncates the slice from the end, so
**consumes a wrong/empty piece set,$pathPiecesIndexis advanced by a negative amount, and subsequent variable segments re-read already-consumed literal prefix pieces. The result is a validated but semantically impossible match.The trigger requires:
**segment not in final position, followed byImpact
Low.
**mid-template is not used by real generated Google API clients today, so practical reachability is minimal. However this is a correctness bug in a public parsing API (PathTemplate::match/matches) — callers doing authorization or routing decisions based on parsed bindings could be affected, and the emitted "Undefined array key" warnings indicate the code path is genuinely broken, not merely permissive.Environment
Suggested fix
Clamp the piece count / add a bounds check before slicing, and bail out (no match) when the path is shorter than the minimum required for the template: