Skip to content

Commit bbed2b2

Browse files
authored
Merge pull request #50 from infinum/fix-edgecase-security-sniff
Fix edgecase security sniff
2 parents cf7a49a + 9c78572 commit bbed2b2

File tree

5 files changed

+249
-81
lines changed

5 files changed

+249
-81
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,19 @@ The semantic versioning started from version 0.2.1.
1010

1111
_No documentation available about unreleased changes yet._
1212

13+
## [1.5.0](https://github.com/infinum/eightshift-coding-standards/compare/1.4.2...1.5.0) - 2022-03-16
14+
15+
### Added
16+
- Add `overriddenClass` parameter for the EightShift ComponentsEscape sniff
17+
- This parameter will catch the cases where the libs Components class has been overridden.
18+
- Add ignoreComments property for the line length sniff
19+
20+
### Fixed
21+
- Fixed the edge case with overwriting libs classes.
22+
23+
### Changed
24+
- Code cleanup in EightShift ComponentsEscape sniff
25+
1326
## [1.4.2](https://github.com/infinum/eightshift-coding-standards/compare/1.4.1...1.4.2) - 2022-03-10
1427

1528
### Fixed

Eightshift/Sniffs/Security/ComponentsEscapeSniff.php

Lines changed: 70 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,18 @@
3030
*/
3131
class ComponentsEscapeSniff extends EscapeOutputSniff
3232
{
33+
/**
34+
* A fully qualified class name for Components class override
35+
*
36+
* You should put the fully qualified class name of the class you used
37+
* to override the EightshiftLibs\Helpers\Component class.
38+
*
39+
* For Example: namespace\\SomeSubNamespace\\MyComponents.
40+
*
41+
* @var string
42+
*/
43+
public $overriddenClass = '';
44+
3345
/**
3446
* Processes this test, when one of its tokens is encountered.
3547
*
@@ -42,21 +54,27 @@ public function process_token($stackPtr)
4254
{
4355
$tokens = $this->tokens;
4456
$phpcsFile = $this->phpcsFile;
45-
$importData = [];
57+
$importData = [
58+
'importExists' => false,
59+
'fullImportExists' => false
60+
];
4661
$importExists = false;
4762

4863
// Check if the current token is a part of the import, if it is, skip the check.
4964
$useToken = $phpcsFile->findPrevious(\T_USE, ($stackPtr - 1), null, false, null, false);
5065

5166
if ($useToken) {
52-
$endOfUse = $phpcsFile->findNext(\T_SEMICOLON, $useToken, null, false, null, false);
67+
// Find all use tokens.
68+
foreach ($tokens as $tokenPtr => $token) {
69+
if ($token['code'] === \T_USE) {
70+
$importData = $this->checkIfImportInformation($tokenPtr, $importData);
5371

54-
// Ignore all the tokens that are a part of the import statement. Every import statement ends in the semicolon.
55-
if ($stackPtr <= $endOfUse) {
56-
return;
72+
if ($importData['importExists']) {
73+
break;
74+
}
75+
}
5776
}
5877

59-
$importData = $this->checkIfImportInformation($stackPtr);
6078
$importExists = $importData['importExists'];
6179
}
6280

@@ -110,8 +128,9 @@ public function process_token($stackPtr)
110128
if ($importData['fullImportExists']) {
111129
// Components name is ok, \Components is not ok, \Anything\Components is not ok FQCN is ok.
112130
if (
113-
$className === 'Components' ||
114-
\strpos($className, 'EightshiftLibs\\Helpers\\Components') !== false
131+
$className === 'Components'
132+
|| \strpos($className, 'EightshiftLibs\\Helpers\\Components') !== false
133+
|| (! empty($this->overriddenClass) && \strpos($className, $this->overriddenClass) !== false)
115134
) {
116135
// Check the static method name.
117136
$methodNamePtr = $phpcsFile->findNext(
@@ -130,27 +149,13 @@ public function process_token($stackPtr)
130149
return; // Skip sniffing allowed methods.
131150
} else {
132151
// Not allowed method, continue as usual.
133-
$echoPtr = $phpcsFile->findPrevious(
134-
\T_ECHO,
135-
($componentsClassNamePtr - 1),
136-
null,
137-
false,
138-
null,
139-
true
140-
);
152+
$echoPtr = $this->getEchoToken($componentsClassNamePtr);
141153

142154
return parent::process_token($echoPtr);
143155
}
144156
} else {
145157
// Some other class we don't care about.
146-
$echoPtr = $phpcsFile->findPrevious(
147-
\T_ECHO,
148-
($componentsClassNamePtr - 1),
149-
null,
150-
false,
151-
null,
152-
true
153-
);
158+
$echoPtr = $this->getEchoToken($componentsClassNamePtr);
154159

155160
return parent::process_token($echoPtr);
156161
}
@@ -180,34 +185,23 @@ public function process_token($stackPtr)
180185
return; // Skip sniffing allowed methods.
181186
} else {
182187
// Not allowed method, continue as usual.
183-
$echoPtr = $phpcsFile->findPrevious(
184-
\T_ECHO,
185-
($componentsClassNamePtr - 1),
186-
null,
187-
false,
188-
null,
189-
true
190-
);
188+
$echoPtr = $this->getEchoToken($componentsClassNamePtr);
191189

192190
return parent::process_token($echoPtr);
193191
}
194192
} else {
195193
// Wrongly imported, or class that is not related to the libs.
196-
$echoPtr = $phpcsFile->findPrevious(
197-
\T_ECHO,
198-
($componentsClassNamePtr - 1),
199-
null,
200-
false,
201-
null,
202-
true
203-
);
194+
$echoPtr = $this->getEchoToken($componentsClassNamePtr);
204195

205196
return parent::process_token($echoPtr);
206197
}
207198
}
208199
} else {
209200
// Check if the class name is fully qualified and contains the helper part.
210-
if (\strpos($className, 'EightshiftLibs\\Helpers\\Components') !== false) {
201+
if (
202+
\strpos($className, 'EightshiftLibs\\Helpers\\Components') !== false
203+
|| (! empty($this->overriddenClass) && \strpos($className, $this->overriddenClass) !== false)
204+
) {
211205
$methodNamePtr = $phpcsFile->findNext(
212206
\T_STRING,
213207
($componentsClassNamePtr + 1),
@@ -224,26 +218,12 @@ public function process_token($stackPtr)
224218
return; // Skip sniffing allowed methods.
225219
} else {
226220
// Not allowed method, continue as usual.
227-
$echoPtr = $phpcsFile->findPrevious(
228-
\T_ECHO,
229-
($componentsClassNamePtr - 1),
230-
null,
231-
false,
232-
null,
233-
true
234-
);
221+
$echoPtr = $this->getEchoToken($componentsClassNamePtr);
235222

236223
return parent::process_token($echoPtr);
237224
}
238225
} else {
239-
$echoPtr = $phpcsFile->findPrevious(
240-
\T_ECHO,
241-
($componentsClassNamePtr - 1),
242-
null,
243-
false,
244-
null,
245-
true
246-
);
226+
$echoPtr = $this->getEchoToken($componentsClassNamePtr);
247227

248228
return parent::process_token($echoPtr);
249229
}
@@ -258,48 +238,57 @@ public function process_token($stackPtr)
258238
* Checks if the import statement exists in the current file, for the given stack pointer
259239
*
260240
* @param int $stackPtr The position of the current token in the stack.
241+
* @param String[] $importData Import data array.
261242
*
262243
* @return array<string, bool|string> Information about the imports
263244
*/
264-
private function checkIfImportInformation($stackPtr): array
245+
private function checkIfImportInformation(int $stackPtr, array $importData): array
265246
{
266-
$tokens = $this->tokens;
267247
$phpcsFile = $this->phpcsFile;
268248

269-
$importData = [
270-
'importExists' => false,
271-
'fullImportExists' => false
272-
];
249+
$overriddenClass = \str_replace('\\\\', '\\', $this->overriddenClass);
273250

274-
// Check if the correct import exists at the top of the file.
275-
$importPtr = $phpcsFile->findPrevious(\T_USE, ($stackPtr - 1), null, false, null, false);
251+
if (UseStatements::isImportUse($phpcsFile, $stackPtr)) {
252+
$importInfo = UseStatements::splitImportUseStatement($phpcsFile, $stackPtr);
276253

277-
if ($tokens[$importPtr]['code'] === \T_USE) {
278-
if (UseStatements::isImportUse($phpcsFile, $importPtr)) {
279-
$importInfo = UseStatements::splitImportUseStatement($phpcsFile, $importPtr);
254+
if (!empty($importInfo)) {
255+
foreach ($importInfo['name'] as $fullyQualifiedClassNameImport) {
256+
if (
257+
\strpos($fullyQualifiedClassNameImport, 'EightshiftLibs\\Helpers') !== false
258+
|| (! empty($overriddenClass) && \strpos($fullyQualifiedClassNameImport, $overriddenClass) !== false)
259+
) {
260+
$importData['importExists'] = true;
261+
$importData['importName'] = $fullyQualifiedClassNameImport;
280262

281-
if (!empty($importInfo)) {
282-
foreach ($importInfo['name'] as $fullyQualifiedClassNameImport) {
283-
// Check for partial import.
284-
if (\strpos($fullyQualifiedClassNameImport, 'EightshiftLibs\\Helpers') !== false) {
285-
$importData['importExists'] = true;
263+
// Check for fully qualified import.
264+
if (
265+
\strpos($fullyQualifiedClassNameImport, 'EightshiftLibs\\Helpers\\Components') !== false
266+
|| (! empty($overriddenClass) && \strpos($fullyQualifiedClassNameImport, $overriddenClass) !== false)
267+
) {
268+
$importData['fullImportExists'] = true;
286269
$importData['importName'] = $fullyQualifiedClassNameImport;
287270

288-
// Check for fully qualified import.
289-
if (\strpos($fullyQualifiedClassNameImport, 'EightshiftLibs\\Helpers\\Components') !== false) {
290-
$importData['fullImportExists'] = true;
291-
$importData['importName'] = $fullyQualifiedClassNameImport;
292-
293-
break;
294-
}
295-
296271
break;
297272
}
273+
274+
break;
298275
}
299276
}
300277
}
301278
}
302279

303280
return $importData;
304281
}
282+
283+
/**
284+
* Return the position of the previous echo pointer
285+
*
286+
* @param int $stackPtr The position of the current token in the stack.
287+
*
288+
* @return int Token pointer number.
289+
*/
290+
private function getEchoToken(int $stackPtr): int
291+
{
292+
return $this->phpcsFile->findPrevious(\T_ECHO, ($stackPtr - 1), null, false, null, true);
293+
}
305294
}

0 commit comments

Comments
 (0)