From 2ef5a37fa0fd6d8e43467a2a2cf7feb391de3058 Mon Sep 17 00:00:00 2001 From: Randall Wilk Date: Tue, 14 Nov 2023 09:54:51 -0600 Subject: [PATCH 1/3] Add safelist for object unserialization --- config/settings.php | 18 ++++++++++++++++++ .../ValueSerializers/ValueSerializer.php | 5 ++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/config/settings.php b/config/settings.php index ee8cd09..de081f5 100644 --- a/config/settings.php +++ b/config/settings.php @@ -175,4 +175,22 @@ | */ 'cache_default_value' => true, + + /* + |-------------------------------------------------------------------------- + | Unserialize Safelist + |-------------------------------------------------------------------------- + | + | When using the default value serializer class from this package, we + | will only unserialize objects that have their classes whitelisted here. + | Any other objects will be unserialized to something like: + | __PHP_Incomplete_Class(App\Models\User) {...} + | + | To prevent any objects from being unserialized, simply set this to + | an empty array. + */ + 'unserialize_safelist' => [ + \Carbon\Carbon::class, + \Carbon\CarbonImmutable::class, + ], ]; diff --git a/src/Support/ValueSerializers/ValueSerializer.php b/src/Support/ValueSerializers/ValueSerializer.php index fa16db3..a6baffd 100644 --- a/src/Support/ValueSerializers/ValueSerializer.php +++ b/src/Support/ValueSerializers/ValueSerializer.php @@ -4,6 +4,7 @@ namespace Rawilk\Settings\Support\ValueSerializers; +use Illuminate\Support\Arr; use Rawilk\Settings\Contracts\ValueSerializer as ValueSerializerContract; class ValueSerializer implements ValueSerializerContract @@ -15,6 +16,8 @@ public function serialize($value): string public function unserialize(string $serialized): mixed { - return unserialize($serialized, ['allowed_classes' => false]); + $safelistedClasses = Arr::wrap(config('settings.unserialize_safelist', [])); + + return unserialize($serialized, ['allowed_classes' => $safelistedClasses]); } } From a28f62b98d894d0684360673435b4678c5201991 Mon Sep 17 00:00:00 2001 From: Randall Wilk Date: Tue, 14 Nov 2023 10:08:17 -0600 Subject: [PATCH 2/3] Add \Illuminate\Support\Carbon to unserialize safelist --- config/settings.php | 1 + 1 file changed, 1 insertion(+) diff --git a/config/settings.php b/config/settings.php index de081f5..c570b77 100644 --- a/config/settings.php +++ b/config/settings.php @@ -192,5 +192,6 @@ 'unserialize_safelist' => [ \Carbon\Carbon::class, \Carbon\CarbonImmutable::class, + \Illuminate\Support\Carbon::class, ], ]; From 8a632995eeeecf71204cacfae8403d8e4a76e994 Mon Sep 17 00:00:00 2001 From: Randall Wilk Date: Tue, 14 Nov 2023 10:12:45 -0600 Subject: [PATCH 3/3] Test unserialize safelist --- .../ValueSerializers/ValueSerializerTest.php | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/Unit/ValueSerializers/ValueSerializerTest.php b/tests/Unit/ValueSerializers/ValueSerializerTest.php index dfaad53..b9ab367 100644 --- a/tests/Unit/ValueSerializers/ValueSerializerTest.php +++ b/tests/Unit/ValueSerializers/ValueSerializerTest.php @@ -2,6 +2,8 @@ declare(strict_types=1); +use Carbon\Carbon; +use Illuminate\Support\Facades\Date; use Rawilk\Settings\Support\ValueSerializers\ValueSerializer; it('serializes values', function (mixed $value) { @@ -22,6 +24,38 @@ } })->with('values'); +test('certain objects can be safelisted for unserialization', function () { + config(['settings.unserialize_safelist' => [ + Carbon::class, + ]]); + + $serializer = new ValueSerializer; + + Date::setTestNow('2023-01-01 10:00:00'); + + $now = Carbon::now(); + + $serialized = serialize($now); + $unserialized = $serializer->unserialize($serialized); + + expect($unserialized)->toBeInstanceOf(Carbon::class) + ->and($unserialized->eq($now))->toBeTrue() + ->and($unserialized->toDateTimeString())->toBe('2023-01-01 10:00:00'); +}); + +test('objects not in the safelist will be unserialized to __PHP_Incomplete_Class', function () { + config(['settings.unserialize_safelist' => []]); + + $serializer = new ValueSerializer; + + $serialized = serialize(Carbon::now()); + $unserialized = $serializer->unserialize($serialized); + + expect($unserialized)->toBeObject() + ->and($unserialized)->not->toBeInstanceOf(Carbon::class) + ->and($unserialized)->toBeInstanceOf(__PHP_Incomplete_Class::class); +}); + dataset('values', [ null, 1,