feat: add mimeTypes validation for uploads

This commit is contained in:
Elliot DeNolf
2021-05-11 21:33:07 -04:00
committed by Elliot DeNolf
parent 1c6f32f288
commit a5fcdf03ba
5 changed files with 71 additions and 2 deletions

View File

@@ -10,6 +10,7 @@ import baseUploadFields from '../../fields/baseFields/baseUploadFields';
import baseImageUploadFields from '../../fields/baseFields/baseImageUploadFields';
import { formatLabels } from '../../utilities/formatLabels';
import { defaults, authDefaults } from './defaults';
import { mimeTypeValidator } from '../../fields/baseFields/mimeTypeValidator';
const mergeBaseFields = (fields, baseFields) => {
const mergedFields = [];
@@ -73,6 +74,10 @@ const sanitizeCollection = (collections: PayloadCollectionConfig[], collection:
let uploadFields = baseUploadFields;
if (sanitized.upload.mimeTypes) {
uploadFields.find((f) => f.name === 'mimeType').validate = mimeTypeValidator(sanitized.upload.mimeTypes);
}
if (sanitized.upload.imageSizes && Array.isArray(sanitized.upload.imageSizes)) {
uploadFields = uploadFields.concat(baseImageUploadFields(sanitized.upload.imageSizes));
}

View File

@@ -3,7 +3,7 @@ import APIError from './APIError';
class ValidationError extends APIError {
constructor(results: {message: string, field: string}[]) {
super(`Bad request with ${results.length} errors`, httpStatus.BAD_REQUEST, results);
super(`The following field${results.length === 1 ? ' is' : 's are'} invalid: ${results.map((f) => f.field).join(', ')}`, httpStatus.BAD_REQUEST, results);
}
}

View File

@@ -0,0 +1,55 @@
import { mimeTypeValidator } from './mimeTypeValidator';
describe('mimeTypeValidator', () => {
it('should validate single mimeType', () => {
const mimeTypes = ['image/png'];
const validate = mimeTypeValidator(mimeTypes);
expect(validate('image/png')).toBe(true);
});
it('should validate multiple mimeTypes', () => {
const mimeTypes = ['image/png', 'application/pdf'];
const validate = mimeTypeValidator(mimeTypes);
expect(validate('image/png')).toBe(true);
expect(validate('application/pdf')).toBe(true);
});
it('should validate using wildcard', () => {
const mimeTypes = ['image/*'];
const validate = mimeTypeValidator(mimeTypes);
expect(validate('image/png')).toBe(true);
expect(validate('image/gif')).toBe(true);
});
it('should validate multiple wildcards', () => {
const mimeTypes = ['image/*', 'audio/*'];
const validate = mimeTypeValidator(mimeTypes);
expect(validate('image/png')).toBe(true);
expect(validate('audio/mpeg')).toBe(true);
});
it('should not validate when unmatched', () => {
const mimeTypes = ['image/png'];
const validate = mimeTypeValidator(mimeTypes);
expect(validate('audio/mpeg')).toBe('Invalid file type: \'audio/mpeg\'');
});
it('should not validate when unmatched - multiple mimeTypes', () => {
const mimeTypes = ['image/png', 'application/pdf'];
const validate = mimeTypeValidator(mimeTypes);
expect(validate('audio/mpeg')).toBe('Invalid file type: \'audio/mpeg\'');
});
it('should not validate using wildcard - unmatched', () => {
const mimeTypes = ['image/*'];
const validate = mimeTypeValidator(mimeTypes);
expect(validate('audio/mpeg')).toBe('Invalid file type: \'audio/mpeg\'');
});
it('should not validate multiple wildcards - unmatched', () => {
const mimeTypes = ['image/*', 'audio/*'];
const validate = mimeTypeValidator(mimeTypes);
expect(validate('video/mp4')).toBe('Invalid file type: \'video/mp4\'');
expect(validate('application/pdf')).toBe('Invalid file type: \'application/pdf\'');
});
});

View File

@@ -0,0 +1,9 @@
import { Validate } from '../config/types';
export const mimeTypeValidator = (mimeTypes: string[]): Validate => (val: string) => {
const cleanedMimeTypes = mimeTypes.map((v) => v.replace('*', ''));
return !cleanedMimeTypes.some((v) => val.startsWith(v))
? `Invalid file type: '${val}'`
: true;
};

View File

@@ -40,7 +40,7 @@ export type Labels = {
plural: string;
};
export type Validate = (value: unknown, options?: any) => string | boolean | Promise<string | boolean>;
export type Validate = (value: unknown, options?: any) => string | true | Promise<string | true>;
export type OptionObject = {
label: string