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); const nonVisibleWritableAttributes = intersection(nonVisibleAttributes, writableAttributes);
return uniq([ return uniq([...fields, ...COMPONENT_FIELDS, ...nonVisibleWritableAttributes]);
...fields,
...STATIC_FIELDS,
...COMPONENT_FIELDS,
...nonVisibleWritableAttributes,
]);
}; };
const getOutputFields = (fields = []) => { const getOutputFields = (fields = []) => {

View File

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

View File

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

View File

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