Skip to content

[Serializer] Handle invalid mapping type property type #60296

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

Open
wants to merge 2 commits into
base: 7.2
Choose a base branch
from

Conversation

KorvinSzanto
Copy link
Contributor

Q A
Branch? 7.2
Bug fix? yes
New feature? no
Deprecations? no
Issues
License MIT

When using #[MapRequestPayload] along with a type that uses a #[DescriminatorMap] it's possible for a user to craft a payload that triggers a TypeError by passing the wrong type for the "type" property.

For example, a class that has:

#[DiscriminatorMap('field', ['a' => AController::class, 'b' => BController::class])]

and a request comes in with:

Content-Type: application/json

{"field":{}}

will trigger a 500 because AbstractObjectNormalizer doesn't validate the field type before passing it to ->getClassForType which typehints for string.

This PR adds a conditional that filters anything other than strings or objects that have a __toString method.

@carsonbot

This comment was marked as outdated.

@KorvinSzanto KorvinSzanto changed the title Handle invalid mapping type property type Apr 28, 2025
@KorvinSzanto KorvinSzanto changed the base branch from 7.3 to 7.2 April 28, 2025 21:59
@KorvinSzanto KorvinSzanto changed the base branch from 7.2 to 7.3 April 28, 2025 21:59
@KorvinSzanto KorvinSzanto force-pushed the handle-invalid-type-type branch from 355794e to ebad77f Compare April 28, 2025 22:04
@KorvinSzanto KorvinSzanto changed the base branch from 7.3 to 7.2 April 28, 2025 22:04
@lyrixx lyrixx removed their request for review April 29, 2025 10:27
Copy link
Member

@lyrixx lyrixx left a comment

Choose a reason for hiding this comment

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

Could you add a test case for this?

@mtarld mtarld modified the milestones: 7.3, 7.2 Apr 29, 2025
$normalizedData = $normalizer->denormalize(['foo' => 'foo', 'baz' => 'baz', 'quux' => ['value' => 'quux'], 'type' => $typeValue], AbstractDummy::class);

if ($shouldFail) {
$this->fail('Expected exception not thrown.');
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not needed because of the exceptException above

};

$discriminatorResolver = new ClassDiscriminatorFromClassMetadata($loaderMock);
$normalizer = new AbstractObjectNormalizerDummy($factory, null, new PhpDocExtractor(), $discriminatorResolver);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure you need the PhpDocExtractor here

$discriminatorResolver = new ClassDiscriminatorFromClassMetadata($loaderMock);
$normalizer = new AbstractObjectNormalizerDummy($factory, null, new PhpDocExtractor(), $discriminatorResolver);
$serializer = new Serializer([$normalizer]);
$normalizer->setSerializer($serializer);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this needed?


return [
[[], true],
[new StdClass(), true],
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
[new StdClass(), true],
[new \stdClass(), true],
$this->assertInstanceOf(DummyFirstChildQuux::class, $normalizedData->quux);
}

public function provideInvalidDiscriminatorTypes()
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
public function provideInvalidDiscriminatorTypes()
/**
* @return iterable<array{0: mixed, 1: bool}>
*/
public function provideInvalidDiscriminatorTypes(): iterable
@@ -1159,6 +1159,10 @@ private function getMappedClass(array $data, string $class, array $context): str
throw NotNormalizableValueException::createForUnexpectedDataType(\sprintf('Type property "%s" not found for the abstract object "%s".', $mapping->getTypeProperty(), $class), null, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(), false);
}

if (!is_string($type) && (!is_object($type) || !method_exists($type, '__toString'))) {
throw NotNormalizableValueException::createForUnexpectedDataType(\sprintf('The type property "%s" for the abstract object "%s" must be a string.', $mapping->getTypeProperty(), $class), $type, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(), false);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
throw NotNormalizableValueException::createForUnexpectedDataType(\sprintf('The type property "%s" for the abstract object "%s" must be a string.', $mapping->getTypeProperty(), $class), $type, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(), false);
throw NotNormalizableValueException::createForUnexpectedDataType(\sprintf('The type property "%s" for the abstract object "%s" must be a string or a stringable object.', $mapping->getTypeProperty(), $class), $type, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(), false);

maybe?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment