Fix input payload validation

This commit is contained in:
Jean-Sébastien Herbaux 2024-02-09 15:05:32 +01:00 committed by GitHub
parent 023e95b482
commit 90a86f595c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 104 additions and 17 deletions

View File

@ -0,0 +1,89 @@
'use strict';
const { createStrapiInstance } = require('api-tests/strapi');
const { createTestBuilder } = require('api-tests/builder');
const { createContentAPIRequest } = require('api-tests/request');
const builder = createTestBuilder();
let strapi;
let rq;
let data;
const productFixtures = [
{
name: 'foo',
description: 'first product',
},
{
name: 'bar',
description: 'second product',
},
];
const product = {
attributes: {
name: { type: 'string' },
description: { type: 'text' },
},
displayName: 'product',
singularName: 'product',
pluralName: 'products',
description: '',
collectionName: '',
};
describe('Validate Body', () => {
beforeAll(async () => {
await builder
.addContentType(product)
.addFixtures(product.singularName, productFixtures)
.build();
data = builder.fixturesFor(product.singularName);
strapi = await createStrapiInstance();
rq = await createContentAPIRequest({ strapi });
});
afterAll(async () => {
await strapi.destroy();
await builder.cleanup();
});
describe('Create', () => {
test('Cannot specify the ID during entity creation', async () => {
const createPayload = { data: { id: -1, name: 'baz', description: 'third product' } };
const response = await rq.post('/products', { body: createPayload });
expect(response.statusCode).toBe(200);
const { id, attributes } = response.body.data;
expect(id).not.toBe(createPayload.data.id);
expect(attributes).toHaveProperty('name', createPayload.data.name);
expect(attributes).toHaveProperty('description', createPayload.data.description);
});
});
describe('Update', () => {
test('ID cannot be updated, but allowed fields can', async () => {
const target = data[0];
const updatePayload = { data: { id: -1, name: 'baz' } };
const response = await rq.put(`/products/${target.id}`, {
body: updatePayload,
});
expect(response.statusCode).toBe(200);
const { id, attributes } = response.body.data;
expect(id).toBe(target.id);
expect(attributes).toHaveProperty('name', updatePayload.data.name);
expect(attributes).toHaveProperty('description', target.description);
});
});
});

View File

@ -241,12 +241,7 @@ export default ({ action, ability, model }: any) => {
const nonVisibleWritableAttributes = intersection(nonVisibleAttributes, writableAttributes);
return uniq([
...fields,
...STATIC_FIELDS,
...COMPONENT_FIELDS,
...nonVisibleWritableAttributes,
]);
return uniq([...fields, ...COMPONENT_FIELDS, ...nonVisibleWritableAttributes]);
};
const getOutputFields = (fields = []) => {

View File

@ -118,7 +118,7 @@ export default ({ action, ability, model }: any) => {
const wrapValidate = (createValidateFunction: any) => {
// TODO
// @ts-expect-error define the correct return type
const wrappedValidate = async (data, options = {}) => {
const wrappedValidate = async (data, options = {}): Promise<unknown> => {
if (isArray(data)) {
return Promise.all(data.map((entity: unknown) => wrappedValidate(entity, options)));
}
@ -187,12 +187,7 @@ export default ({ action, ability, model }: any) => {
const nonVisibleWritableAttributes = intersection(nonVisibleAttributes, writableAttributes);
return uniq([
...fields,
...STATIC_FIELDS,
...COMPONENT_FIELDS,
...nonVisibleWritableAttributes,
]);
return uniq([...fields, ...COMPONENT_FIELDS, ...nonVisibleWritableAttributes]);
};
const getQueryFields = (fields = []) => {

View File

@ -1,5 +1,5 @@
import { CurriedFunction1 } from 'lodash';
import { isArray, cloneDeep } from 'lodash/fp';
import { isArray, cloneDeep, omit } from 'lodash/fp';
import { getNonWritableAttributes } from '../content-types';
import { pipeAsync } from '../async';
@ -34,7 +34,9 @@ const createContentAPISanitizers = () => {
const nonWritableAttributes = getNonWritableAttributes(schema);
const transforms = [
// Remove non writable attributes
// Remove first level ID in inputs
omit('id'),
// Remove non-writable attributes
traverseEntity(visitors.removeRestrictedFields(nonWritableAttributes), { schema }),
];

View File

@ -1,8 +1,9 @@
import { CurriedFunction1 } from 'lodash';
import { isArray } from 'lodash/fp';
import { isArray, isObject } from 'lodash/fp';
import { getNonWritableAttributes } from '../content-types';
import { pipeAsync } from '../async';
import { throwInvalidParam } from './utils';
import * as visitors from './visitors';
import * as validators from './validators';
@ -37,7 +38,12 @@ const createContentAPIValidators = () => {
const nonWritableAttributes = getNonWritableAttributes(schema);
const transforms = [
// non writable attributes
(data: unknown) => {
if (isObject(data) && 'id' in data) {
throwInvalidParam({ key: 'id' });
}
},
// non-writable attributes
traverseEntity(visitors.throwRestrictedFields(nonWritableAttributes), { schema }),
];