Update website
This commit is contained in:
parent
011b183e28
commit
41ce1aa076
23 changed files with 284 additions and 94 deletions
11
vendor/twig/twig/CHANGELOG
vendored
11
vendor/twig/twig/CHANGELOG
vendored
|
@ -1,3 +1,14 @@
|
|||
# 3.11.3 (2024-11-07)
|
||||
|
||||
* Fix an infinite recursion in the sandbox code
|
||||
|
||||
# 3.11.2 (2024-11-06)
|
||||
|
||||
* [BC BREAK] Fix a security issue in the sandbox mode allowing an attacker to call attributes on Array-like objects
|
||||
They are now checked via the property policy
|
||||
* Fix a security issue in the sandbox mode allowing an attacker to be able to call `toString()`
|
||||
under some circumstances on an object even if the `__toString()` method is not allowed by the security policy
|
||||
|
||||
# 3.11.1 (2024-09-10)
|
||||
|
||||
* Fix a security issue when an included sandboxed template has been loaded before without the sandbox context
|
||||
|
|
6
vendor/twig/twig/src/Environment.php
vendored
6
vendor/twig/twig/src/Environment.php
vendored
|
@ -43,11 +43,11 @@ use Twig\TokenParser\TokenParserInterface;
|
|||
*/
|
||||
class Environment
|
||||
{
|
||||
public const VERSION = '3.11.1';
|
||||
public const VERSION_ID = 301101;
|
||||
public const VERSION = '3.11.3';
|
||||
public const VERSION_ID = 301103;
|
||||
public const MAJOR_VERSION = 4;
|
||||
public const MINOR_VERSION = 11;
|
||||
public const RELEASE_VERSION = 1;
|
||||
public const RELEASE_VERSION = 3;
|
||||
public const EXTRA_VERSION = '';
|
||||
|
||||
private $charset;
|
||||
|
|
64
vendor/twig/twig/src/Extension/CoreExtension.php
vendored
64
vendor/twig/twig/src/Extension/CoreExtension.php
vendored
|
@ -57,6 +57,8 @@ use Twig\Node\Expression\Unary\NegUnary;
|
|||
use Twig\Node\Expression\Unary\NotUnary;
|
||||
use Twig\Node\Expression\Unary\PosUnary;
|
||||
use Twig\NodeVisitor\MacroAutoImportNodeVisitor;
|
||||
use Twig\Sandbox\SecurityNotAllowedMethodError;
|
||||
use Twig\Sandbox\SecurityNotAllowedPropertyError;
|
||||
use Twig\Source;
|
||||
use Twig\Template;
|
||||
use Twig\TemplateWrapper;
|
||||
|
@ -82,6 +84,20 @@ use Twig\TwigTest;
|
|||
|
||||
final class CoreExtension extends AbstractExtension
|
||||
{
|
||||
public const ARRAY_LIKE_CLASSES = [
|
||||
'ArrayIterator',
|
||||
'ArrayObject',
|
||||
'CachingIterator',
|
||||
'RecursiveArrayIterator',
|
||||
'RecursiveCachingIterator',
|
||||
'SplDoublyLinkedList',
|
||||
'SplFixedArray',
|
||||
'SplObjectStorage',
|
||||
'SplQueue',
|
||||
'SplStack',
|
||||
'WeakMap',
|
||||
];
|
||||
|
||||
private $dateFormats = ['F j, Y H:i', '%d days'];
|
||||
private $numberFormat = [0, '.', ','];
|
||||
private $timezone = null;
|
||||
|
@ -1549,10 +1565,20 @@ final class CoreExtension extends AbstractExtension
|
|||
*/
|
||||
public static function getAttribute(Environment $env, Source $source, $object, $item, array $arguments = [], $type = /* Template::ANY_CALL */ 'any', $isDefinedTest = false, $ignoreStrictCheck = false, $sandboxed = false, int $lineno = -1)
|
||||
{
|
||||
$propertyNotAllowedError = null;
|
||||
|
||||
// array
|
||||
if (/* Template::METHOD_CALL */ 'method' !== $type) {
|
||||
$arrayItem = \is_bool($item) || \is_float($item) ? (int) $item : $item;
|
||||
|
||||
if ($sandboxed && $object instanceof \ArrayAccess && !\in_array(get_class($object), self::ARRAY_LIKE_CLASSES, true)) {
|
||||
try {
|
||||
$env->getExtension(SandboxExtension::class)->checkPropertyAllowed($object, $arrayItem, $lineno, $source);
|
||||
} catch (SecurityNotAllowedPropertyError $propertyNotAllowedError) {
|
||||
goto methodCheck;
|
||||
}
|
||||
}
|
||||
|
||||
if (((\is_array($object) || $object instanceof \ArrayObject) && (isset($object[$arrayItem]) || \array_key_exists($arrayItem, (array) $object)))
|
||||
|| ($object instanceof \ArrayAccess && isset($object[$arrayItem]))
|
||||
) {
|
||||
|
@ -1624,19 +1650,25 @@ final class CoreExtension extends AbstractExtension
|
|||
|
||||
// object property
|
||||
if (/* Template::METHOD_CALL */ 'method' !== $type) {
|
||||
if ($sandboxed) {
|
||||
try {
|
||||
$env->getExtension(SandboxExtension::class)->checkPropertyAllowed($object, $item, $lineno, $source);
|
||||
} catch (SecurityNotAllowedPropertyError $propertyNotAllowedError) {
|
||||
goto methodCheck;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($object->$item) || \array_key_exists((string) $item, (array) $object)) {
|
||||
if ($isDefinedTest) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($sandboxed) {
|
||||
$env->getExtension(SandboxExtension::class)->checkPropertyAllowed($object, $item, $lineno, $source);
|
||||
}
|
||||
|
||||
return $object->$item;
|
||||
}
|
||||
}
|
||||
|
||||
methodCheck:
|
||||
|
||||
static $cache = [];
|
||||
|
||||
$class = \get_class($object);
|
||||
|
@ -1695,6 +1727,10 @@ final class CoreExtension extends AbstractExtension
|
|||
return false;
|
||||
}
|
||||
|
||||
if ($propertyNotAllowedError) {
|
||||
throw $propertyNotAllowedError;
|
||||
}
|
||||
|
||||
if ($ignoreStrictCheck || !$env->isStrictVariables()) {
|
||||
return;
|
||||
}
|
||||
|
@ -1702,12 +1738,24 @@ final class CoreExtension extends AbstractExtension
|
|||
throw new RuntimeError(\sprintf('Neither the property "%1$s" nor one of the methods "%1$s()", "get%1$s()"/"is%1$s()"/"has%1$s()" or "__call()" exist and have public access in class "%2$s".', $item, $class), $lineno, $source);
|
||||
}
|
||||
|
||||
if ($isDefinedTest) {
|
||||
return true;
|
||||
if ($sandboxed) {
|
||||
try {
|
||||
$env->getExtension(SandboxExtension::class)->checkMethodAllowed($object, $method, $lineno, $source);
|
||||
} catch (SecurityNotAllowedMethodError $e) {
|
||||
if ($isDefinedTest) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($propertyNotAllowedError) {
|
||||
throw $propertyNotAllowedError;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
if ($sandboxed) {
|
||||
$env->getExtension(SandboxExtension::class)->checkMethodAllowed($object, $method, $lineno, $source);
|
||||
if ($isDefinedTest) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Some objects throw exceptions when they have __call, and the method we try
|
||||
|
|
|
@ -119,6 +119,12 @@ final class SandboxExtension extends AbstractExtension
|
|||
|
||||
public function ensureToStringAllowed($obj, int $lineno = -1, ?Source $source = null)
|
||||
{
|
||||
if (\is_array($obj)) {
|
||||
$this->ensureToStringAllowedForArray($obj, $lineno, $source);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
if ($this->isSandboxed($source) && \is_object($obj) && method_exists($obj, '__toString')) {
|
||||
try {
|
||||
$this->policy->checkMethodAllowed($obj, '__toString');
|
||||
|
@ -132,4 +138,45 @@ final class SandboxExtension extends AbstractExtension
|
|||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
private function ensureToStringAllowedForArray(array $obj, int $lineno, ?Source $source, array &$stack = []): void
|
||||
{
|
||||
foreach ($obj as $k => $v) {
|
||||
if (!$v) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!\is_array($v)) {
|
||||
$this->ensureToStringAllowed($v, $lineno, $source);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (\PHP_VERSION_ID < 70400) {
|
||||
static $cookie;
|
||||
|
||||
if ($v === $cookie ?? $cookie = new \stdClass()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$obj[$k] = $cookie;
|
||||
try {
|
||||
$this->ensureToStringAllowedForArray($v, $lineno, $source, $stack);
|
||||
} finally {
|
||||
$obj[$k] = $v;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($r = \ReflectionReference::fromArrayElement($obj, $k)) {
|
||||
if (isset($stack[$r->getId()])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$stack[$r->getId()] = true;
|
||||
}
|
||||
|
||||
$this->ensureToStringAllowedForArray($v, $lineno, $source, $stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ class GetAttrExpression extends AbstractExpression
|
|||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$env = $compiler->getEnvironment();
|
||||
$arrayAccessSandbox = false;
|
||||
|
||||
// optimize array calls
|
||||
if (
|
||||
|
@ -44,17 +45,35 @@ class GetAttrExpression extends AbstractExpression
|
|||
->raw('(('.$var.' = ')
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw(') && is_array(')
|
||||
->raw($var)
|
||||
->raw($var);
|
||||
|
||||
if (!$env->hasExtension(SandboxExtension::class)) {
|
||||
$compiler
|
||||
->raw(') || ')
|
||||
->raw($var)
|
||||
->raw(' instanceof ArrayAccess ? (')
|
||||
->raw($var)
|
||||
->raw('[')
|
||||
->subcompile($this->getNode('attribute'))
|
||||
->raw('] ?? null) : null)')
|
||||
;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$arrayAccessSandbox = true;
|
||||
|
||||
$compiler
|
||||
->raw(') || ')
|
||||
->raw($var)
|
||||
->raw(' instanceof ArrayAccess ? (')
|
||||
->raw(' instanceof ArrayAccess && in_array(')
|
||||
->raw('get_class('.$var.')')
|
||||
->raw(', CoreExtension::ARRAY_LIKE_CLASSES, true) ? (')
|
||||
->raw($var)
|
||||
->raw('[')
|
||||
->subcompile($this->getNode('attribute'))
|
||||
->raw('] ?? null) : null)')
|
||||
->raw('] ?? null) : ')
|
||||
;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$compiler->raw('CoreExtension::getAttribute($this->env, $this->source, ');
|
||||
|
@ -83,5 +102,9 @@ class GetAttrExpression extends AbstractExpression
|
|||
->raw(', ')->repr($this->getNode('node')->getTemplateLine())
|
||||
->raw(')')
|
||||
;
|
||||
|
||||
if ($arrayAccessSandbox) {
|
||||
$compiler->raw(')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,12 +15,14 @@ use Twig\Environment;
|
|||
use Twig\Node\CheckSecurityCallNode;
|
||||
use Twig\Node\CheckSecurityNode;
|
||||
use Twig\Node\CheckToStringNode;
|
||||
use Twig\Node\Expression\ArrayExpression;
|
||||
use Twig\Node\Expression\Binary\ConcatBinary;
|
||||
use Twig\Node\Expression\Binary\RangeBinary;
|
||||
use Twig\Node\Expression\FilterExpression;
|
||||
use Twig\Node\Expression\FunctionExpression;
|
||||
use Twig\Node\Expression\GetAttrExpression;
|
||||
use Twig\Node\Expression\NameExpression;
|
||||
use Twig\Node\Expression\Unary\SpreadUnary;
|
||||
use Twig\Node\ModuleNode;
|
||||
use Twig\Node\Node;
|
||||
use Twig\Node\PrintNode;
|
||||
|
@ -120,7 +122,18 @@ final class SandboxNodeVisitor implements NodeVisitorInterface
|
|||
{
|
||||
$expr = $node->getNode($name);
|
||||
if (($expr instanceof NameExpression || $expr instanceof GetAttrExpression) && !$expr->isGenerator()) {
|
||||
$node->setNode($name, new CheckToStringNode($expr));
|
||||
// Simplify in 4.0 as the spread attribute has been removed there
|
||||
$new = new CheckToStringNode($expr);
|
||||
if ($expr->hasAttribute('spread')) {
|
||||
$new->setAttribute('spread', $expr->getAttribute('spread'));
|
||||
}
|
||||
$node->setNode($name, $new);
|
||||
} elseif ($expr instanceof SpreadUnary) {
|
||||
$this->wrapNode($expr, 'node');
|
||||
} elseif ($expr instanceof ArrayExpression) {
|
||||
foreach ($expr as $name => $_) {
|
||||
$this->wrapNode($expr, $name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue