Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[DI] Validate env vars in config #23888

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

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
optimized
  • Loading branch information
ro0NL committed Mar 22, 2018
commit d33b1c8de2a49dc50e831a4c19d8c9b9697c4d99
129 changes: 76 additions & 53 deletions 129 src/Symfony/Component/Config/Definition/BaseNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ abstract class BaseNode implements NodeInterface
protected $attributes = array();
protected $pathSeparator;

private $handlingPlaceholder = false;
private $handlingPlaceholder;

/**
* @throws \InvalidArgumentException if the name contains a period
Expand Down Expand Up @@ -289,47 +289,33 @@ final public function merge($leftSide, $rightSide)
}

if ($leftSide !== $leftPlaceholders = self::resolvePlaceholderValue($leftSide)) {
if (!$leftPlaceholders) {
return $rightSide;
}

foreach ($leftPlaceholders as $leftPlaceholder) {
$this->handlingPlaceholder = true;
$this->handlingPlaceholder = $leftSide;
try {
$this->merge($leftPlaceholder, $rightSide);

return $rightSide;
} catch (InvalidConfigurationException $e) {
} finally {
$this->handlingPlaceholder = false;
$this->handlingPlaceholder = null;
}
}

throw $e;
return $rightSide;
}

if ($rightSide !== $rightPlaceholders = self::resolvePlaceholderValue($rightSide)) {
if (!$rightPlaceholders) {
return $rightSide;
}

foreach ($rightPlaceholders as $rightPlaceholder) {
$this->handlingPlaceholder = true;
$this->handlingPlaceholder = $rightSide;
try {
$this->merge($leftSide, $rightPlaceholder);

return $rightSide;
} catch (InvalidConfigurationException $e) {
} finally {
$this->handlingPlaceholder = false;
$this->handlingPlaceholder = null;
}
}

throw $e;
return $rightSide;
}

$this->validateType($leftSide);
$this->validateType($rightSide);
$this->doValidateType($leftSide);
$this->doValidateType($rightSide);

return $this->mergeValues($leftSide, $rightSide);
}
Expand All @@ -348,23 +334,16 @@ final public function normalize($value)

// resolve placeholder value
if ($value !== $placeholders = self::resolvePlaceholderValue($value)) {
if (!$placeholders) {
return $value;
}

foreach ($placeholders as $placeholder) {
$this->handlingPlaceholder = true;
$this->handlingPlaceholder = $value;
try {
$this->normalize($placeholder);

return $value;
} catch (InvalidConfigurationException $e) {
} finally {
$this->handlingPlaceholder = false;
$this->handlingPlaceholder = null;
}
}

throw $e;
return $value;
}

// replace value with their equivalent
Expand All @@ -375,7 +354,7 @@ final public function normalize($value)
}

// validate type
$this->validateType($value);
$this->doValidateType($value);

// normalize value
return $this->normalizeValue($value);
Expand Down Expand Up @@ -409,33 +388,19 @@ public function getParent()
final public function finalize($value)
{
if ($value !== $placeholders = self::resolvePlaceholderValue($value)) {
if (!$placeholders) {
return $value;
}

foreach ($placeholders as $placeholder) {
$this->handlingPlaceholder = true;
$this->handlingPlaceholder = $value;
try {
$this->finalize($placeholder);

return $value;
} catch (InvalidConfigurationException $e) {
} finally {
$this->handlingPlaceholder = false;
$this->handlingPlaceholder = null;
}
}

throw $e;
return $value;
}

$this->validateType($value);

if ($this->handlingPlaceholder && !$this->allowPlaceholders()) {
$e = new InvalidConfigurationException(sprintf('A dynamic value is not compatible with a "%s" node type at path "%s".', get_class($this), $this->getPath()));
$e->setPath($this->getPath());

throw $e;
}
$this->doValidateType($value);

$value = $this->finalizeValue($value);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case the value is a placeholder, I think we should preserve the value before the finalization (which will be the placeholder) and return this one instead. Otherwise, transformations done on the placeholder would break it. It should still be called though.

this advice should be challenged though, as it may depend on the usage of the finalization.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's exactly what we do no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


Expand All @@ -445,7 +410,7 @@ final public function finalize($value)
try {
$value = $closure($value);
} catch (Exception $e) {
if ($e instanceof UnsetKeyException && $this->handlingPlaceholder) {
if ($e instanceof UnsetKeyException && null !== $this->handlingPlaceholder) {
continue;
}

Expand Down Expand Up @@ -503,6 +468,14 @@ protected function allowPlaceholders(): bool
return true;
}

/**
* Get allowed dynamic types for this node.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gets

*/
protected function getValidPlaceholderTypes(): array
{
return array();
}

private static function resolvePlaceholderValue($value)
{
if (\is_string($value)) {
Expand All @@ -517,4 +490,54 @@ private static function resolvePlaceholderValue($value)

return $value;
}

private static function getType($value): string
{
switch ($type = gettype($value)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

\gettype

case 'boolean':
return 'bool';
case 'double':
return 'float';
case 'integer':
return 'int';
}

return $type;
}

private function doValidateType($value): void
{
if (null === $this->handlingPlaceholder || null === $value) {
$this->validateType($value);

return;
}

if (!$this->allowPlaceholders()) {
$e = new InvalidTypeException(sprintf('A dynamic value is not compatible with a "%s" node type at path "%s".', get_class($this), $this->getPath()));
$e->setPath($this->getPath());

throw $e;
}

$knownTypes = array_keys(self::$placeholders[$this->handlingPlaceholder]);
$validTypes = $this->getValidPlaceholderTypes();

if (array_diff($knownTypes, $validTypes)) {
$e = new InvalidTypeException(sprintf(
'Invalid type for path "%s". Expected %s, but got %s.',
$this->getPath(),
1 === count($validTypes) ? '"'.reset($validTypes).'"' : 'one of "'.implode('", "', $validTypes).'"',
1 === count($knownTypes) ? '"'.reset($knownTypes).'"' : 'one of "'.implode('", "', $knownTypes).'"'
));
if ($hint = $this->getInfo()) {
$e->addHint($hint);
}
$e->setPath($this->getPath());

throw $e;
}

$this->validateType($value);
}
}
8 changes: 8 additions & 0 deletions 8 src/Symfony/Component/Config/Definition/BooleanNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,12 @@ protected function isValueEmpty($value)
// a boolean value cannot be empty
return false;
}

/**
* {@inheritdoc}
*/
protected function getValidPlaceholderTypes(): array
{
return array('bool');
}
}
8 changes: 8 additions & 0 deletions 8 src/Symfony/Component/Config/Definition/FloatNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,12 @@ protected function validateType($value)
throw $ex;
}
}

/**
* {@inheritdoc}
*/
protected function getValidPlaceholderTypes(): array
{
return array('float');
}
}
8 changes: 8 additions & 0 deletions 8 src/Symfony/Component/Config/Definition/IntegerNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,12 @@ protected function validateType($value)
throw $ex;
}
}

/**
* {@inheritdoc}
*/
protected function getValidPlaceholderTypes(): array
{
return array('int');
}
}
8 changes: 8 additions & 0 deletions 8 src/Symfony/Component/Config/Definition/ScalarNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,12 @@ protected function isValueEmpty($value)
{
return null === $value || '' === $value;
}

/**
* {@inheritdoc}
*/
protected function getValidPlaceholderTypes(): array
{
return array('bool', 'int', 'float', 'string');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,22 @@ public function testEnvsAreValidatedInConfig()
'scalar_node' => '%env(NULLED)%',
'int_node' => '%env(int:FOO)%',
'float_node' => '%env(float:BAR)%',
));

$this->doProcess($container);

$this->assertSame($expected, $container->resolveEnvPlaceholders($ext->getConfig()));
}

/**
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException
* @expectedExceptionMessage Invalid type for path "env_extension.bool_node". Expected "bool", but got one of "bool", "int", "float", "string", "array".
*/
public function testEnvsAreValidatedInConfigWithInvalidPlaceholder()
{
$container = new ContainerBuilder();
$container->registerExtension($ext = new EnvExtension());
$container->prependExtensionConfig('env_extension', $expected = array(
'bool_node' => '%env(const:BAZ)%',
Copy link
Contributor Author

@ro0NL ro0NL Mar 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps this should be invalid 🤔 it can provide a bool, but also a string or so.

so we need to be a bit more clear on what a placeholder value "means" in Config, that is; i think those must be valid for the current node type domain. A bool node only expect boolean dummy values for a placeholder.

If it encounters a different type, the config is invalid.

));

Expand All @@ -57,7 +73,7 @@ public function testEnvsAreValidatedInConfig()

/**
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException
* @expectedExceptionMessage Invalid type for path "env_extension.int_node". Expected int, but got array.
* @expectedExceptionMessage Invalid type for path "env_extension.int_node". Expected "int", but got "array".
*/
public function testInvalidEnvInConfig()
{
Expand Down Expand Up @@ -91,7 +107,7 @@ public function testValidateEnvOnMerge()
$container = new ContainerBuilder();
$container->registerExtension($ext = new EnvExtension());
$container->prependExtensionConfig('env_extension', array(
'int_node' => '%env(const:FOO)%',
'int_node' => '%env(int:const:FOO)%',
'bool_node' => true,
));
$container->prependExtensionConfig('env_extension', array(
Expand All @@ -103,7 +119,7 @@ public function testValidateEnvOnMerge()
$this->doProcess($container);

$expected = array(
'int_node' => '%env(const:FOO)%',
'int_node' => '%env(int:const:FOO)%',
'bool_node' => true,
'scalar_node' => '%env(BAZ)%',
);
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.