Skip to content

Commit

Permalink
security #cve-2019-10910 [DI] Check service IDs are valid (nicolas-gr…
Browse files Browse the repository at this point in the history
…ekas)

This PR was merged into the 2.8 branch.

Discussion
----------

[DI] Check service IDs are valid

Based on #87

Commits
-------

7fdfeefdfb [DI] Check service IDs are valid
  • Loading branch information
nicolas-grekas committed Apr 16, 2019
1 parent a2f40df commit c306198
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 10 deletions.
8 changes: 8 additions & 0 deletions ContainerBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,10 @@ public function setAlias($alias, $id)
{
$alias = strtolower($alias);

if ('' === $alias || '\\' === substr($alias, -1) || \strlen($alias) !== strcspn($alias, "\0\r\n'")) {
throw new InvalidArgumentException(sprintf('Invalid alias id: "%s"', $alias));
}

if (\is_string($id)) {
$id = new Alias($id);
} elseif (!$id instanceof Alias) {
Expand Down Expand Up @@ -775,6 +779,10 @@ public function setDefinition($id, Definition $definition)

$id = strtolower($id);

if ('' === $id || '\\' === substr($id, -1) || \strlen($id) !== strcspn($id, "\0\r\n'")) {
throw new InvalidArgumentException(sprintf('Invalid service id: "%s"', $id));
}

unset($this->aliasDefinitions[$id]);

return $this->definitions[$id] = $definition;
Expand Down
26 changes: 16 additions & 10 deletions Dumper/PhpDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -379,9 +379,9 @@ private function addServiceInstance($id, Definition $definition)
$instantiation = '';

if (!$isProxyCandidate && $definition->isShared() && ContainerInterface::SCOPE_CONTAINER === $definition->getScope(false)) {
$instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance');
$instantiation = sprintf('$this->services[%s] = %s', var_export($id, true), $simple ? '' : '$instance');
} elseif (!$isProxyCandidate && $definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope(false)) {
$instantiation = "\$this->services['$id'] = \$this->scopedServices['$scope']['$id'] = ".($simple ? '' : '$instance');
$instantiation = sprintf('$this->services[%s] = $this->scopedServices[%s][%1$s] = %s', var_export($id, true), var_export($scope, true), $simple ? '' : '$instance');
} elseif (!$simple) {
$instantiation = '$instance';
}
Expand Down Expand Up @@ -609,6 +609,9 @@ private function addService($id, Definition $definition)
* Gets the $public '$id'$shared$autowired service.
*
* $return
EOF;
$code = str_replace('*/', ' ', $code).<<<EOF
*/
{$visibility} function get{$this->camelize($id)}Service($lazyInitialization)
{
Expand All @@ -620,15 +623,15 @@ private function addService($id, Definition $definition)
if (!\in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) {
$code .= <<<EOF
if (!isset(\$this->scopedServices['$scope'])) {
throw new InactiveScopeException('$id', '$scope');
throw new InactiveScopeException({$this->export($id)}, '$scope');
}
EOF;
}

if ($definition->isSynthetic()) {
$code .= sprintf(" throw new RuntimeException('You have requested a synthetic service (\"%s\"). The DIC does not know how to construct this service.');\n }\n", $id);
$code .= sprintf(" throw new RuntimeException(%s);\n }\n", var_export("You have requested a synthetic service (\"$id\"). The DIC does not know how to construct this service.", true));
} else {
if ($definition->isDeprecated()) {
$code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", var_export($definition->getDeprecationMessage($id), true));
Expand Down Expand Up @@ -706,10 +709,11 @@ private function addServiceSynchronizer($id, Definition $definition)
$arguments[] = $this->dumpValue($value);
}

$call = $this->wrapServiceConditionals($call[1], sprintf("\$this->get('%s')->%s(%s);", $definitionId, $call[0], implode(', ', $arguments)));
$definitionId = var_export($definitionId, true);
$call = $this->wrapServiceConditionals($call[1], sprintf('$this->get(%s)->%s(%s);', $definitionId, $call[0], implode(', ', $arguments)));

$code .= <<<EOF
if (\$this->initialized('$definitionId')) {
if (\$this->initialized($definitionId)) {
$call
}
Expand Down Expand Up @@ -1142,7 +1146,7 @@ private function wrapServiceConditionals($value, $code)

$conditions = array();
foreach ($services as $service) {
$conditions[] = sprintf("\$this->has('%s')", $service);
$conditions[] = sprintf('$this->has(%s)', var_export($service, true));
}

// re-indent the wrapped code
Expand Down Expand Up @@ -1419,11 +1423,13 @@ private function dumpLiteralClass($class)
*/
public function dumpParameter($name)
{
$name = (string) $name;

if ($this->container->isFrozen() && $this->container->hasParameter($name)) {
return $this->dumpValue($this->container->getParameter($name), false);
}

return sprintf("\$this->getParameter('%s')", strtolower($name));
return sprintf('$this->getParameter(%s)', var_export($name, true));
}

/**
Expand Down Expand Up @@ -1458,10 +1464,10 @@ private function getServiceCall($id, Reference $reference = null)
}

if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
return sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id);
return sprintf('$this->get(%s, ContainerInterface::NULL_ON_INVALID_REFERENCE)', var_export($id, true));
}

return sprintf('$this->get(\'%s\')', $id);
return sprintf('$this->get(%s)', var_export($id, true));
}

/**
Expand Down
32 changes: 32 additions & 0 deletions Tests/ContainerBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,38 @@ public function testNonSharedServicesReturnsDifferentInstances()
$this->assertNotSame($builder->get('bar'), $builder->get('bar'));
}

/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
* @dataProvider provideBadId
*/
public function testBadAliasId($id)
{
$builder = new ContainerBuilder();
$builder->setAlias($id, 'foo');
}

/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
* @dataProvider provideBadId
*/
public function testBadDefinitionId($id)
{
$builder = new ContainerBuilder();
$builder->setDefinition($id, new Definition('Foo'));
}

public function provideBadId()
{
return [
[''],
["\0"],
["\r"],
["\n"],
["'"],
['ab\\'],
];
}

/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
* @expectedExceptionMessage You have requested a synthetic service ("foo"). The DIC does not know how to construct this service.
Expand Down

0 comments on commit c306198

Please sign in to comment.