diff --git a/CHANGELOG-4.4.md b/CHANGELOG-4.4.md
index 70feb9ba3cab..68eceda5b974 100644
--- a/CHANGELOG-4.4.md
+++ b/CHANGELOG-4.4.md
@@ -7,6 +7,18 @@ in 4.4 minor versions.
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v4.4.0...v4.4.1
+* 4.4.7 (2020-03-30)
+
+ * security #cve-2020-5255 [HttpFoundation] Do not set the default Content-Type based on the Accept header (yceruto)
+ * security #cve-2020-5275 [Security] Fix access_control behavior with unanimous decision strategy (chalasr)
+ * bug #36262 [DI] fix generating TypedReference from PriorityTaggedServiceTrait (nicolas-grekas)
+ * bug #36252 [Security/Http] Allow setting cookie security settings for delete_cookies (wouterj)
+ * bug #36261 [FrameworkBundle] revert to legacy wiring of the session when circular refs are detected (nicolas-grekas)
+ * bug #36259 [DomCrawler] Fix BC break in assertions breaking Panther (dunglas)
+ * bug #36181 [BrowserKit] fixed missing post request parameters in file uploads (codebay)
+ * bug #36216 [Validator] Assert Valid with many groups (phucwan91)
+ * bug #36222 [Console] Fix OutputStream for PHP 7.4 (guillbdx)
+
* 4.4.6 (2020-03-27)
* bug #36169 [HttpKernel] fix locking for PHP 7.4+ (nicolas-grekas)
diff --git a/src/Symfony/Bridge/Twig/Command/LintCommand.php b/src/Symfony/Bridge/Twig/Command/LintCommand.php
index 03b7f6d2f8d1..66c108af35b7 100644
--- a/src/Symfony/Bridge/Twig/Command/LintCommand.php
+++ b/src/Symfony/Bridge/Twig/Command/LintCommand.php
@@ -103,7 +103,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
$prevErrorHandler = set_error_handler(static function ($level, $message, $file, $line) use (&$prevErrorHandler) {
if (E_USER_DEPRECATED === $level) {
$templateLine = 0;
- if (preg_match('/ at line (\d+) /', $message, $matches)) {
+ if (preg_match('/ at line (\d+)[ .]/', $message, $matches)) {
$templateLine = $matches[1];
}
@@ -229,6 +229,14 @@ private function renderException(OutputInterface $output, string $template, Erro
$output->text(sprintf(' ERROR (line %s)', $line));
}
+ // If the line is not known (this might happen for deprecations if we fail at detecting the line for instance),
+ // we render the message without context, to ensure the message is displayed.
+ if ($line <= 0) {
+ $output->text(sprintf(' >> %s ', $exception->getRawMessage()));
+
+ return;
+ }
+
foreach ($this->getContext($template, $line) as $lineNumber => $code) {
$output->text(sprintf(
'%s %-6s %s',
diff --git a/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php b/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php
index 890506840519..8970bb497764 100644
--- a/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php
@@ -18,6 +18,7 @@
use Symfony\Component\Console\Tester\CommandTester;
use Twig\Environment;
use Twig\Loader\FilesystemLoader;
+use Twig\TwigFilter;
class LintCommandTest extends TestCase
{
@@ -66,6 +67,37 @@ public function testLintFileCompileTimeException()
$this->assertRegExp('/ERROR in \S+ \(line /', trim($tester->getDisplay()));
}
+ /**
+ * When deprecations are not reported by the command, the testsuite reporter will catch them so we need to mark the test as legacy.
+ *
+ * @group legacy
+ */
+ public function testLintFileWithNotReportedDeprecation()
+ {
+ $tester = $this->createCommandTester();
+ $filename = $this->createFile('{{ foo|deprecated_filter }}');
+
+ $ret = $tester->execute(['filename' => [$filename]], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false]);
+
+ $this->assertEquals(0, $ret, 'Returns 0 in case of success');
+ $this->assertStringContainsString('OK in', trim($tester->getDisplay()));
+ }
+
+ public function testLintFileWithReportedDeprecation()
+ {
+ $tester = $this->createCommandTester();
+ $filename = $this->createFile('{{ foo|deprecated_filter }}');
+
+ $ret = $tester->execute(['filename' => [$filename], '--show-deprecations' => true], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false]);
+
+ $this->assertEquals(1, $ret, 'Returns 1 in case of error');
+ $this->assertRegExp('/ERROR in \S+ \(line 1\)/', trim($tester->getDisplay()));
+ $this->assertStringContainsString('Filter "deprecated_filter" is deprecated', trim($tester->getDisplay()));
+ }
+
+ /**
+ * @group tty
+ */
public function testLintDefaultPaths()
{
$tester = $this->createCommandTester();
@@ -77,7 +109,12 @@ public function testLintDefaultPaths()
private function createCommandTester(): CommandTester
{
- $command = new LintCommand(new Environment(new FilesystemLoader(\dirname(__DIR__).'/Fixtures/templates/')));
+ $environment = new Environment(new FilesystemLoader(\dirname(__DIR__).'/Fixtures/templates/'));
+ $environment->addFilter(new TwigFilter('deprecated_filter', function ($v) {
+ return $v;
+ }, ['deprecated' => true]));
+
+ $command = new LintCommand($environment);
$application = new Application();
$application->add($command);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php
index ad7194de97b0..f1f2bdc746e9 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Bundle\FrameworkBundle\Tests;
+namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\CustomPathBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php
index 2f090b2de8d5..c58b25066bf4 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Bundle\FrameworkBundle\Tests;
+namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\TestBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php
index f8dc8859efd4..34c6dc4297ee 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php
@@ -177,7 +177,7 @@ private function sanitizeLogs(array $logs)
continue;
}
- $message = $log['message'];
+ $message = '_'.$log['message'];
$exception = $log['context']['exception'];
if ($exception instanceof SilencedErrorContext) {
diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php
index adfba5d4220e..9c175397fced 100644
--- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php
@@ -176,13 +176,15 @@ public function getCollectTestData()
[
['message' => 'foo3', 'context' => ['exception' => new \ErrorException('warning', 0, E_USER_WARNING)], 'priority' => 100, 'priorityName' => 'DEBUG'],
['message' => 'foo3', 'context' => ['exception' => new SilencedErrorContext(E_USER_WARNING, __FILE__, __LINE__)], 'priority' => 100, 'priorityName' => 'DEBUG'],
+ ['message' => '0', 'context' => ['exception' => new SilencedErrorContext(E_USER_WARNING, __FILE__, __LINE__)], 'priority' => 100, 'priorityName' => 'DEBUG'],
],
[
['message' => 'foo3', 'context' => ['exception' => ['warning', E_USER_WARNING]], 'priority' => 100, 'priorityName' => 'DEBUG'],
['message' => 'foo3', 'context' => ['exception' => [E_USER_WARNING]], 'priority' => 100, 'priorityName' => 'DEBUG', 'errorCount' => 1, 'scream' => true],
+ ['message' => '0', 'context' => ['exception' => [E_USER_WARNING]], 'priority' => 100, 'priorityName' => 'DEBUG', 'errorCount' => 1, 'scream' => true],
],
0,
- 1,
+ 2,
];
}
}
diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Receiver/SingleMessageReceiverTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Receiver/SingleMessageReceiverTest.php
index e8f5ef4ce172..bfebfc5aa3f0 100644
--- a/src/Symfony/Component/Messenger/Tests/Transport/Receiver/SingleMessageReceiverTest.php
+++ b/src/Symfony/Component/Messenger/Tests/Transport/Receiver/SingleMessageReceiverTest.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Component\Messenger\Tests\Transport\Sender;
+namespace Symfony\Component\Messenger\Tests\Transport\Receiver;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Envelope;
diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportFactoryTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportFactoryTest.php
index 021c7ae9706a..63aa43fb7ca0 100644
--- a/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportFactoryTest.php
+++ b/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportFactoryTest.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Component\Messenger\Tests\Transport\AmqpExt;
+namespace Symfony\Component\Messenger\Tests\Transport\Sync;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\MessageBusInterface;
diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportTest.php
index 13549e27582a..8ea4eb2e0052 100644
--- a/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportTest.php
+++ b/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportTest.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Component\Messenger\Tests\Transport\AmqpExt;
+namespace Symfony\Component\Messenger\Tests\Transport\Sync;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Envelope;
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
index 3f48a1d828e1..0d3c32206786 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Component\PropertyInfo\Tests\PhpDocExtractor;
+namespace Symfony\Component\PropertyInfo\Tests\Extractor;
use phpDocumentor\Reflection\Types\Collection;
use PHPUnit\Framework\TestCase;
diff --git a/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php b/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php
index 7690b3e2264b..7a9c4d0cfb23 100644
--- a/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php
+++ b/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php
@@ -47,8 +47,10 @@ public function __construct(AccessDecisionManagerInterface $manager)
/**
* {@inheritdoc}
+ *
+ * @param bool $allowMultipleAttributes Whether to allow passing multiple values to the $attributes array
*/
- public function decide(TokenInterface $token, array $attributes, $object = null): bool
+ public function decide(TokenInterface $token, array $attributes, $object = null/*, bool $allowMultipleAttributes = false*/): bool
{
$currentDecisionLog = [
'attributes' => $attributes,
@@ -58,7 +60,7 @@ public function decide(TokenInterface $token, array $attributes, $object = null)
$this->currentLog[] = &$currentDecisionLog;
- $result = $this->manager->decide($token, $attributes, $object);
+ $result = $this->manager->decide($token, $attributes, $object, 3 < \func_num_args() && func_get_arg(3));
$currentDecisionLog['result'] = $result;
diff --git a/src/Symfony/Component/Validator/Context/ExecutionContext.php b/src/Symfony/Component/Validator/Context/ExecutionContext.php
index d7eb4785a927..74ed391034f1 100644
--- a/src/Symfony/Component/Validator/Context/ExecutionContext.php
+++ b/src/Symfony/Component/Validator/Context/ExecutionContext.php
@@ -20,6 +20,7 @@
use Symfony\Component\Validator\Mapping\MetadataInterface;
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
use Symfony\Component\Validator\Util\PropertyPath;
+use Symfony\Component\Validator\Validator\LazyProperty;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Validator\Violation\ConstraintViolationBuilder;
use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface;
@@ -180,7 +181,7 @@ public function addViolation(string $message, array $parameters = [])
$parameters,
$this->root,
$this->propertyPath,
- $this->value,
+ $this->getValue(),
null,
null,
$this->constraint
@@ -199,7 +200,7 @@ public function buildViolation(string $message, array $parameters = []): Constra
$parameters,
$this->root,
$this->propertyPath,
- $this->value,
+ $this->getValue(),
$this->translator,
$this->translationDomain
);
@@ -234,6 +235,10 @@ public function getRoot()
*/
public function getValue()
{
+ if ($this->value instanceof LazyProperty) {
+ return $this->value->getPropertyValue();
+ }
+
return $this->value;
}
diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/EntityWithGroupedConstraintOnMethods.php b/src/Symfony/Component/Validator/Tests/Fixtures/EntityWithGroupedConstraintOnMethods.php
new file mode 100644
index 000000000000..89cae29f050c
--- /dev/null
+++ b/src/Symfony/Component/Validator/Tests/Fixtures/EntityWithGroupedConstraintOnMethods.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Validator\Tests\Fixtures;
+
+class EntityWithGroupedConstraintOnMethods
+{
+ public $bar;
+
+ public function isValidInFoo()
+ {
+ return false;
+ }
+
+ public function getBar()
+ {
+ throw new \Exception('Should not be called');
+ }
+}
diff --git a/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php b/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php
index 954a62cb68e2..3d41539de2e6 100644
--- a/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php
+++ b/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php
@@ -14,14 +14,19 @@
use Symfony\Component\Translation\IdentityTranslator;
use Symfony\Component\Validator\Constraints\All;
use Symfony\Component\Validator\Constraints\Collection;
+use Symfony\Component\Validator\Constraints\GroupSequence;
+use Symfony\Component\Validator\Constraints\IsTrue;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
+use Symfony\Component\Validator\Constraints\NotNull;
use Symfony\Component\Validator\ConstraintValidatorFactory;
use Symfony\Component\Validator\Context\ExecutionContextFactory;
+use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
use Symfony\Component\Validator\Tests\Constraints\Fixtures\ChildA;
use Symfony\Component\Validator\Tests\Constraints\Fixtures\ChildB;
use Symfony\Component\Validator\Tests\Fixtures\Entity;
+use Symfony\Component\Validator\Tests\Fixtures\EntityWithGroupedConstraintOnMethods;
use Symfony\Component\Validator\Validator\RecursiveValidator;
use Symfony\Component\Validator\Validator\ValidatorInterface;
@@ -118,6 +123,25 @@ public function testCollectionConstraintValidateAllGroupsForNestedConstraints()
$this->assertInstanceOf(NotBlank::class, $violations->get(1)->getConstraint());
}
+ public function testGroupedMethodConstraintValidateInSequence()
+ {
+ $metadata = new ClassMetadata(EntityWithGroupedConstraintOnMethods::class);
+ $metadata->addPropertyConstraint('bar', new NotNull(['groups' => 'Foo']));
+ $metadata->addGetterMethodConstraint('validInFoo', 'isValidInFoo', new IsTrue(['groups' => 'Foo']));
+ $metadata->addGetterMethodConstraint('bar', 'getBar', new NotNull(['groups' => 'Bar']));
+
+ $this->metadataFactory->addMetadata($metadata);
+
+ $entity = new EntityWithGroupedConstraintOnMethods();
+ $groups = new GroupSequence(['EntityWithGroupedConstraintOnMethods', 'Foo', 'Bar']);
+
+ $violations = $this->validator->validate($entity, null, $groups);
+
+ $this->assertCount(2, $violations);
+ $this->assertInstanceOf(NotNull::class, $violations->get(0)->getConstraint());
+ $this->assertInstanceOf(IsTrue::class, $violations->get(1)->getConstraint());
+ }
+
public function testAllConstraintValidateAllGroupsForNestedConstraints()
{
$this->metadata->addPropertyConstraint('data', new All(['constraints' => [
diff --git a/src/Symfony/Component/Validator/Validator/LazyProperty.php b/src/Symfony/Component/Validator/Validator/LazyProperty.php
new file mode 100644
index 000000000000..a0799963c150
--- /dev/null
+++ b/src/Symfony/Component/Validator/Validator/LazyProperty.php
@@ -0,0 +1,32 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Validator\Validator;
+
+/**
+ * A wrapper for a callable initializing a property from a getter.
+ *
+ * @internal
+ */
+class LazyProperty
+{
+ private $propertyValueCallback;
+
+ public function __construct(\Closure $propertyValueCallback)
+ {
+ $this->propertyValueCallback = $propertyValueCallback;
+ }
+
+ public function getPropertyValue()
+ {
+ return \call_user_func($this->propertyValueCallback);
+ }
+}
diff --git a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php
index 030b828e84d9..de724e8480ed 100644
--- a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php
+++ b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php
@@ -28,6 +28,7 @@
use Symfony\Component\Validator\Mapping\ClassMetadataInterface;
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
use Symfony\Component\Validator\Mapping\GenericMetadata;
+use Symfony\Component\Validator\Mapping\GetterMetadata;
use Symfony\Component\Validator\Mapping\MetadataInterface;
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
use Symfony\Component\Validator\Mapping\TraversalStrategy;
@@ -502,7 +503,13 @@ private function validateClassNode(object $object, ?string $cacheKey, ClassMetad
throw new UnsupportedMetadataException(sprintf('The property metadata instances should implement "Symfony\Component\Validator\Mapping\PropertyMetadataInterface", got: "%s".', \is_object($propertyMetadata) ? \get_class($propertyMetadata) : \gettype($propertyMetadata)));
}
- $propertyValue = $propertyMetadata->getPropertyValue($object);
+ if ($propertyMetadata instanceof GetterMetadata) {
+ $propertyValue = new LazyProperty(static function () use ($propertyMetadata, $object) {
+ return $propertyMetadata->getPropertyValue($object);
+ });
+ } else {
+ $propertyValue = $propertyMetadata->getPropertyValue($object);
+ }
$this->validateGenericNode(
$propertyValue,
@@ -729,6 +736,10 @@ private function validateInGroup($value, ?string $cacheKey, MetadataInterface $m
$validator = $this->validatorFactory->getInstance($constraint);
$validator->initialize($context);
+ if ($value instanceof LazyProperty) {
+ $value = $value->getPropertyValue();
+ }
+
try {
$validator->validate($value, $constraint);
} catch (UnexpectedValueException $e) {