diff --git a/config/settings.php b/config/settings.php index ee8cd09..c570b77 100644 --- a/config/settings.php +++ b/config/settings.php @@ -175,4 +175,23 @@ | */ '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, + \Illuminate\Support\Carbon::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]); } } 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,