JSON:API: request documents with no `data`, no `type` or a wrong `type` are accepted with 201
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 62/100
Research direction
Start in src/JsonApi/Serializer, where JsonApi\Serializer\ItemNormalizerTrait::denormalize() reads $data['data']['attributes'] ?? [] and $data['data']['relationships'] ?? [] without checking the document shape. Reproduce with the request bodies in the table (for example "Y", {"title":"A"} and a mismatched type) against a POST /novels operation. Done means malformed documents return 400 Bad Request, and a POST whose type does not match the endpoint returns 409 Conflict, with the existing JSON:API tests still passing.
Written by the indexing model from the issue text.
Description
API Platform version(s) affected
4.4.4
Description
The JSON:API denormalizer does not check the structure of the request document. A body that is not a valid JSON:API document is accepted, and the resource is created with its default values.
The spec requires:
- the top level of a request document is an object with a
datamember (document structure), datais a resource object with atypemember, andattributesis an object (resource objects),- "A server MUST return
409 Conflictwhen processing aPOSTrequest in which the resource object'stypeis not among the type(s) that constitute the collection represented by the endpoint" (creating resources).
JsonApi\Serializer\ItemNormalizerTrait::denormalize() reads $data['data']['attributes'] ?? [] and $data['data']['relationships'] ?? [], and it does not check the other members.
How to reproduce
#[ApiResource(operations: [
new Get(provider: [self::class, 'provide']),
new Post(processor: [self::class, 'process']),
])]
final class Novel
{
public int $id = 1;
public string $title = 'Dune';
public static function provide(): self { return new self(); }
public static function process(self $data, Operation $operation): self { return $data; }
}
Each of these POST /novels bodies answers 201 Created:
| Body | Problem |
|---|---|
"Y" |
not an object |
42 |
not an object |
{"title":"A"} |
no data member |
{"data":"x"} |
data is not a resource object |
{"data":{"type":"Novel","attributes":["x"]}} |
attributes is not an object |
{"data":{"attributes":{"title":"A"}}} |
no type member |
{"data":{"type":"Author","attributes":{"title":"A"}}} |
type does not match the endpoint |
Response for "Y":
{"data":{"id":"/novels/1","type":"Novel","attributes":{"_id":1,"title":"Dune"}}}
A validation constraint on a required property hides the problem in some cases, but a resource with optional properties or defaults is created from any body.
Expected:
- A body that is not a JSON:API document, or has no
dataobject, notypeor a non-objectattributes/relationships:400 Bad Request. - A
typethat does not match the resource onPOST:409 Conflict.
Additional Context
Checked on api-platform/core v4.4.4, Symfony 7.4, PHP 8.5, in an empty Symfony app. The related code in src/JsonApi/Serializer is the same on main, except for the error status cast (#8366).
The app has only FrameworkBundle, TwigBundle and ApiPlatformBundle, with this config:
$c->extension('api_platform', [
'mapping' => ['paths' => ['%kernel.project_dir%/src/ApiResource']],
'formats' => ['jsonapi' => ['application/vnd.api+json']],
'error_formats' => ['jsonapi' => ['application/vnd.api+json'], 'jsonproblem' => ['application/problem+json']],
'doctrine' => ['enabled' => false],
]);
All requests send Accept: application/vnd.api+json and, when they have a body, Content-Type: application/vnd.api+json. The responses are from the prod environment.
- Dominant language
- PHP
- Stars
- 2.6k
- Forks
- 989
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 100
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from api-platform/core
-
JSON:API: resource schemas (`*.jsonapi`) have `properties` but no `type: object`Possibly taken @ousamabenyounes claimed this 2 days ago. Open
Difficulty 2/5 Under an hour Newbie friendliness 80/100
api-platform/core#8701 ·
Maintainers usually reply within 1 day
-
JSON:API: `JsonApiCollectionBaseSchemaNoPagination` declares `data` as `type: array` without `items`Open
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
api-platform/core#8699 ·
Maintainers usually reply within 1 day
-
[Json-Schema] Nested resources lost externalDoc and description dataPossibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 3/5 1-2 days Newbie friendliness 68/100
api-platform/core#8721 ·
Maintainers usually reply within 1 day
-
Difficulty 3/5 1-2 days Newbie friendliness 50/100
api-platform/core#8704 ·
Maintainers usually reply within 1 day
-
Difficulty 3/5 1-2 days Newbie friendliness 68/100
api-platform/core#8702 ·
Maintainers usually reply within 1 day
All issues in api-platform/core
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
neos/flow-development-collection#3639 · 1 comment ·
Maintainers usually reply within 1 day
-
The PHP version check in prepare.php still allows PHP 7.2 and 7.3Possibly taken @ekamran claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
WordPress/phpunit-test-runner#359 ·
Maintainers usually reply within 2 days
-
ObjectMapper
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
symfony/symfony-docs#23390 ·
Maintainers usually reply within 2 days
-
sync-en
Difficulty 1/5 1-3 hours Newbie friendliness 84/100
Maintainers usually reply within 1 day
-
sync-en
Difficulty 1/5 Under an hour Newbie friendliness 83/100
Maintainers usually reply within 3 days