diff --git a/docs/book/adapter.md b/docs/book/adapter.md index 82e7146..dbc5c47 100644 --- a/docs/book/adapter.md +++ b/docs/book/adapter.md @@ -16,7 +16,12 @@ The `Zend\Serializer\Adapter\PhpSerialize` adapter uses the built-in [serialize()](http://php.net/serialize)/[unserialize()](http://php.net/unserialize) functions, and is a good default adapter choice. -There are no configurable options for this adapter. +Available options include: + +Option | Data Type | Default Value | Description +--------------------------- | ----------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- +unserialize_class_whitelist | `array` or `bool` | `true` | The allowed classes for unserialize(), see [unserialize()](http://php.net/unserialize) for more information. Only available on PHP 7.0 or higher. + ## The IgBinary Adapter diff --git a/src/Adapter/PhpSerialize.php b/src/Adapter/PhpSerialize.php index 14ecf04..581e21e 100644 --- a/src/Adapter/PhpSerialize.php +++ b/src/Adapter/PhpSerialize.php @@ -3,12 +3,13 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2018 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ namespace Zend\Serializer\Adapter; +use Traversable; use Zend\Serializer\Exception; use Zend\Stdlib\ErrorHandler; @@ -21,8 +22,15 @@ class PhpSerialize extends AbstractAdapter */ private static $serializedFalse = null; + /** + * @var PhpSerializeOptions + */ + protected $options; + /** * Constructor + * + * @param array|Traversable|PhpSerializeOptions|null $options */ public function __construct($options = null) { @@ -35,6 +43,36 @@ public function __construct($options = null) parent::__construct($options); } + /** + * Set options + * + * @param array|Traversable|PhpSerializeOptions $options + * @return PhpSerialize + */ + public function setOptions($options) + { + if (! $options instanceof PhpSerializeOptions) { + $options = new PhpSerializeOptions($options); + } + + $this->options = $options; + return $this; + } + + /** + * Get options + * + * @return PhpSerializeOptions + */ + public function getOptions() + { + if ($this->options === null) { + $this->options = new PhpSerializeOptions(); + } + + return $this->options; + } + /** * Serialize using serialize() * @@ -85,7 +123,14 @@ public function unserialize($serialized) } ErrorHandler::start(E_NOTICE); - $ret = unserialize($serialized); + + if (PHP_MAJOR_VERSION >= 7) { + // the second parameter is only available on PHP 7.0 or higher + $ret = unserialize($serialized, ['allowed_classes' => $this->getOptions()->getUnserializeClassWhitelist()]); + } else { + $ret = unserialize($serialized); + } + $err = ErrorHandler::stop(); if ($ret === false) { throw new Exception\RuntimeException('Unserialization failed', 0, $err); diff --git a/src/Adapter/PhpSerializeOptions.php b/src/Adapter/PhpSerializeOptions.php new file mode 100644 index 0000000..1d0cce8 --- /dev/null +++ b/src/Adapter/PhpSerializeOptions.php @@ -0,0 +1,52 @@ +unserializeClassWhitelist = $unserializeClassWhitelist; + return $this; + } + + /** + * @return string[]|bool + */ + public function getUnserializeClassWhitelist() + { + return $this->unserializeClassWhitelist; + } +} diff --git a/test/Adapter/PhpSerializeTest.php b/test/Adapter/PhpSerializeTest.php index e992a6e..d9ac4d0 100644 --- a/test/Adapter/PhpSerializeTest.php +++ b/test/Adapter/PhpSerializeTest.php @@ -11,10 +11,11 @@ use PHPUnit\Framework\TestCase; use Zend\Serializer; +use Zend\Serializer\Exception\InvalidArgumentException; /** - * @group Zend_Serializer - * @covers Zend\Serializer\Adapter\PhpSerialize + * @group Zend_Serializer + * @covers \Zend\Serializer\Adapter\PhpSerialize */ class PhpSerializeTest extends TestCase { @@ -165,4 +166,66 @@ public function testUnserializingInvalidStringRaisesException($string, $expected $this->expectExceptionMessage($expected); $this->adapter->unserialize($string); } + + public function testUnserializeNoWhitelistedClasses() + { + $value = 'O:8:"stdClass":0:{}'; + + if (PHP_MAJOR_VERSION >= 7) { + $this->adapter->getOptions()->setUnserializeClassWhitelist(false); + + $data = $this->adapter->unserialize($value); + + $this->assertNotInstanceOf(\stdClass::class, $data); + $this->assertInstanceOf('__PHP_Incomplete_Class', $data); + } else { + // In PHP < 7.0 the options-class will throw an exception + + self::expectException(InvalidArgumentException::class); + self::expectExceptionMessage('Class whitelist for unserialize() is only available on PHP 7.0 or higher.'); + + $this->adapter->getOptions()->setUnserializeClassWhitelist(false); + } + } + + public function testUnserializeClassNotAllowed() + { + $value = 'O:8:"stdClass":0:{}'; + + if (PHP_MAJOR_VERSION >= 7) { + $this->adapter->getOptions()->setUnserializeClassWhitelist([\My\Dummy::class]); + + $data = $this->adapter->unserialize($value); + + $this->assertNotInstanceOf(\stdClass::class, $data); + $this->assertInstanceOf('__PHP_Incomplete_Class', $data); + } else { + // In PHP < 7.0 the options-class will throw an exception + + self::expectException(InvalidArgumentException::class); + self::expectExceptionMessage('Class whitelist for unserialize() is only available on PHP 7.0 or higher.'); + + $this->adapter->getOptions()->setUnserializeClassWhitelist(false); + } + } + + public function testUnserializeClassAllowed() + { + $value = 'O:8:"stdClass":0:{}'; + + if (PHP_MAJOR_VERSION >= 7) { + $this->adapter->getOptions()->setUnserializeClassWhitelist([\stdClass::class]); + + $data = $this->adapter->unserialize($value); + $this->assertInstanceOf(\stdClass::class, $data); + $this->assertNotInstanceOf('__PHP_Incomplete_Class', $data); + } else { + // In PHP < 7.0 the options-class will throw an exception + + self::expectException(InvalidArgumentException::class); + self::expectExceptionMessage('Class whitelist for unserialize() is only available on PHP 7.0 or higher.'); + + $this->adapter->getOptions()->setUnserializeClassWhitelist(false); + } + } }