From e22fded5fe85acc89da8da7aa2916e3eb2b62e9d Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Thu, 21 Oct 2021 17:29:05 +0100 Subject: [PATCH] MDL-70823 core: safer alternative for unserializing objects. --- lib/moodlelib.php | 15 +++++++++++++++ lib/tests/moodlelib_test.php | 23 +++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/lib/moodlelib.php b/lib/moodlelib.php index 809bd63ec72..8e2c53591f9 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -10404,6 +10404,21 @@ function unserialize_array($expression) { return $value; } +/** + * Safe method for unserializing given input that is expected to contain only a serialized instance of an stdClass object + * + * If any class type other than stdClass is included in the input string, it will not be instantiated and will be cast to an + * stdClass object. The initial cast to array, then back to object is to ensure we are always returning the correct type, + * otherwise we would return an instances of {@see __PHP_Incomplete_class} for malformed strings + * + * @param string $input + * @return stdClass + */ +function unserialize_object(string $input): stdClass { + $instance = (array) unserialize($input, ['allowed_classes' => [stdClass::class]]); + return (object) $instance; +} + /** * The lang_string class * diff --git a/lib/tests/moodlelib_test.php b/lib/tests/moodlelib_test.php index 650fd78eacb..e3b25cb8d46 100644 --- a/lib/tests/moodlelib_test.php +++ b/lib/tests/moodlelib_test.php @@ -4543,6 +4543,29 @@ EOF; $this->assertEquals($a, unserialize_array(serialize($a))); } + /** + * Test method for safely unserializing a serialized object of type stdClass + */ + public function test_unserialize_object(): void { + $object = (object) [ + 'foo' => 42, + 'bar' => 'Hamster', + 'innerobject' => (object) [ + 'baz' => 'happy', + ], + ]; + + // We should get back the same object we serialized. + $serializedobject = serialize($object); + $this->assertEquals($object, unserialize_object($serializedobject)); + + // Try serializing a different class, not allowed. + $langstr = new lang_string('no'); + $serializedlangstr = serialize($langstr); + $unserializedlangstr = unserialize_object($serializedlangstr); + $this->assertInstanceOf(stdClass::class, $unserializedlangstr); + } + /** * Test that the component_class_callback returns the correct default value when the class was not found. *