8000 [JsonPath] Handle slice selector overflow · symfony/symfony@b9231c9 · GitHub
[go: up one dir, main page]

Skip to content

Commit b9231c9

Browse files
[JsonPath] Handle slice selector overflow
1 parent e568dab commit b9231c9

File tree

3 files changed

+73
-22
lines changed

3 files changed

+73
-22
lines changed

src/Symfony/Component/JsonPath/JsonCrawler.php

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ private function evaluateBracket(string $expr, mixed $value): array
143143

144144
// single negative index
145145
if (preg_match('/^-\d+$/', $expr)) {
146+
if (JsonPathUtils::hasLeadingZero($expr) || JsonPathUtils::isIntegerOverflow($expr) || '-0' === $expr) {
147+
throw new JsonCrawlerException($expr, 'invalid index selector');
148+
}
149+
146150
if (!array_is_list($value)) {
147151
return [];
148152
}
@@ -154,6 +158,12 @@ private function evaluateBracket(string $expr, mixed $value): array
154158

155159
// start and end index
156160
if (preg_match('/^-?\d+(?:\s*,\s*-?\d+)*$/', $expr)) {
161+
foreach (explode(',', $expr) as $exprPart) {
162+
if (JsonPathUtils::hasLeadingZero($exprPart = trim($exprPart)) || JsonPathUtils::isIntegerOverflow($exprPart) || '-0' === $exprPart) {
163+
throw new JsonCrawlerException($expr, 'invalid index selector');
164+
}
165+
}
166+
157167
if (!array_is_list($value)) {
158168
return [];
159169
}
@@ -177,12 +187,36 @@ private function evaluateBracket(string $expr, mixed $value): array
177187
return [];
178188
}
179189

190+
$startStr = trim($matches[1]);
191+
$endStr = trim($matches[2]);
192+
$stepStr = trim($matches[3] ?? '1');
193+
194+
if (
195+
JsonPathUtils::hasLeadingZero($startStr)
196+
|| JsonPathUtils::hasLeadingZero($endStr)
197+
|| JsonPathUtils::hasLeadingZero($stepStr)
198+
) {
199+
throw new JsonCrawlerException($expr, 'slice selector numbers cannot have leading zeros');
200+
}
201+
202+
if ('-0' === $stepStr) {
203+
throw new JsonCrawlerException($expr, 'slice selector step cannot be negative zero');
204+
}
205+
206+
if (
207+
JsonPathUtils::isIntegerOverflow($startStr)
208+
|| JsonPathUtils::isIntegerOverflow($endStr)
209+
|| JsonPathUtils::isIntegerOverflow($stepStr)
210+
) {
211+
throw new JsonCrawlerException($expr, 'slice selector integer overflow');
212+
}
213+
180214
$length = \count($value);
181-
$start = '' !== $matches[1] ? (int) $matches[1] : null;
182-
$end = '' !== $matches[2] ? (int) $matches[2] : null;
183-
$step = isset($matches[3]) && '' !== $matches[3] ? (int) $matches[3] : 1;
215+
$start = '' !== $startStr ? (int) $startStr : null;
216+
$end = '' !== $endStr ? (int) $endStr : null;
217+
$step = '' !== $stepStr ? (int) $stepStr : 1;
184218

185-
if (0 === $step || $start > $length) {
219+
if (0 === $step) {
186220
return [];
187221
}
188222

@@ -192,6 +226,11 @@ private function evaluateBracket(string $expr, mixed $value): array
192226
if ($start < 0) {
193227
$start = $length + $start;
194228
}
229+
230+
if ($step > 0 && $start >= $length) {
231+
return [];
232+
}
233+
195234
$start = max(0, min($start, $length - 1));
196235
}
197236

src/Symfony/Component/JsonPath/JsonPathUtils.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,4 +227,34 @@ public static function parseCommaSeparatedValues(string $expr): array
227227

228228
return $parts;
229229
}
230+
231+
public static function hasLeadingZero(string $s): bool
232+
{
233+
if ('' === $s || str_starts_with($s, '-') && '' === $s = substr($s, 1)) {
234+
return false;
235+
}
236+
237+
return '0' === $s[0] && 1 < \strlen($s);
238+
}
239+
240+
/**
241+
* Safe integer range is [-(2^53) + 1, (2^53) - 1].
242+
*
243+
* @see https://datatracker.ietf.org/doc/rfc9535/, section 2.1
244+
*/
245+
public static function isIntegerOverflow(string $s): bool
246+
{
247+
if ('' === $s) {
248+
return false;
249+
}
250+
251+
if (\strlen($s) > (str_starts_with($s, '-') ? 17 : 16)) {
252+
// obviously too long to represent anything in the interval
253+
return true;
254+
}
255+
256+
$value = (int) $s;
257+
258+
return $value < (-(1 << 53) + 1) || ((1 << 53) - 1) < $value;
259+
}
230260
}

src/Symfony/Component/JsonPath/Tests/JsonPathComplianceTestSuiteTest.php

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,6 @@ final class JsonPathComplianceTestSuiteTest extends TestCase
142142
'filter, absolute non-singular query, slice, less-or-equal',
143143
'filter, equals, special nothing',
144144
'filter, group terms, left',
145-
'index selector, min exact index - 1',
146-
'index selector, max exact index + 1',
147-
'index selector, overflowing index',
148-
'index selector, leading 0',
149-
'index selector, -0',
150-
'index selector, leading -0',
151145
'name selector, double quotes, escaped line feed',
152146
'name selector, double quotes, invalid escaped single quote',
153147
'name selector, double quotes, question mark escape',
@@ -172,18 +166,6 @@ final class JsonPathComplianceTestSuiteTest extends TestCase
172166
'name selector, single quotes, escaped backspace',
173167
'name selector, single quotes, escaped line feed',
174168
'name selector, single quotes, invalid escaped double quote',
175-
'slice selector, excessively large from value with negative step',
176-
'slice selector, step, min exact - 1',
177-
'slice selector, step, max exact + 1',
178-
'slice selector, overflowing to value',
179-
'slice selector, underflowing from value',
180-
'slice selector, overflowing from value with negative step',
181-
'slice selector, underflowing to value with negative step',
182-
'slice selector, overflowing step',
183-
'slice selector, underflowing step',
184-
'slice selector, step, leading 0',
185-
'slice selector, step, -0',
186-
'slice selector, step, leading -0',
187169
];
188170

189171
/**

0 commit comments

Comments
 (0)
0