Skip to content

Commit

Permalink
Fix whitelist case sensitiveness
Browse files Browse the repository at this point in the history
Closes humbug#215
  • Loading branch information
theofidry committed Jun 9, 2018
1 parent 5ff91b8 commit 4bf3717
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 28 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
/clover.xml
/dist/
/fixtures/set004/scoper.inc.php
/fixtures/*/vendor
/fixtures/*/.box_dump/
/fixtures/*/vendor/
/vendor/
/vendor-bin/*/vendor/
/vendor-bin/*/bin/
Expand Down
2 changes: 1 addition & 1 deletion src/PhpParser/NodeVisitor/ConstStmtReplacer.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public function enterNode(Node $node): Node
)
)->getName();

if (false === $this->whitelist->isClassWhitelisted((string) $resolvedConstantName)) {
if (false === $this->whitelist->isConstantWhitelisted((string) $resolvedConstantName)) {
continue;
}

Expand Down
2 changes: 1 addition & 1 deletion src/PhpParser/NodeVisitor/NameStmtPrefixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ private function prefixName(Name $name): Node
}

if ($parentNode instanceof ConstFetch) {
if ($this->whitelist->isClassWhitelisted($resolvedName->toString())) {
if ($this->whitelist->isConstantWhitelisted($resolvedName->toString())) {
return $resolvedName;
}

Expand Down
24 changes: 23 additions & 1 deletion src/PhpParser/NodeVisitor/StringScalarPrefixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ private function prefixStringScalar(String_ $string): Node
$string->getAttributes()
);

$isConstantNode = $this->isConstantNode($string);

// Skip if is already prefixed
if ($this->prefix === $stringName->getFirst()) {
$newStringName = $stringName;
Expand All @@ -117,7 +119,8 @@ private function prefixStringScalar(String_ $string): Node
} elseif (
1 === count($stringName->parts)
|| $this->reflector->isClassInternal($stringName->toString())
|| $this->whitelist->isClassWhitelisted((string) $stringName)
|| (false === $isConstantNode && $this->whitelist->isClassWhitelisted((string) $stringName))
|| ($isConstantNode && $this->whitelist->isConstantWhitelisted((string) $stringName))
|| $this->whitelist->isNamespaceWhitelisted((string) $stringName)
) {
$newStringName = $stringName;
Expand All @@ -127,4 +130,23 @@ private function prefixStringScalar(String_ $string): Node

return new String_($newStringName->toString(), $string->getAttributes());
}

private function isConstantNode(String_ $node): bool
{
$parent = AppendParentNode::getParent($node);

if (false === ($parent instanceof Arg)) {
return false;
}

/** @var Arg $parent */
$argParent = AppendParentNode::getParent($parent);

if (false === ($argParent instanceof FuncCall)) {
return false;
}

/** @var FuncCall $argParent */
return 'define' === (string) $argParent->name;
}
}
70 changes: 56 additions & 14 deletions src/Whitelist.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,35 @@

namespace Humbug\PhpScoper;

use function array_filter;
use function array_pop;
use Countable;
use function explode;
use function implode;
use InvalidArgumentException;
use function array_map;
use function array_merge;
use function array_unique;
use function count;
use function in_array;
use function sprintf;
use function strtolower;
use function substr;
use function trim;

final class Whitelist implements Countable
{
private $original;
private $classes;
private $constants;
private $namespaces;
private $whitelistGlobalConstants;

public static function create(bool $whitelistGlobalConstants, string ...$elements): self
{
$classes = [];
$constants = [];
$namespaces = [];
$original = [];

foreach ($elements as $element) {
if (isset($element[0]) && '\\' === $element[0]) {
Expand All @@ -50,30 +58,46 @@ public static function create(bool $whitelistGlobalConstants, string ...$element
);
}

$original[] = $element;

if ('\*' === substr($element, -2)) {
$namespaces[] = substr($element, 0, -2);
$namespace = strtolower(substr($element, 0, -2));

$namespaces[] = $namespace;
} elseif ('*' === $element) {
$namespaces[] = '';
} else {
$classes[] = $element;
$classes[] = strtolower($element);
$constants[] = self::lowerConstantName($element);
}
}

return new self(
$whitelistGlobalConstants,
array_unique($original),
array_unique($classes),
array_unique($constants),
array_unique($namespaces)
);
}

/**
* @param string[] $original
* @param string[] $classes
* @param string[] $constants
* @param string[] $namespaces
*/
private function __construct(bool $whitelistGlobalConstants, array $classes, array $namespaces)
{
private function __construct(
bool $whitelistGlobalConstants,
array $original,
array $classes,
array $constants,
array $namespaces
) {
$this->whitelistGlobalConstants = $whitelistGlobalConstants;
$this->original = $original;
$this->classes = $classes;
$this->constants = $constants;
$this->namespaces = $namespaces;
}

Expand All @@ -84,19 +108,31 @@ public function whitelistGlobalConstants(): bool

public function isClassWhitelisted(string $name): bool
{
return in_array($name, $this->classes, true);
return in_array(strtolower($name), $this->classes, true);
}

public function isConstantWhitelisted(string $name): bool
{
return in_array(self::lowerConstantName($name), $this->constants, true);
}

/**
* @return string[]
*/
public function getClassWhitelistArray(): array
{
return $this->classes;
return array_filter(
$this->original,
function (string $name): bool {
return '' !== $name && '\*' !== substr($name, -2);
}
);
}

public function isNamespaceWhitelisted(string $name): bool
{
$name = strtolower($name);

foreach ($this->namespaces as $namespace) {
if ('' === $namespace || 0 === strpos($name, $namespace)) {
return true;
Expand All @@ -116,13 +152,19 @@ public function count(): int

public function toArray(): array
{
$namespaces = array_map(
function (string $namespace): string {
return '' === $namespace ? '*' : $namespace.'\*';
},
$this->namespaces
);
return $this->original;
}

private static function lowerConstantName(string $name): string
{
$parts = explode('\\', $name);

$lastPart = array_pop($parts);

$parts = array_map('strtolower', $parts);

$parts[] = $lastPart;

return array_merge($this->classes, $namespaces);
return implode('\\', $parts);
}
}
60 changes: 50 additions & 10 deletions tests/WhitelistTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ class WhitelistTest extends TestCase
*/
public function test_it_can_be_created_from_a_list_of_strings(
array $whitelist,
array $expectedNamespaces,
array $expectedClasses,
array $expectedNamespaces
array $expectedConstants
) {
$whitelistObject = Whitelist::create(true, ...$whitelist);

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

$whitelistConstantReflection = $whitelistReflection->getProperty('constants');
$whitelistConstantReflection->setAccessible(true);
$actualConstants = $whitelistConstantReflection->getValue($whitelistObject);

$this->assertTrue($whitelistObject->whitelistGlobalConstants());
$this->assertSame($expectedClasses, $actualClasses);
$this->assertSame($expectedNamespaces, $actualNamespaces);
$this->assertSame($expectedClasses, $actualClasses);
$this->assertSame($expectedConstants, $actualConstants);

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

$this->assertFalse($whitelistObject->whitelistGlobalConstants());
$this->assertSame($expectedClasses, $actualClasses);
$this->assertSame($expectedNamespaces, $actualNamespaces);
$this->assertSame($expectedConstants, $actualConstants);
}

/**
Expand Down Expand Up @@ -83,19 +90,22 @@ public function test_it_can_be_converted_back_into_an_array(Whitelist $whitelist

public function provideWhitelists()
{
yield [[], [], []];

yield [['Acme\Foo'], ['Acme\Foo'], []];
yield [[], [], [], []];

yield [['\Acme\Foo'], ['Acme\Foo'], []];
yield [['Acme\Foo'], [], ['acme\foo'], ['acme\Foo']];

yield [['Acme\Foo\*'], [], ['Acme\Foo']];
yield [['Acme\Foo\*'], ['acme\foo'], [], []];

yield [['\*'], [], ['']];
yield [['\*'], [''], [], []];

yield [['*'], [], ['']];
yield [['*'], [''], [], []];

yield [['Acme\Foo', 'Acme\Foo\*', '\*'], ['Acme\Foo'], ['Acme\Foo', '']];
yield [
['Acme\Foo', 'Acme\Foo\*', '\*'],
['acme\foo', ''],
['acme\foo'],
['acme\Foo'],
];
}

public function provideClassWhitelists()
Expand Down Expand Up @@ -151,29 +161,59 @@ public function provideNamespaceWhitelists()
true,
];

yield [
Whitelist::create(true, 'Acme\Foo\*'),
'acme\foo',
true,
];

yield [
Whitelist::create(true, 'Acme\*'),
'Acme\Foo',
true,
];

yield [
Whitelist::create(true, 'Acme\*'),
'acme\foo',
true,
];

yield [
Whitelist::create(true, 'Acme\Foo\*'),
'Acme\Foo\Bar',
true,
];

yield [
Whitelist::create(true, 'Acme\Foo\*'),
'acme\foo\bar',
true,
];

yield [
Whitelist::create(true, '\*'),
'Acme',
true,
];

yield [
Whitelist::create(true, '\*'),
'acme',
true,
];

yield [
Whitelist::create(true, '\*'),
'Acme\Foo',
true,
];

yield [
Whitelist::create(true, '\*'),
'acme\foo',
true,
];
}

public function provideWhitelistToConvert()
Expand Down

0 comments on commit 4bf3717

Please sign in to comment.