Skip to content

Commit 21bd501

Browse files
authored
Fix whitelist case sensitiveness (#222)
Closes #215
1 parent 38b7155 commit 21bd501

File tree

6 files changed

+131
-28
lines changed

6 files changed

+131
-28
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
/clover.xml
88
/dist/
99
/fixtures/set004/scoper.inc.php
10-
/fixtures/*/vendor
10+
/fixtures/*/.box_dump/
11+
/fixtures/*/vendor/
1112
/vendor/
1213
/vendor-bin/*/vendor/
1314
/vendor-bin/*/bin/

src/PhpParser/NodeVisitor/ConstStmtReplacer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public function enterNode(Node $node): Node
7272
)
7373
)->getName();
7474

75-
if (false === $this->whitelist->isClassWhitelisted((string) $resolvedConstantName)) {
75+
if (false === $this->whitelist->isConstantWhitelisted((string) $resolvedConstantName)) {
7676
continue;
7777
}
7878

src/PhpParser/NodeVisitor/NameStmtPrefixer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ private function prefixName(Name $name): Node
160160
}
161161

162162
if ($parentNode instanceof ConstFetch) {
163-
if ($this->whitelist->isClassWhitelisted($resolvedName->toString())) {
163+
if ($this->whitelist->isConstantWhitelisted($resolvedName->toString())) {
164164
return $resolvedName;
165165
}
166166

src/PhpParser/NodeVisitor/StringScalarPrefixer.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ private function prefixStringScalar(String_ $string): Node
109109
$string->getAttributes()
110110
);
111111

112+
$isConstantNode = $this->isConstantNode($string);
113+
112114
// Skip if is already prefixed
113115
if ($this->prefix === $stringName->getFirst()) {
114116
$newStringName = $stringName;
@@ -117,7 +119,8 @@ private function prefixStringScalar(String_ $string): Node
117119
} elseif (
118120
1 === count($stringName->parts)
119121
|| $this->reflector->isClassInternal($stringName->toString())
120-
|| $this->whitelist->isClassWhitelisted((string) $stringName)
122+
|| (false === $isConstantNode && $this->whitelist->isClassWhitelisted((string) $stringName))
123+
|| ($isConstantNode && $this->whitelist->isConstantWhitelisted((string) $stringName))
121124
|| $this->whitelist->isNamespaceWhitelisted((string) $stringName)
122125
) {
123126
$newStringName = $stringName;
@@ -127,4 +130,23 @@ private function prefixStringScalar(String_ $string): Node
127130

128131
return new String_($newStringName->toString(), $string->getAttributes());
129132
}
133+
134+
private function isConstantNode(String_ $node): bool
135+
{
136+
$parent = AppendParentNode::getParent($node);
137+
138+
if (false === ($parent instanceof Arg)) {
139+
return false;
140+
}
141+
142+
/** @var Arg $parent */
143+
$argParent = AppendParentNode::getParent($parent);
144+
145+
if (false === ($argParent instanceof FuncCall)) {
146+
return false;
147+
}
148+
149+
/* @var FuncCall $argParent */
150+
return 'define' === (string) $argParent->name;
151+
}
130152
}

src/Whitelist.php

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,33 @@
1616

1717
use Countable;
1818
use InvalidArgumentException;
19+
use function array_filter;
1920
use function array_map;
20-
use function array_merge;
21+
use function array_pop;
2122
use function array_unique;
2223
use function count;
24+
use function explode;
25+
use function implode;
2326
use function in_array;
2427
use function sprintf;
28+
use function strtolower;
2529
use function substr;
2630
use function trim;
2731

2832
final class Whitelist implements Countable
2933
{
34+
private $original;
3035
private $classes;
36+
private $constants;
3137
private $namespaces;
3238
private $whitelistGlobalConstants;
3339

3440
public static function create(bool $whitelistGlobalConstants, string ...$elements): self
3541
{
3642
$classes = [];
43+
$constants = [];
3744
$namespaces = [];
45+
$original = [];
3846

3947
foreach ($elements as $element) {
4048
if (isset($element[0]) && '\\' === $element[0]) {
@@ -50,30 +58,44 @@ public static function create(bool $whitelistGlobalConstants, string ...$element
5058
);
5159
}
5260

61+
$original[] = $element;
62+
5363
if ('\*' === substr($element, -2)) {
54-
$namespaces[] = substr($element, 0, -2);
64+
$namespaces[] = strtolower(substr($element, 0, -2));
5565
} elseif ('*' === $element) {
5666
$namespaces[] = '';
5767
} else {
58-
$classes[] = $element;
68+
$classes[] = strtolower($element);
69+
$constants[] = self::lowerConstantName($element);
5970
}
6071
}
6172

6273
return new self(
6374
$whitelistGlobalConstants,
75+
array_unique($original),
6476
array_unique($classes),
77+
array_unique($constants),
6578
array_unique($namespaces)
6679
);
6780
}
6881

6982
/**
83+
* @param string[] $original
7084
* @param string[] $classes
85+
* @param string[] $constants
7186
* @param string[] $namespaces
7287
*/
73-
private function __construct(bool $whitelistGlobalConstants, array $classes, array $namespaces)
74-
{
88+
private function __construct(
89+
bool $whitelistGlobalConstants,
90+
array $original,
91+
array $classes,
92+
array $constants,
93+
array $namespaces
94+
) {
7595
$this->whitelistGlobalConstants = $whitelistGlobalConstants;
96+
$this->original = $original;
7697
$this->classes = $classes;
98+
$this->constants = $constants;
7799
$this->namespaces = $namespaces;
78100
}
79101

@@ -84,19 +106,31 @@ public function whitelistGlobalConstants(): bool
84106

85107
public function isClassWhitelisted(string $name): bool
86108
{
87-
return in_array($name, $this->classes, true);
109+
return in_array(strtolower($name), $this->classes, true);
110+
}
111+
112+
public function isConstantWhitelisted(string $name): bool
113+
{
114+
return in_array(self::lowerConstantName($name), $this->constants, true);
88115
}
89116

90117
/**
91118
* @return string[]
92119
*/
93120
public function getClassWhitelistArray(): array
94121
{
95-
return $this->classes;
122+
return array_filter(
123+
$this->original,
124+
function (string $name): bool {
125+
return '*' !== $name && '\*' !== substr($name, -2);
126+
}
127+
);
96128
}
97129

98130
public function isNamespaceWhitelisted(string $name): bool
99131
{
132+
$name = strtolower($name);
133+
100134
foreach ($this->namespaces as $namespace) {
101135
if ('' === $namespace || 0 === strpos($name, $namespace)) {
102136
return true;
@@ -116,13 +150,19 @@ public function count(): int
116150

117151
public function toArray(): array
118152
{
119-
$namespaces = array_map(
120-
function (string $namespace): string {
121-
return '' === $namespace ? '*' : $namespace.'\*';
122-
},
123-
$this->namespaces
124-
);
153+
return $this->original;
154+
}
155+
156+
private static function lowerConstantName(string $name): string
157+
{
158+
$parts = explode('\\', $name);
159+
160+
$lastPart = array_pop($parts);
161+
162+
$parts = array_map('strtolower', $parts);
163+
164+
$parts[] = $lastPart;
125165

126-
return array_merge($this->classes, $namespaces);
166+
return implode('\\', $parts);
127167
}
128168
}

tests/WhitelistTest.php

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ class WhitelistTest extends TestCase
2727
*/
2828
public function test_it_can_be_created_from_a_list_of_strings(
2929
array $whitelist,
30+
array $expectedNamespaces,
3031
array $expectedClasses,
31-
array $expectedNamespaces
32+
array $expectedConstants
3233
) {
3334
$whitelistObject = Whitelist::create(true, ...$whitelist);
3435

@@ -40,15 +41,21 @@ public function test_it_can_be_created_from_a_list_of_strings(
4041
$whitelistNamespaceReflection->setAccessible(true);
4142
$actualNamespaces = $whitelistNamespaceReflection->getValue($whitelistObject);
4243

44+
$whitelistConstantReflection = $whitelistReflection->getProperty('constants');
45+
$whitelistConstantReflection->setAccessible(true);
46+
$actualConstants = $whitelistConstantReflection->getValue($whitelistObject);
47+
4348
$this->assertTrue($whitelistObject->whitelistGlobalConstants());
44-
$this->assertSame($expectedClasses, $actualClasses);
4549
$this->assertSame($expectedNamespaces, $actualNamespaces);
50+
$this->assertSame($expectedClasses, $actualClasses);
51+
$this->assertSame($expectedConstants, $actualConstants);
4652

4753
$whitelistObject = Whitelist::create(false, ...$whitelist);
4854

4955
$this->assertFalse($whitelistObject->whitelistGlobalConstants());
5056
$this->assertSame($expectedClasses, $actualClasses);
5157
$this->assertSame($expectedNamespaces, $actualNamespaces);
58+
$this->assertSame($expectedConstants, $actualConstants);
5259
}
5360

5461
/**
@@ -83,19 +90,22 @@ public function test_it_can_be_converted_back_into_an_array(Whitelist $whitelist
8390

8491
public function provideWhitelists()
8592
{
86-
yield [[], [], []];
87-
88-
yield [['Acme\Foo'], ['Acme\Foo'], []];
93+
yield [[], [], [], []];
8994

90-
yield [['\Acme\Foo'], ['Acme\Foo'], []];
95+
yield [['Acme\Foo'], [], ['Acme\Foo'], ['acme\Foo']];
9196

92-
yield [['Acme\Foo\*'], [], ['Acme\Foo']];
97+
yield [['Acme\Foo\*'], ['acme\foo'], [], []];
9398

94-
yield [['\*'], [], ['']];
99+
yield [['\*'], [''], [], []];
95100

96-
yield [['*'], [], ['']];
101+
yield [['*'], [''], [], []];
97102

98-
yield [['Acme\Foo', 'Acme\Foo\*', '\*'], ['Acme\Foo'], ['Acme\Foo', '']];
103+
yield [
104+
['Acme\Foo', 'Acme\Foo\*', '\*'],
105+
['acme\foo', ''],
106+
['Acme\Foo'],
107+
['acme\Foo'],
108+
];
99109
}
100110

101111
public function provideClassWhitelists()
@@ -151,29 +161,59 @@ public function provideNamespaceWhitelists()
151161
true,
152162
];
153163

164+
yield [
165+
Whitelist::create(true, 'Acme\Foo\*'),
166+
'acme\foo',
167+
true,
168+
];
169+
154170
yield [
155171
Whitelist::create(true, 'Acme\*'),
156172
'Acme\Foo',
157173
true,
158174
];
159175

176+
yield [
177+
Whitelist::create(true, 'Acme\*'),
178+
'acme\foo',
179+
true,
180+
];
181+
160182
yield [
161183
Whitelist::create(true, 'Acme\Foo\*'),
162184
'Acme\Foo\Bar',
163185
true,
164186
];
165187

188+
yield [
189+
Whitelist::create(true, 'Acme\Foo\*'),
190+
'acme\foo\bar',
191+
true,
192+
];
193+
166194
yield [
167195
Whitelist::create(true, '\*'),
168196
'Acme',
169197
true,
170198
];
171199

200+
yield [
201+
Whitelist::create(true, '\*'),
202+
'acme',
203+
true,
204+
];
205+
172206
yield [
173207
Whitelist::create(true, '\*'),
174208
'Acme\Foo',
175209
true,
176210
];
211+
212+
yield [
213+
Whitelist::create(true, '\*'),
214+
'acme\foo',
215+
true,
216+
];
177217
}
178218

179219
public function provideWhitelistToConvert()

0 commit comments

Comments
 (0)