Skip to content

Commit 97a04e6

Browse files
committed
Add more defensive coding / improve type checking
Add more defensive coding against incorrect stack pointers being passed. It is common to pass the result of a call to `File::findPrevious()` or `File::findNext()` to functions expecting a stack pointer, but these `File` functions can return `false`, which would be juggled to `0` when used in the typical `isset($tokens[$stackPtr])` checks. This would then lead to that check passing, while the value should have been rejected, as the method may now try to act on a completely different token than intended (and more defensive coding should have been added to the originating sniff). Adding a preliminary check to make sure the received parameter is an integer prevents this problem and should surface any such bugs in sniffs using the updated PHPCSUtils methods. Includes tests.
1 parent 10aa322 commit 97a04e6

30 files changed

+345
-13
lines changed

PHPCSUtils/Fixers/SpacesFixer.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHP_CodeSniffer\Util\Tokens;
1515
use PHPCSUtils\Exceptions\LogicException;
1616
use PHPCSUtils\Exceptions\OutOfBoundsStackPtr;
17+
use PHPCSUtils\Exceptions\TypeError;
1718
use PHPCSUtils\Exceptions\UnexpectedTokenType;
1819
use PHPCSUtils\Exceptions\ValueError;
1920
use PHPCSUtils\Utils\Numbers;
@@ -83,6 +84,7 @@ final class SpacesFixer
8384
*
8485
* @return void
8586
*
87+
* @throws \PHPCSUtils\Exceptions\TypeError If the $stackPtr or $secondPtr parameters are not integers.
8688
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the tokens passed do not exist in the $phpcsFile.
8789
* @throws \PHPCSUtils\Exceptions\UnexpectedTokenType If the tokens passed are whitespace tokens.
8890
* @throws \PHPCSUtils\Exceptions\ValueError If `$expectedSpaces` parameter is not a valid value.
@@ -106,6 +108,14 @@ public static function checkAndFix(
106108
* Validate the received function input.
107109
*/
108110

111+
if (\is_int($stackPtr) === false) {
112+
throw TypeError::create(2, '$stackPtr', 'integer', $stackPtr);
113+
}
114+
115+
if (\is_int($secondPtr) === false) {
116+
throw TypeError::create(3, '$secondPtr', 'integer', $secondPtr);
117+
}
118+
109119
if (isset($tokens[$stackPtr]) === false) {
110120
throw OutOfBoundsStackPtr::create(2, '$stackPtr', $stackPtr);
111121
}

PHPCSUtils/Internal/IsShortArrayOrList.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHP_CodeSniffer\Util\Tokens;
1515
use PHPCSUtils\BackCompat\Helper;
1616
use PHPCSUtils\Exceptions\OutOfBoundsStackPtr;
17+
use PHPCSUtils\Exceptions\TypeError;
1718
use PHPCSUtils\Exceptions\UnexpectedTokenType;
1819
use PHPCSUtils\Internal\Cache;
1920
use PHPCSUtils\Internal\IsShortArrayOrListWithCache;
@@ -183,6 +184,7 @@ final class IsShortArrayOrList
183184
*
184185
* @return void
185186
*
187+
* @throws \PHPCSUtils\Exceptions\TypeError If the $stackPtr parameter is not an integer.
186188
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the token passed does not exist in the $phpcsFile.
187189
* @throws \PHPCSUtils\Exceptions\UnexpectedTokenType If the token passed is not one of the accepted types.
188190
*/
@@ -191,6 +193,10 @@ public function __construct(File $phpcsFile, $stackPtr)
191193
$tokens = $phpcsFile->getTokens();
192194
$openBrackets = StableCollections::$shortArrayListOpenTokensBC;
193195

196+
if (\is_int($stackPtr) === false) {
197+
throw TypeError::create(2, '$stackPtr', 'integer', $stackPtr);
198+
}
199+
194200
if (isset($tokens[$stackPtr]) === false) {
195201
throw OutOfBoundsStackPtr::create(2, '$stackPtr', $stackPtr);
196202
}

PHPCSUtils/Utils/Arrays.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PHP_CodeSniffer\Files\File;
1414
use PHPCSUtils\Exceptions\LogicException;
1515
use PHPCSUtils\Exceptions\OutOfBoundsStackPtr;
16+
use PHPCSUtils\Exceptions\TypeError;
1617
use PHPCSUtils\Internal\Cache;
1718
use PHPCSUtils\Internal\IsShortArrayOrListWithCache;
1819
use PHPCSUtils\Tokens\Collections;
@@ -152,13 +153,22 @@ public static function getOpenClose(File $phpcsFile, $stackPtr, $isShortArray =
152153
*
153154
* @return int|false Stack pointer to the double arrow if this array item has a key; or `FALSE` otherwise.
154155
*
156+
* @throws \PHPCSUtils\Exceptions\TypeError If the $start or $end parameters are not integers.
155157
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the tokens passed do not exist in the $phpcsFile.
156158
* @throws \PHPCSUtils\Exceptions\LogicException If $end pointer is before the $start pointer.
157159
*/
158160
public static function getDoubleArrowPtr(File $phpcsFile, $start, $end)
159161
{
160162
$tokens = $phpcsFile->getTokens();
161163

164+
if (\is_int($start) === false) {
165+
throw TypeError::create(2, '$start', 'integer', $start);
166+
}
167+
168+
if (\is_int($end) === false) {
169+
throw TypeError::create(3, '$end', 'integer', $end);
170+
}
171+
162172
if (isset($tokens[$start]) === false) {
163173
throw OutOfBoundsStackPtr::create(2, '$start', $start);
164174
}

PHPCSUtils/Utils/ControlStructures.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PHP_CodeSniffer\Files\File;
1414
use PHP_CodeSniffer\Util\Tokens;
1515
use PHPCSUtils\Exceptions\OutOfBoundsStackPtr;
16+
use PHPCSUtils\Exceptions\TypeError;
1617
use PHPCSUtils\Exceptions\UnexpectedTokenType;
1718
use PHPCSUtils\Tokens\Collections;
1819

@@ -213,13 +214,18 @@ public static function isElseIf(File $phpcsFile, $stackPtr)
213214
* ```
214215
* In case of an invalid catch structure, the array may be empty.
215216
*
217+
* @throws \PHPCSUtils\Exceptions\TypeError If the $stackPtr parameter is not an integer.
216218
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the token passed does not exist in the $phpcsFile.
217219
* @throws \PHPCSUtils\Exceptions\UnexpectedTokenType If the token passed is not a `T_CATCH` token.
218220
*/
219221
public static function getCaughtExceptions(File $phpcsFile, $stackPtr)
220222
{
221223
$tokens = $phpcsFile->getTokens();
222224

225+
if (\is_int($stackPtr) === false) {
226+
throw TypeError::create(2, '$stackPtr', 'integer', $stackPtr);
227+
}
228+
223229
if (isset($tokens[$stackPtr]) === false) {
224230
throw OutOfBoundsStackPtr::create(2, '$stackPtr', $stackPtr);
225231
}

PHPCSUtils/Utils/FunctionDeclarations.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PHP_CodeSniffer\Files\File;
1414
use PHP_CodeSniffer\Util\Tokens;
1515
use PHPCSUtils\Exceptions\OutOfBoundsStackPtr;
16+
use PHPCSUtils\Exceptions\TypeError;
1617
use PHPCSUtils\Exceptions\UnexpectedTokenType;
1718
use PHPCSUtils\Exceptions\ValueError;
1819
use PHPCSUtils\Internal\Cache;
@@ -131,6 +132,7 @@ final class FunctionDeclarations
131132
* @return string|null The name of the function; or `NULL` if the passed token doesn't exist,
132133
* the function is anonymous or in case of a parse error/live coding.
133134
*
135+
* @throws \PHPCSUtils\Exceptions\TypeError If the $stackPtr parameter is not an integer.
134136
* @throws \PHPCSUtils\Exceptions\UnexpectedTokenType If the token passed is not a `T_FUNCTION` token.
135137
*/
136138
public static function getName(File $phpcsFile, $stackPtr)
@@ -180,6 +182,7 @@ public static function getName(File $phpcsFile, $stackPtr)
180182
* );
181183
* ```
182184
*
185+
* @throws \PHPCSUtils\Exceptions\TypeError If the $stackPtr parameter is not an integer.
183186
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the token passed does not exist in the $phpcsFile.
184187
* @throws \PHPCSUtils\Exceptions\UnexpectedTokenType If the token passed is not a T_FUNCTION, T_CLOSURE
185188
* or T_FN token.
@@ -188,6 +191,10 @@ public static function getProperties(File $phpcsFile, $stackPtr)
188191
{
189192
$tokens = $phpcsFile->getTokens();
190193

194+
if (\is_int($stackPtr) === false) {
195+
throw TypeError::create(2, '$stackPtr', 'integer', $stackPtr);
196+
}
197+
191198
if (isset($tokens[$stackPtr]) === false) {
192199
throw OutOfBoundsStackPtr::create(2, '$stackPtr', $stackPtr);
193200
}
@@ -396,6 +403,7 @@ public static function getProperties(File $phpcsFile, $stackPtr)
396403
*
397404
* @return array<int, array<string, mixed>>
398405
*
406+
* @throws \PHPCSUtils\Exceptions\TypeError If the $stackPtr parameter is not an integer.
399407
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the token passed does not exist in the $phpcsFile.
400408
* @throws \PHPCSUtils\Exceptions\UnexpectedTokenType If the token passed is not a T_FUNCTION, T_CLOSURE,
401409
* T_FN or T_USE token.
@@ -405,6 +413,10 @@ public static function getParameters(File $phpcsFile, $stackPtr)
405413
{
406414
$tokens = $phpcsFile->getTokens();
407415

416+
if (\is_int($stackPtr) === false) {
417+
throw TypeError::create(2, '$stackPtr', 'integer', $stackPtr);
418+
}
419+
408420
if (isset($tokens[$stackPtr]) === false) {
409421
throw OutOfBoundsStackPtr::create(2, '$stackPtr', $stackPtr);
410422
}

PHPCSUtils/Utils/GetTokensAsString.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PHP_CodeSniffer\Files\File;
1414
use PHP_CodeSniffer\Util\Tokens;
1515
use PHPCSUtils\Exceptions\OutOfBoundsStackPtr;
16+
use PHPCSUtils\Exceptions\TypeError;
1617

1718
/**
1819
* Utility functions to retrieve the content of a set of tokens as a string.
@@ -43,6 +44,7 @@ final class GetTokensAsString
4344
*
4445
* @return string The token contents.
4546
*
47+
* @throws \PHPCSUtils\Exceptions\TypeError If the $start parameter is not an integer.
4648
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the $start token does not exist in the $phpcsFile.
4749
*/
4850
public static function normal(File $phpcsFile, $start, $end)
@@ -72,6 +74,7 @@ public static function normal(File $phpcsFile, $start, $end)
7274
*
7375
* @return string The token contents.
7476
*
77+
* @throws \PHPCSUtils\Exceptions\TypeError If the $start parameter is not an integer.
7578
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the $start token does not exist in the $phpcsFile.
7679
*/
7780
public static function tabReplaced(File $phpcsFile, $start, $end)
@@ -103,6 +106,7 @@ public static function tabReplaced(File $phpcsFile, $start, $end)
103106
*
104107
* @return string The token contents.
105108
*
109+
* @throws \PHPCSUtils\Exceptions\TypeError If the $start parameter is not an integer.
106110
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the $start token does not exist in the $phpcsFile.
107111
*/
108112
public static function origContent(File $phpcsFile, $start, $end)
@@ -124,6 +128,7 @@ public static function origContent(File $phpcsFile, $start, $end)
124128
*
125129
* @return string The token contents stripped off comments.
126130
*
131+
* @throws \PHPCSUtils\Exceptions\TypeError If the $start parameter is not an integer.
127132
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the $start token does not exist in the $phpcsFile.
128133
*/
129134
public static function noComments(File $phpcsFile, $start, $end)
@@ -148,6 +153,7 @@ public static function noComments(File $phpcsFile, $start, $end)
148153
*
149154
* @return string The token contents stripped off comments and whitespace.
150155
*
156+
* @throws \PHPCSUtils\Exceptions\TypeError If the $start parameter is not an integer.
151157
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the $start token does not exist in the $phpcsFile.
152158
*/
153159
public static function noEmpties(File $phpcsFile, $start, $end)
@@ -172,6 +178,7 @@ public static function noEmpties(File $phpcsFile, $start, $end)
172178
*
173179
* @return string The token contents with compacted whitespace and optionally stripped off comments.
174180
*
181+
* @throws \PHPCSUtils\Exceptions\TypeError If the $start parameter is not an integer.
175182
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the $start token does not exist in the $phpcsFile.
176183
*/
177184
public static function compact(File $phpcsFile, $start, $end, $stripComments = false)
@@ -200,6 +207,7 @@ public static function compact(File $phpcsFile, $start, $end, $stripComments = f
200207
*
201208
* @return string The token contents.
202209
*
210+
* @throws \PHPCSUtils\Exceptions\TypeError If the $start parameter is not an integer.
203211
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the $start token does not exist in the $phpcsFile.
204212
*/
205213
protected static function getString(
@@ -213,7 +221,11 @@ protected static function getString(
213221
) {
214222
$tokens = $phpcsFile->getTokens();
215223

216-
if (\is_int($start) === false || isset($tokens[$start]) === false) {
224+
if (\is_int($start) === false) {
225+
throw TypeError::create(2, '$start', 'integer', $start);
226+
}
227+
228+
if (isset($tokens[$start]) === false) {
217229
throw OutOfBoundsStackPtr::create(2, '$start', $start);
218230
}
219231

PHPCSUtils/Utils/Namespaces.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use PHPCSUtils\BackCompat\BCFile;
1616
use PHPCSUtils\Exceptions\OutOfBoundsStackPtr;
1717
use PHPCSUtils\Exceptions\RuntimeException;
18+
use PHPCSUtils\Exceptions\TypeError;
1819
use PHPCSUtils\Exceptions\UnexpectedTokenType;
1920
use PHPCSUtils\Internal\Cache;
2021
use PHPCSUtils\Tokens\Collections;
@@ -46,6 +47,7 @@ final class Namespaces
4647
* reliably determined what the `T_NAMESPACE` token is used for,
4748
* which, in most cases, will mean the code contains a parse/fatal error.
4849
*
50+
* @throws \PHPCSUtils\Exceptions\TypeError If the $stackPtr parameter is not an integer.
4951
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the token passed does not exist in the $phpcsFile.
5052
* @throws \PHPCSUtils\Exceptions\UnexpectedTokenType If the token passed is not a `T_NAMESPACE` token.
5153
*/
@@ -72,6 +74,10 @@ public static function getType(File $phpcsFile, $stackPtr)
7274

7375
$tokens = $phpcsFile->getTokens();
7476

77+
if (\is_int($stackPtr) === false) {
78+
throw TypeError::create(2, '$stackPtr', 'integer', $stackPtr);
79+
}
80+
7581
if (isset($tokens[$stackPtr]) === false) {
7682
throw OutOfBoundsStackPtr::create(2, '$stackPtr', $stackPtr);
7783
}
@@ -131,6 +137,7 @@ public static function getType(File $phpcsFile, $stackPtr)
131137
* @return bool `TRUE` if the token passed is the keyword for a namespace declaration.
132138
* `FALSE` if not.
133139
*
140+
* @throws \PHPCSUtils\Exceptions\TypeError If the $stackPtr parameter is not an integer.
134141
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the token passed does not exist in the $phpcsFile.
135142
* @throws \PHPCSUtils\Exceptions\UnexpectedTokenType If the token passed is not a `T_NAMESPACE` token.
136143
*/
@@ -152,6 +159,7 @@ public static function isDeclaration(File $phpcsFile, $stackPtr)
152159
*
153160
* @return bool `TRUE` if the namespace token passed is used as an operator. `FALSE` if not.
154161
*
162+
* @throws \PHPCSUtils\Exceptions\TypeError If the $stackPtr parameter is not an integer.
155163
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the token passed does not exist in the $phpcsFile.
156164
* @throws \PHPCSUtils\Exceptions\UnexpectedTokenType If the token passed is not a `T_NAMESPACE` token.
157165
*/

PHPCSUtils/Utils/Numbers.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
use PHP_CodeSniffer\Files\File;
1414
use PHPCSUtils\Exceptions\OutOfBoundsStackPtr;
15+
use PHPCSUtils\Exceptions\TypeError;
1516
use PHPCSUtils\Exceptions\UnexpectedTokenType;
1617

1718
/**
@@ -126,13 +127,18 @@ final class Numbers
126127
* )
127128
* ```
128129
*
130+
* @throws \PHPCSUtils\Exceptions\TypeError If the $stackPtr parameter is not an integer.
129131
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the token passed does not exist in the $phpcsFile.
130132
* @throws \PHPCSUtils\Exceptions\UnexpectedTokenType If the token passed is not a `T_LNUMBER` or `T_DNUMBER` token.
131133
*/
132134
public static function getCompleteNumber(File $phpcsFile, $stackPtr)
133135
{
134136
$tokens = $phpcsFile->getTokens();
135137

138+
if (\is_int($stackPtr) === false) {
139+
throw TypeError::create(2, '$stackPtr', 'integer', $stackPtr);
140+
}
141+
136142
if (isset($tokens[$stackPtr]) === false) {
137143
throw OutOfBoundsStackPtr::create(2, '$stackPtr', $stackPtr);
138144
}

PHPCSUtils/Utils/ObjectDeclarations.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PHP_CodeSniffer\Files\File;
1414
use PHP_CodeSniffer\Util\Tokens;
1515
use PHPCSUtils\Exceptions\OutOfBoundsStackPtr;
16+
use PHPCSUtils\Exceptions\TypeError;
1617
use PHPCSUtils\Exceptions\UnexpectedTokenType;
1718
use PHPCSUtils\Tokens\Collections;
1819
use PHPCSUtils\Utils\GetTokensAsString;
@@ -58,6 +59,7 @@ final class ObjectDeclarations
5859
* or `NULL` if the passed token doesn't exist, the function or
5960
* class is anonymous or in case of a parse error/live coding.
6061
*
62+
* @throws \PHPCSUtils\Exceptions\TypeError If the $stackPtr parameter is not an integer.
6163
* @throws \PHPCSUtils\Exceptions\UnexpectedTokenType If the token passed is not a `T_FUNCTION`, `T_CLASS`,
6264
* `T_ANON_CLASS`, `T_CLOSURE`, `T_TRAIT`, `T_ENUM`
6365
* or `T_INTERFACE` token.
@@ -66,6 +68,10 @@ public static function getName(File $phpcsFile, $stackPtr)
6668
{
6769
$tokens = $phpcsFile->getTokens();
6870

71+
if (\is_int($stackPtr) === false) {
72+
throw TypeError::create(2, '$stackPtr', 'integer', $stackPtr);
73+
}
74+
6975
if (isset($tokens[$stackPtr]) === false
7076
|| ($tokens[$stackPtr]['code'] === \T_ANON_CLASS || $tokens[$stackPtr]['code'] === \T_CLOSURE)
7177
) {
@@ -162,13 +168,18 @@ public static function getName(File $phpcsFile, $stackPtr)
162168
* );
163169
* ```
164170
*
171+
* @throws \PHPCSUtils\Exceptions\TypeError If the $stackPtr parameter is not an integer.
165172
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the token passed does not exist in the $phpcsFile.
166173
* @throws \PHPCSUtils\Exceptions\UnexpectedTokenType If the token passed is not a T_CLASS token.
167174
*/
168175
public static function getClassProperties(File $phpcsFile, $stackPtr)
169176
{
170177
$tokens = $phpcsFile->getTokens();
171178

179+
if (\is_int($stackPtr) === false) {
180+
throw TypeError::create(2, '$stackPtr', 'integer', $stackPtr);
181+
}
182+
172183
if (isset($tokens[$stackPtr]) === false) {
173184
throw OutOfBoundsStackPtr::create(2, '$stackPtr', $stackPtr);
174185
}

PHPCSUtils/Utils/PassedParameters.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHP_CodeSniffer\Util\Tokens;
1515
use PHPCSUtils\Exceptions\MissingArgumentError;
1616
use PHPCSUtils\Exceptions\OutOfBoundsStackPtr;
17+
use PHPCSUtils\Exceptions\TypeError;
1718
use PHPCSUtils\Exceptions\UnexpectedTokenType;
1819
use PHPCSUtils\Internal\Cache;
1920
use PHPCSUtils\Tokens\Collections;
@@ -77,13 +78,18 @@ final class PassedParameters
7778
*
7879
* @return bool
7980
*
81+
* @throws \PHPCSUtils\Exceptions\TypeError If the $stackPtr parameter is not an integer.
8082
* @throws \PHPCSUtils\Exceptions\OutOfBoundsStackPtr If the token passed does not exist in the $phpcsFile.
8183
* @throws \PHPCSUtils\Exceptions\UnexpectedTokenType If the token passed is not one of the accepted types.
8284
*/
8385
public static function hasParameters(File $phpcsFile, $stackPtr, $isShortArray = null)
8486
{
8587
$tokens = $phpcsFile->getTokens();
8688

89+
if (\is_int($stackPtr) === false) {
90+
throw TypeError::create(2, '$stackPtr', 'integer', $stackPtr);
91+
}
92+
8793
if (isset($tokens[$stackPtr]) === false) {
8894
throw OutOfBoundsStackPtr::create(2, '$stackPtr', $stackPtr);
8995
}

0 commit comments

Comments
 (0)