Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add maximum depth to serializer to prevent infinite recursion (#167) #171

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions src/DataCollector/MongoQuerySerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ 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();
}

$newData = [];
foreach ($data as $key => $item) {
$newData[$key] = self::prepareItemData($item);
$newData[$key] = self::prepareItemData($item, $depth + 1);
}

return $newData;
Expand All @@ -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);
}
Comment on lines +46 to 59

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we use a maxDepth approach?

Default it to 256 or so, and then decrease it at each iteration int $maxDepth = 256

To stop infinite recursion you should add

if ($depth < 0) {
     return null;
}

so that also negative $maxDepth are correctly managed.

Also, having maxDepth as a parameter cloud make it configurable in the future.


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')) {
Expand All @@ -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;
Expand Down
23 changes: 23 additions & 0 deletions tests/Unit/DataCollector/MongoQuerySerializerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down