mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-24 11:08:45 +02:00
N°8017 - Security - dependabot - Symfony's VarDumper vulnerable to un… (#731)
Upgrade all Symfony components to last security fix (~6.4.0)
This commit is contained in:
@@ -82,7 +82,7 @@ abstract class AbstractRecursivePass implements CompilerPassInterface
|
||||
continue;
|
||||
}
|
||||
if ($isRoot) {
|
||||
if ($v->hasTag('container.excluded')) {
|
||||
if ($v instanceof Definition && $v->hasTag('container.excluded')) {
|
||||
continue;
|
||||
}
|
||||
$this->currentId = $k;
|
||||
@@ -219,6 +219,10 @@ abstract class AbstractRecursivePass implements CompilerPassInterface
|
||||
return new \ReflectionMethod(static function (...$arguments) {}, '__invoke');
|
||||
}
|
||||
|
||||
if ($r->hasMethod('__callStatic') && ($r = $r->getMethod('__callStatic')) && $r->isPublic()) {
|
||||
return new \ReflectionMethod(static function (...$arguments) {}, '__invoke');
|
||||
}
|
||||
|
||||
throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" does not exist.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method));
|
||||
}
|
||||
|
||||
|
||||
@@ -696,7 +696,7 @@ class AutowirePass extends AbstractRecursivePass
|
||||
return null;
|
||||
}
|
||||
|
||||
private function populateAutowiringAlias(string $id, string $target = null): void
|
||||
private function populateAutowiringAlias(string $id, ?string $target = null): void
|
||||
{
|
||||
if (!preg_match('/(?(DEFINE)(?<V>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+))^((?&V)(?:\\\\(?&V))*+)(?: \$((?&V)))?$/', $id, $m)) {
|
||||
return;
|
||||
@@ -716,7 +716,7 @@ class AutowirePass extends AbstractRecursivePass
|
||||
}
|
||||
}
|
||||
|
||||
private function getCombinedAlias(string $type, string $name = null): ?string
|
||||
private function getCombinedAlias(string $type, ?string $name = null): ?string
|
||||
{
|
||||
if (str_contains($type, '&')) {
|
||||
$types = explode('&', $type);
|
||||
|
||||
@@ -28,6 +28,7 @@ class CheckCircularReferencesPass implements CompilerPassInterface
|
||||
{
|
||||
private array $currentPath;
|
||||
private array $checkedNodes;
|
||||
private array $checkedLazyNodes;
|
||||
|
||||
/**
|
||||
* Checks the ContainerBuilder object for circular references.
|
||||
@@ -59,22 +60,36 @@ class CheckCircularReferencesPass implements CompilerPassInterface
|
||||
$node = $edge->getDestNode();
|
||||
$id = $node->getId();
|
||||
|
||||
if (empty($this->checkedNodes[$id])) {
|
||||
// Don't check circular references for lazy edges
|
||||
if (!$node->getValue() || (!$edge->isLazy() && !$edge->isWeak())) {
|
||||
$searchKey = array_search($id, $this->currentPath);
|
||||
$this->currentPath[] = $id;
|
||||
if (!empty($this->checkedNodes[$id])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (false !== $searchKey) {
|
||||
throw new ServiceCircularReferenceException($id, \array_slice($this->currentPath, $searchKey));
|
||||
}
|
||||
$isLeaf = !!$node->getValue();
|
||||
$isConcrete = !$edge->isLazy() && !$edge->isWeak();
|
||||
|
||||
$this->checkOutEdges($node->getOutEdges());
|
||||
// Skip already checked lazy services if they are still lazy. Will not gain any new information.
|
||||
if (!empty($this->checkedLazyNodes[$id]) && (!$isLeaf || !$isConcrete)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Process concrete references, otherwise defer check circular references for lazy edges.
|
||||
if (!$isLeaf || $isConcrete) {
|
||||
$searchKey = array_search($id, $this->currentPath);
|
||||
$this->currentPath[] = $id;
|
||||
|
||||
if (false !== $searchKey) {
|
||||
throw new ServiceCircularReferenceException($id, \array_slice($this->currentPath, $searchKey));
|
||||
}
|
||||
|
||||
$this->checkOutEdges($node->getOutEdges());
|
||||
|
||||
$this->checkedNodes[$id] = true;
|
||||
array_pop($this->currentPath);
|
||||
unset($this->checkedLazyNodes[$id]);
|
||||
} else {
|
||||
$this->checkedLazyNodes[$id] = true;
|
||||
}
|
||||
|
||||
array_pop($this->currentPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,15 +60,7 @@ class CheckExceptionOnInvalidReferenceBehaviorPass extends AbstractRecursivePass
|
||||
if (isset($this->serviceLocatorContextIds[$currentId])) {
|
||||
$currentId = $this->serviceLocatorContextIds[$currentId];
|
||||
$locator = $this->container->getDefinition($this->currentId)->getFactory()[0];
|
||||
|
||||
foreach ($locator->getArgument(0) as $k => $v) {
|
||||
if ($v->getValues()[0] === $value) {
|
||||
if ($k !== $id) {
|
||||
$currentId = $k.'" in the container provided to "'.$currentId;
|
||||
}
|
||||
throw new ServiceNotFoundException($id, $currentId, null, $this->getAlternatives($id));
|
||||
}
|
||||
}
|
||||
$this->throwServiceNotFoundException($value, $currentId, $locator->getArgument(0));
|
||||
}
|
||||
|
||||
if ('.' === $currentId[0] && $graph->hasNode($currentId)) {
|
||||
@@ -82,14 +74,21 @@ class CheckExceptionOnInvalidReferenceBehaviorPass extends AbstractRecursivePass
|
||||
$currentId = $sourceId;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($this->serviceLocatorContextIds[$sourceId])) {
|
||||
$currentId = $this->serviceLocatorContextIds[$sourceId];
|
||||
$locator = $this->container->getDefinition($this->currentId);
|
||||
$this->throwServiceNotFoundException($value, $currentId, $locator->getArgument(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new ServiceNotFoundException($id, $currentId, null, $this->getAlternatives($id));
|
||||
$this->throwServiceNotFoundException($value, $currentId, $value);
|
||||
}
|
||||
|
||||
private function getAlternatives(string $id): array
|
||||
private function throwServiceNotFoundException(Reference $ref, string $sourceId, $value): void
|
||||
{
|
||||
$id = (string) $ref;
|
||||
$alternatives = [];
|
||||
foreach ($this->container->getServiceIds() as $knownId) {
|
||||
if ('' === $knownId || '.' === $knownId[0] || $knownId === $this->currentId) {
|
||||
@@ -102,6 +101,28 @@ class CheckExceptionOnInvalidReferenceBehaviorPass extends AbstractRecursivePass
|
||||
}
|
||||
}
|
||||
|
||||
return $alternatives;
|
||||
$pass = new class() extends AbstractRecursivePass {
|
||||
public Reference $ref;
|
||||
public string $sourceId;
|
||||
public array $alternatives;
|
||||
|
||||
public function processValue(mixed $value, bool $isRoot = false): mixed
|
||||
{
|
||||
if ($this->ref !== $value) {
|
||||
return parent::processValue($value, $isRoot);
|
||||
}
|
||||
$sourceId = $this->sourceId;
|
||||
if (null !== $this->currentId && $this->currentId !== (string) $value) {
|
||||
$sourceId = $this->currentId.'" in the container provided to "'.$sourceId;
|
||||
}
|
||||
|
||||
throw new ServiceNotFoundException((string) $value, $sourceId, null, $this->alternatives);
|
||||
}
|
||||
};
|
||||
$pass->ref = $ref;
|
||||
$pass->sourceId = $sourceId;
|
||||
$pass->alternatives = $alternatives;
|
||||
|
||||
$pass->processValue($value, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass
|
||||
/**
|
||||
* @throws InvalidParameterTypeException When a parameter is not compatible with the declared type
|
||||
*/
|
||||
private function checkType(Definition $checkedDefinition, mixed $value, \ReflectionParameter $parameter, ?string $envPlaceholderUniquePrefix, \ReflectionType $reflectionType = null): void
|
||||
private function checkType(Definition $checkedDefinition, mixed $value, \ReflectionParameter $parameter, ?string $envPlaceholderUniquePrefix, ?\ReflectionType $reflectionType = null): void
|
||||
{
|
||||
$reflectionType ??= $parameter->getType();
|
||||
|
||||
|
||||
@@ -65,7 +65,10 @@ class DefinitionErrorExceptionPass extends AbstractRecursivePass
|
||||
}
|
||||
|
||||
if ($value instanceof Reference && $this->currentId !== $targetId = (string) $value) {
|
||||
if (ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) {
|
||||
if (
|
||||
ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior()
|
||||
|| ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior()
|
||||
) {
|
||||
$this->sourceReferences[$targetId][$this->currentId] ??= true;
|
||||
} else {
|
||||
$this->sourceReferences[$targetId][$this->currentId] = false;
|
||||
|
||||
@@ -34,7 +34,7 @@ class InlineServiceDefinitionsPass extends AbstractRecursivePass
|
||||
private array $notInlinableIds = [];
|
||||
private ?ServiceReferenceGraph $graph = null;
|
||||
|
||||
public function __construct(AnalyzeServiceReferencesPass $analyzingPass = null)
|
||||
public function __construct(?AnalyzeServiceReferencesPass $analyzingPass = null)
|
||||
{
|
||||
$this->analyzingPass = $analyzingPass;
|
||||
}
|
||||
@@ -73,6 +73,9 @@ class InlineServiceDefinitionsPass extends AbstractRecursivePass
|
||||
if (!$this->graph->hasNode($id)) {
|
||||
continue;
|
||||
}
|
||||
if ($definition->isPublic()) {
|
||||
$this->connectedIds[$id] = true;
|
||||
}
|
||||
foreach ($this->graph->getNode($id)->getOutEdges() as $edge) {
|
||||
if (isset($notInlinedIds[$edge->getSourceNode()->getId()])) {
|
||||
$this->currentId = $id;
|
||||
@@ -189,17 +192,13 @@ class InlineServiceDefinitionsPass extends AbstractRecursivePass
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($definition->isPublic()) {
|
||||
if ($definition->isPublic()
|
||||
|| $this->currentId === $id
|
||||
|| !$this->graph->hasNode($id)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->graph->hasNode($id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->currentId === $id) {
|
||||
return false;
|
||||
}
|
||||
$this->connectedIds[$id] = true;
|
||||
|
||||
$srcIds = [];
|
||||
@@ -224,6 +223,8 @@ class InlineServiceDefinitionsPass extends AbstractRecursivePass
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->container->getDefinition($srcId)->isShared();
|
||||
$srcDefinition = $this->container->getDefinition($srcId);
|
||||
|
||||
return $srcDefinition->isShared() && !$srcDefinition->isLazy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ class MergeExtensionConfigurationContainerBuilder extends ContainerBuilder
|
||||
{
|
||||
private string $extensionClass;
|
||||
|
||||
public function __construct(ExtensionInterface $extension, ParameterBagInterface $parameterBag = null)
|
||||
public function __construct(ExtensionInterface $extension, ?ParameterBagInterface $parameterBag = null)
|
||||
{
|
||||
parent::__construct($parameterBag);
|
||||
|
||||
@@ -178,7 +178,7 @@ class MergeExtensionConfigurationContainerBuilder extends ContainerBuilder
|
||||
throw new LogicException(sprintf('Cannot compile the container in extension "%s".', $this->extensionClass));
|
||||
}
|
||||
|
||||
public function resolveEnvPlaceholders(mixed $value, string|bool $format = null, array &$usedEnvs = null): mixed
|
||||
public function resolveEnvPlaceholders(mixed $value, string|bool|null $format = null, ?array &$usedEnvs = null): mixed
|
||||
{
|
||||
if (true !== $format || !\is_string($value)) {
|
||||
return parent::resolveEnvPlaceholders($value, $format, $usedEnvs);
|
||||
|
||||
@@ -85,7 +85,8 @@ trait PriorityTaggedServiceTrait
|
||||
} elseif (null === $defaultIndex && $defaultPriorityMethod && $class) {
|
||||
$defaultIndex = PriorityTaggedServiceUtil::getDefault($container, $serviceId, $class, $defaultIndexMethod ?? 'getDefaultName', $tagName, $indexAttribute, $checkTaggedItem);
|
||||
}
|
||||
$index ??= $defaultIndex ??= $serviceId;
|
||||
$decorated = $definition->getTag('container.decorator')[0]['id'] ?? null;
|
||||
$index = $index ?? $defaultIndex ?? $defaultIndex = $decorated ?? $serviceId;
|
||||
|
||||
$services[] = [$priority, ++$i, $index, $serviceId, $class];
|
||||
}
|
||||
@@ -133,6 +134,10 @@ class PriorityTaggedServiceUtil
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($r->isInterface()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (null !== $indexAttribute) {
|
||||
$service = $class !== $serviceId ? sprintf('service "%s"', $serviceId) : 'on the corresponding service';
|
||||
$message = [sprintf('Either method "%s::%s()" should ', $class, $defaultMethod), sprintf(' or tag "%s" on %s is missing attribute "%s".', $tagName, $service, $indexAttribute)];
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
|
||||
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
|
||||
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
|
||||
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Target;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
@@ -181,10 +183,17 @@ class ResolveBindingsPass extends AbstractRecursivePass
|
||||
foreach ($reflectionMethod->getParameters() as $key => $parameter) {
|
||||
$names[$key] = $parameter->name;
|
||||
|
||||
if (\array_key_exists($key, $arguments) && '' !== $arguments[$key]) {
|
||||
if (\array_key_exists($key, $arguments) && '' !== $arguments[$key] && !$arguments[$key] instanceof AbstractArgument) {
|
||||
continue;
|
||||
}
|
||||
if (\array_key_exists($parameter->name, $arguments) && '' !== $arguments[$parameter->name]) {
|
||||
if (\array_key_exists($parameter->name, $arguments) && '' !== $arguments[$parameter->name] && !$arguments[$parameter->name] instanceof AbstractArgument) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
$value->isAutowired()
|
||||
&& !$value->hasTag('container.ignore_attributes')
|
||||
&& $parameter->getAttributes(Autowire::class, \ReflectionAttribute::IS_INSTANCEOF)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -222,7 +231,9 @@ class ResolveBindingsPass extends AbstractRecursivePass
|
||||
|
||||
foreach ($names as $key => $name) {
|
||||
if (\array_key_exists($name, $arguments) && (0 === $key || \array_key_exists($key - 1, $arguments))) {
|
||||
$arguments[$key] = $arguments[$name];
|
||||
if (!array_key_exists($key, $arguments)) {
|
||||
$arguments[$key] = $arguments[$name];
|
||||
}
|
||||
unset($arguments[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ final class ServiceLocatorTagPass extends AbstractRecursivePass
|
||||
return new Reference($id);
|
||||
}
|
||||
|
||||
public static function register(ContainerBuilder $container, array $map, string $callerId = null): Reference
|
||||
public static function register(ContainerBuilder $container, array $map, ?string $callerId = null): Reference
|
||||
{
|
||||
foreach ($map as $k => $v) {
|
||||
$map[$k] = new ServiceClosureArgument($v);
|
||||
|
||||
@@ -74,7 +74,7 @@ class ServiceReferenceGraph
|
||||
/**
|
||||
* Connects 2 nodes together in the Graph.
|
||||
*/
|
||||
public function connect(?string $sourceId, mixed $sourceValue, ?string $destId, mixed $destValue = null, Reference $reference = null, bool $lazy = false, bool $weak = false, bool $byConstructor = false): void
|
||||
public function connect(?string $sourceId, mixed $sourceValue, ?string $destId, mixed $destValue = null, ?Reference $reference = null, bool $lazy = false, bool $weak = false, bool $byConstructor = false): void
|
||||
{
|
||||
if (null === $sourceId || null === $destId) {
|
||||
return;
|
||||
|
||||
@@ -49,17 +49,8 @@ class ValidateEnvPlaceholdersPass implements CompilerPassInterface
|
||||
$defaultBag = new ParameterBag($resolvingBag->all());
|
||||
$envTypes = $resolvingBag->getProvidedTypes();
|
||||
foreach ($resolvingBag->getEnvPlaceholders() + $resolvingBag->getUnusedEnvPlaceholders() as $env => $placeholders) {
|
||||
$values = [];
|
||||
if (false === $i = strpos($env, ':')) {
|
||||
$default = $defaultBag->has("env($env)") ? $defaultBag->get("env($env)") : self::TYPE_FIXTURES['string'];
|
||||
$defaultType = null !== $default ? get_debug_type($default) : 'string';
|
||||
$values[$defaultType] = $default;
|
||||
} else {
|
||||
$prefix = substr($env, 0, $i);
|
||||
foreach ($envTypes[$prefix] ?? ['string'] as $type) {
|
||||
$values[$type] = self::TYPE_FIXTURES[$type] ?? null;
|
||||
}
|
||||
}
|
||||
$values = $this->getPlaceholderValues($env, $defaultBag, $envTypes);
|
||||
|
||||
foreach ($placeholders as $placeholder) {
|
||||
BaseNode::setPlaceholder($placeholder, $values);
|
||||
}
|
||||
@@ -100,4 +91,50 @@ class ValidateEnvPlaceholdersPass implements CompilerPassInterface
|
||||
$this->extensionConfig = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, list<string>> $envTypes
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function getPlaceholderValues(string $env, ParameterBag $defaultBag, array $envTypes): array
|
||||
{
|
||||
if (false === $i = strpos($env, ':')) {
|
||||
[$default, $defaultType] = $this->getParameterDefaultAndDefaultType("env($env)", $defaultBag);
|
||||
|
||||
return [$defaultType => $default];
|
||||
}
|
||||
|
||||
$prefix = substr($env, 0, $i);
|
||||
if ('default' === $prefix) {
|
||||
$parts = explode(':', $env);
|
||||
array_shift($parts); // Remove 'default' prefix
|
||||
$parameter = array_shift($parts); // Retrieve and remove parameter
|
||||
|
||||
[$defaultParameter, $defaultParameterType] = $this->getParameterDefaultAndDefaultType($parameter, $defaultBag);
|
||||
|
||||
return [
|
||||
$defaultParameterType => $defaultParameter,
|
||||
...$this->getPlaceholderValues(implode(':', $parts), $defaultBag, $envTypes),
|
||||
];
|
||||
}
|
||||
|
||||
$values = [];
|
||||
foreach ($envTypes[$prefix] ?? ['string'] as $type) {
|
||||
$values[$type] = self::TYPE_FIXTURES[$type] ?? null;
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{0: string, 1: string}
|
||||
*/
|
||||
private function getParameterDefaultAndDefaultType(string $name, ParameterBag $defaultBag): array
|
||||
{
|
||||
$default = $defaultBag->has($name) ? $defaultBag->get($name) : self::TYPE_FIXTURES['string'];
|
||||
$defaultType = null !== $default ? get_debug_type($default) : 'string';
|
||||
|
||||
return [$default, $defaultType];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user