From dcb7b0b978e7c0aeced7fb28bbc011d9ad827c83 Mon Sep 17 00:00:00 2001 From: Maarten Visscher Date: Fri, 19 Jul 2024 17:05:19 +0200 Subject: [PATCH] Add maximum depth to serializer to prevent infinite recursion (#167) --- src/DataCollector/MongoQuerySerializer.php | 17 +++++++++----- .../MongoQuerySerializerTest.php | 23 +++++++++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/DataCollector/MongoQuerySerializer.php b/src/DataCollector/MongoQuerySerializer.php index 01096a2..0c5ec92 100644 --- a/src/DataCollector/MongoQuerySerializer.php +++ b/src/DataCollector/MongoQuerySerializer.php @@ -24,7 +24,7 @@ public static function serialize(Query $query): void * * @return mixed[] */ - private static function prepareUnserializableData($data): array + private static function prepareUnserializableData($data, int $depth = 0): array { if ($data instanceof Serializable) { $data = $data->bsonSerialize(); @@ -32,7 +32,7 @@ private static function prepareUnserializableData($data): array $newData = []; foreach ($data as $key => $item) { - $newData[$key] = self::prepareItemData($item); + $newData[$key] = self::prepareItemData($item, $depth + 1); } return $newData; @@ -43,19 +43,24 @@ private static function prepareUnserializableData($data): array * * @return mixed */ - public static function prepareItemData($item) + public static function prepareItemData($item, int $depth = 0) { + // Prevent infinite recursion + if ($depth > 1_000) { + return null; + } + if (\is_scalar($item)) { return $item; } if (\is_array($item)) { - return self::prepareUnserializableData($item); + return self::prepareUnserializableData($item, $depth); } if (\is_object($item)) { if (method_exists($item, 'getArrayCopy')) { - return self::prepareUnserializableData($item->getArrayCopy()); + return self::prepareUnserializableData($item->getArrayCopy(), $depth); } if (method_exists($item, 'toDateTime')) { @@ -70,7 +75,7 @@ public static function prepareItemData($item) return $item->bsonSerialize(); } - return self::prepareUnserializableData((array) $item); + return self::prepareUnserializableData((array) $item, $depth); } return $item; diff --git a/tests/Unit/DataCollector/MongoQuerySerializerTest.php b/tests/Unit/DataCollector/MongoQuerySerializerTest.php index b283c0c..848202d 100644 --- a/tests/Unit/DataCollector/MongoQuerySerializerTest.php +++ b/tests/Unit/DataCollector/MongoQuerySerializerTest.php @@ -52,6 +52,29 @@ public function unserializedDataProvider(): array ]; } + public function test_serializer_terminating(): void + { + // tests that the serializer terminates when serializing an object which references itself + $selfReferencingObject = new class () { + public $self; + + public function __construct( + ) { + $this->self = $this; + } + }; + $data = ['test' => $selfReferencingObject]; + + $query = new Query(); + $query->setFilters($data); + $query->setData($data); + $query->setOptions($data); + + MongoQuerySerializer::serialize($query); + + $this->expectNotToPerformAssertions(); + } + public function test_serializer_regression_with_replaceOne(): void { $stdClass = new \stdClass();