Merge branch '5.1' into 5.2

* 5.1:
  Dont allow unserializing classes with a destructor
  Dont allow unserializing classes with a destructor - 4.4
  [Cache] fix possible collision when writing tmp file in filesystem adapter
  a colon followed by spaces exclusively separates mapping keys and values
  Contracts: Remove ellipsis
  fix handling float-like key attribute values
  Fix missing BCC recipients in SES bridge
  Dont allow unserializing classes with a destructor - 5.1
This commit is contained in:
Nicolas Grekas 2021-01-12 15:28:55 +01:00
commit 2052534540
32 changed files with 294 additions and 7 deletions

View File

@ -129,6 +129,16 @@ class ElasticsearchLogstashHandler extends AbstractHandler
$this->wait(false);
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
$this->wait(true);

View File

@ -89,6 +89,12 @@ class AppKernel extends Kernel implements ExtensionInterface, ConfigurationInter
public function __wakeup()
{
foreach ($this as $k => $v) {
if (\is_object($v)) {
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
}
$this->__construct($this->varDir, $this->testCase, $this->rootConfig, $this->environment, $this->debug);
}

View File

@ -93,9 +93,20 @@ trait FilesystemCommonTrait
set_error_handler(__CLASS__.'::throwError');
try {
if (null === $this->tmp) {
$this->tmp = $this->directory.uniqid('', true);
$this->tmp = $this->directory.bin2hex(random_bytes(6));
}
file_put_contents($this->tmp, $data);
try {
$h = fopen($this->tmp, 'x');
} catch (\ErrorException $e) {
if (false === strpos($e->getMessage(), 'File exists')) {
throw $e;
}
$this->tmp = $this->directory.bin2hex(random_bytes(6));
$h = fopen($this->tmp, 'x');
}
fwrite($h, $data);
fclose($h);
if (null !== $expiresAt) {
touch($this->tmp, $expiresAt);

View File

@ -227,6 +227,10 @@ class PrototypedArrayNode extends ArrayNode
} elseif (isset($v[$this->keyAttribute])) {
$k = $v[$this->keyAttribute];
if (\is_float($k)) {
$k = var_export($k, true);
}
// remove the key attribute when required
if ($this->removeKeyAttribute) {
unset($v[$this->keyAttribute]);

View File

@ -201,6 +201,32 @@ class NormalizationTest extends TestCase
$this->assertNormalized($tree, $data, $data);
}
public function testFloatLikeValueAsMapKeyAttribute()
{
$tree = (new TreeBuilder('root'))
->getRootNode()
->useAttributeAsKey('number')
->arrayPrototype()
->children()
->scalarNode('foo')->end()
->end()
->end()
->end()
->buildTree()
;
$this->assertNormalized($tree, [
[
'number' => 3.0,
'foo' => 'bar',
],
], [
'3.0' => [
'foo' => 'bar',
],
]);
}
public static function assertNormalized(NodeInterface $tree, $denormalized, $normalized)
{
self::assertSame($normalized, $tree->normalize($denormalized));

View File

@ -40,6 +40,16 @@ abstract class AbstractConfigurator
throw new \BadMethodCallException(sprintf('Call to undefined method "%s::%s()".', static::class, $method));
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
/**
* Checks that a value is valid, optionally replacing Definition and Reference configurators by their configure value.
*

View File

@ -35,6 +35,16 @@ class BufferingLogger extends AbstractLogger
return $logs;
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
foreach ($this->logs as [$level, $message, $context]) {

View File

@ -76,6 +76,16 @@ class OrderedHashMapIterator implements \Iterator
$this->managedCursors[$this->cursorId] = &$this->cursor;
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
/**
* Removes the iterator's cursors from the managed cursors of the
* corresponding {@link OrderedHashMap} instance.

View File

@ -120,6 +120,16 @@ class ErrorChunk implements ChunkInterface
return $this->didThrow;
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
if (!$this->didThrow) {

View File

@ -367,6 +367,16 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface,
}
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
$this->reset();

View File

@ -218,6 +218,16 @@ final class HttplugClient implements HttplugInterface, HttpAsyncClient, RequestF
throw new \LogicException(sprintf('You cannot use "%s()" as the "nyholm/psr7" package is not installed. Try running "composer require nyholm/psr7".', __METHOD__));
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
$this->wait();

View File

@ -142,6 +142,16 @@ final class AmpResponse implements ResponseInterface, StreamableInterface
return null !== $type ? $this->info[$type] ?? null : $this->info;
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
try {

View File

@ -179,7 +179,7 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
$fileLinkFormat = array_pop($this->data);
$this->dataCount = \count($this->data);
self::__construct($this->stopwatch, $fileLinkFormat, $charset);
self::__construct($this->stopwatch, \is_string($fileLinkFormat) || $fileLinkFormat instanceof FileLinkFormatter ? $fileLinkFormat : null, \is_string($charset) ? $charset : null);
}
public function getDumpsCount(): int

View File

@ -865,6 +865,10 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
public function __wakeup()
{
if (\is_object($this->environment) || \is_object($this->debug)) {
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
$this->__construct($this->environment, $this->debug);
}
}

View File

@ -35,6 +35,16 @@ class Connection extends AbstractConnection
/** @var resource */
private $connection;
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
$this->disconnect();

View File

@ -38,6 +38,16 @@ class Query extends AbstractQuery
parent::__construct($connection, $dn, $query, $options);
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
$con = $this->connection->getResource();

View File

@ -49,6 +49,16 @@ final class Lock implements SharedLockInterface, LoggerAwareInterface
$this->logger = new NullLogger();
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
/**
* Automatically releases the underlying lock when the object is destructed.
*/

View File

@ -63,6 +63,12 @@ class SesHttpTransportTest extends TestCase
$this->assertStringContainsString('AWS3-HTTPS AWSAccessKeyId=ACCESS_KEY,Algorithm=HmacSHA256,Signature=', $options['headers'][0] ?? $options['request_headers'][0]);
parse_str($options['body'], $body);
$this->assertArrayHasKey('Destinations_member_1', $body);
$this->assertSame('saif.gmati@symfony.com', $body['Destinations_member_1']);
$this->assertArrayHasKey('Destinations_member_2', $body);
$this->assertSame('jeremy@derusse.com', $body['Destinations_member_2']);
$content = base64_decode($body['RawMessage_Data']);
$this->assertStringContainsString('Hello!', $content);
@ -88,6 +94,7 @@ class SesHttpTransportTest extends TestCase
$mail = new Email();
$mail->subject('Hello!')
->to(new Address('saif.gmati@symfony.com', 'Saif Eddin'))
->bcc(new Address('jeremy@derusse.com', 'Jérémy Derussé'))
->from(new Address('fabpot@symfony.com', 'Fabien'))
->text('Hello There!');

View File

@ -66,8 +66,14 @@ class SesHttpTransport extends AbstractHttpTransport
],
];
if (($message->getOriginalMessage() instanceof Message)
&& $configurationSetHeader = $message->getOriginalMessage()->getHeaders()->get('X-SES-CONFIGURATION-SET')) {
$index = 1;
foreach ($message->getEnvelope()->getRecipients() as $recipient) {
$request['body']['Destinations.member.'.$index++] = $recipient->getAddress();
}
if ($message->getOriginalMessage() instanceof Message
&& $configurationSetHeader = $message->getOriginalMessage()->getHeaders()->get('X-SES-CONFIGURATION-SET')
) {
$request['body']['ConfigurationSetName'] = $configurationSetHeader->getBodyAsString();
}

View File

@ -340,6 +340,16 @@ class SmtpTransport extends AbstractTransport
$this->restartCounter = 0;
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
$this->stop();

View File

@ -64,6 +64,16 @@ class Connection
$this->queueUrl = $queueUrl;
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
$this->reset();

View File

@ -155,7 +155,13 @@ class DataPart extends TextPart
$r->setValue($this, $this->_headers);
unset($this->_headers);
if (!\is_array($this->_parent)) {
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
foreach (['body', 'charset', 'subtype', 'disposition', 'name', 'encoding'] as $name) {
if (null !== $this->_parent[$name] && !\is_string($this->_parent[$name])) {
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
$r = new \ReflectionProperty(TextPart::class, $name);
$r->setAccessible(true);
$r->setValue($this, $this->_parent[$name]);

View File

@ -35,6 +35,16 @@ class UnixPipes extends AbstractPipes
parent::__construct($input);
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
$this->close();

View File

@ -88,6 +88,16 @@ class WindowsPipes extends AbstractPipes
parent::__construct($input);
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
$this->close();

View File

@ -195,6 +195,16 @@ class Process implements \IteratorAggregate
return $process;
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
if ($this->options['create_new_console'] ?? false) {

View File

@ -38,6 +38,16 @@ class CollectionConfigurator
$this->parentPrefixes = $parentPrefixes;
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
if (null === $this->prefixes) {

View File

@ -30,6 +30,16 @@ class ImportConfigurator
$this->route = $route;
}
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
$this->parent->addCollection($this->route);

View File

@ -359,6 +359,10 @@ class UnicodeString extends AbstractUnicodeString
public function __wakeup()
{
if (!\is_string($this->string)) {
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
normalizer_is_normalized($this->string) ?: $this->string = normalizer_normalize($this->string);
}

View File

@ -203,7 +203,7 @@ class Parser
array_pop($this->refsBeingParsed);
}
} elseif (
self::preg_match('#^(?P<key>(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?[^ \'"\[\{!].*?)) *\:(\s++(?P<value>.+))?$#u', rtrim($this->currentLine), $values)
self::preg_match('#^(?P<key>(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?[^ \'"\[\{!].*?)) *\:( ++(?P<value>.+))?$#u', rtrim($this->currentLine), $values)
&& (false === strpos($values['key'], ' #') || \in_array($values['key'][0], ['"', "'"]))
) {
if ($context && 'sequence' == $context) {

View File

@ -915,4 +915,21 @@ class InlineTest extends TestCase
[['!'], '! ["!"]'],
];
}
/**
* @dataProvider ideographicSpaceProvider
*/
public function testParseIdeographicSpace(string $yaml, string $expected)
{
$this->assertSame($expected, Inline::parse($yaml));
}
public function ideographicSpaceProvider(): array
{
return [
["\u{3000}", ' '],
["'\u{3000}'", ' '],
["'a b'", 'a b'],
];
}
}

View File

@ -2732,6 +2732,22 @@ YAML;
// (before, there was no \n after row2)
$this->assertSame(['a' => ['b' => "row\nrow2\n"], 'c' => 'd'], $this->parser->parse($longDocument));
}
public function testParseIdeographicSpaces()
{
$expected = <<<YAML
unquoted: \u{3000}
quoted: '\u{3000}'
within_string: 'a b'
regular_space: 'a b'
YAML;
$this->assertSame([
'unquoted' => ' ',
'quoted' => ' ',
'within_string' => 'a b',
'regular_space' => 'a b',
], $this->parser->parse($expected));
}
}
class B

View File

@ -11,7 +11,7 @@ Design Principles
* contracts are split by domain, each into their own sub-namespaces;
* contracts are small and consistent sets of PHP interfaces, traits, normative
docblocks and reference test suites when applicable, ...;
docblocks and reference test suites when applicable;
* all contracts must have a proven implementation to enter this repository;
* they must be backward compatible with existing Symfony components.