-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Security policy wildcard support for methods/properties #3901
base: 2.x
Are you sure you want to change the base?
Changes from all commits
ccacf25
bc44bb2
e84974f
868fb94
97d3992
3489765
c7b2c14
2ee0fad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,79 @@ | ||||||
<?php | ||||||
|
||||||
/* | ||||||
* This file is part of Twig. | ||||||
* | ||||||
* (c) Fabien Potencier | ||||||
* | ||||||
* For the full copyright and license information, please view the LICENSE | ||||||
* file that was distributed with this source code. | ||||||
*/ | ||||||
|
||||||
namespace Twig\Sandbox; | ||||||
|
||||||
/** | ||||||
* Allows for flexible wildcard support in allowedMethods and allowedProperties in SecurityPolicy. | ||||||
* - Class can be specified as wildcard `* => [...]` in order to allow those methods/properties for all classes. | ||||||
* - Method/property can be specified as wildcard eg. `\DateTime => '*'` in order to allow all methods/properties for that class. | ||||||
* - Method/property can also be specified with a trailing wildcard to allow all methods/properties with a certain prefix, eg. `\DateTime => ['get*', ...]` in order to allow all methods/properties that start with `get`. | ||||||
* | ||||||
* @author Yaakov Saxon <[email protected]> | ||||||
*/ | ||||||
final class MemberMatcher | ||||||
{ | ||||||
private $allowedMembers; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
private $cache = []; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
public function __construct(array $allowedMembers) | ||||||
{ | ||||||
$normalizedMembers = []; | ||||||
foreach ($allowedMembers as $class => $members) { | ||||||
if (!\is_array($members)) { | ||||||
$normalizedMembers[$class][] = strtolower($members); | ||||||
} else { | ||||||
foreach ($members as $index => $member) { | ||||||
$normalizedMembers[$class][$index] = strtolower($member); | ||||||
} | ||||||
} | ||||||
} | ||||||
$this->allowedMembers = $normalizedMembers; | ||||||
} | ||||||
|
||||||
public function isAllowed($obj, string $member): bool | ||||||
{ | ||||||
$cacheKey = get_class($obj) . "::" . $member; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
// Check cache first | ||||||
if (isset($this->cache[$cacheKey])) { | ||||||
return true; | ||||||
} | ||||||
|
||||||
$member = strtolower($member); // normalize member name | ||||||
|
||||||
foreach ($this->allowedMembers as $class => $members) { | ||||||
if ('*' === $class || $obj instanceof $class) { | ||||||
foreach ($members as $allowedMember) { | ||||||
if ('*' === $allowedMember) { | ||||||
$this->cache[$cacheKey] = true; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Same below |
||||||
|
||||||
return true; | ||||||
} | ||||||
if ($allowedMember === $member) { | ||||||
$this->cache[$cacheKey] = true; | ||||||
|
||||||
return true; | ||||||
} | ||||||
// if allowedMember ends with a *, check if the member starts with the allowedMember | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we only allow |
||||||
if ('*' === substr($allowedMember, -1) && substr($member, 0, \strlen($allowedMember) - 1) === rtrim($allowedMember, '*')) { | ||||||
$this->cache[$cacheKey] = true; | ||||||
|
||||||
return true; | ||||||
} | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
// If we reach here, the member is not allowed | ||||||
return false; | ||||||
} | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's mark this class as
@internal
.