Skip to content

Commit 5243fca

Browse files
committed
fix(common): update file mime package and add param skip magic numbers
1 parent 664c30d commit 5243fca

File tree

9 files changed

+79
-1051
lines changed

9 files changed

+79
-1051
lines changed

package-lock.json

Lines changed: 7 additions & 1041 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
"express": "5.1.0",
6868
"fast-json-stringify": "6.0.1",
6969
"fast-safe-stringify": "2.1.1",
70-
"file-type": "20.4.1",
70+
"file-type-checker": "1.1.4",
7171
"iterare": "1.2.1",
7272
"object-hash": "3.0.0",
7373
"path-to-regexp": "8.2.0",

packages/common/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
},
1919
"license": "MIT",
2020
"dependencies": {
21-
"file-type": "20.4.1",
21+
"file-type-checker": "1.1.4",
2222
"iterare": "1.2.1",
2323
"tslib": "2.8.1",
2424
"uid": "2.0.2"

packages/common/pipes/file/file-type.validator.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as fileTypeChecker from 'file-type-checker';
12
import { FileValidator } from './file-validator.interface';
23
import { FileTypeValidatorOptions, IFile } from './interfaces';
34

@@ -26,19 +27,24 @@ export class FileTypeValidator extends FileValidator<
2627
return true;
2728
}
2829

29-
if (!file || !file?.buffer || !file?.mimetype) {
30+
const isFileValid = !!file && 'mimetype' in file;
31+
32+
if (this.validationOptions.skipMagicNumbersValidation) {
33+
return (
34+
isFileValid && !!file.mimetype.match(this.validationOptions.fileType)
35+
);
36+
}
37+
38+
if (!isFileValid || !file.buffer) {
3039
return false;
3140
}
3241

3342
try {
34-
const { fileTypeFromBuffer } = await import('file-type');
35-
36-
const fileType = await fileTypeFromBuffer(file.buffer);
37-
if (!fileType) {
38-
return false;
39-
}
43+
const fileType = fileTypeChecker.detectFile(file.buffer);
4044

41-
return !!fileType.mime.match(this.validationOptions.fileType);
45+
return (
46+
!!fileType && !!fileType.mimeType.match(this.validationOptions.fileType)
47+
);
4248
} catch {
4349
return false;
4450
}

packages/common/pipes/file/interfaces/file.interface.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,11 @@ export interface IFile {
66

77
export type FileTypeValidatorOptions = {
88
fileType: string | RegExp;
9+
10+
/**
11+
* If `true`, the validator will skip the magic numbers validation.
12+
* This can be useful when you can't identify some files as there are no common magic numbers available for some file types.
13+
* @default false
14+
*/
15+
skipMagicNumbersValidation?: boolean;
916
};

packages/common/test/pipes/file/file-type.validator.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,19 @@ describe('FileTypeValidator', () => {
144144
expect(await fileTypeValidator.isValid(requestFile)).to.equal(true);
145145
});
146146

147+
it('should skip magic numbers validation when the skipMagicNumbersValidation is true', async () => {
148+
const fileTypeValidator = new FileTypeValidator({
149+
fileType: 'image/jpeg',
150+
skipMagicNumbersValidation: true,
151+
});
152+
153+
const requestFile = {
154+
mimetype: 'image/jpeg',
155+
} as IFile;
156+
157+
expect(await fileTypeValidator.isValid(requestFile)).to.equal(true);
158+
});
159+
147160
it('should return false when the file buffer does not match any known type', async () => {
148161
const fileTypeValidator = new FileTypeValidator({
149162
fileType: 'unknown/type',

sample/29-file-upload/e2e/app/app.e2e-spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,20 @@ describe('E2E FileTest', () => {
3232
it('should allow for file uploads that pass validation', async () => {
3333
return request(app.getHttpServer())
3434
.post('/file/pass-validation')
35+
.attach('file', './resources/nestjs.jpg')
36+
.field('name', 'test')
37+
.expect(201)
38+
.expect({
39+
body: {
40+
name: 'test',
41+
},
42+
file: readFileSync('./resources/nestjs.jpg').toString(),
43+
});
44+
});
45+
46+
it('should allow for file uploads that pass validation without using magic numbers validation', async () => {
47+
return request(app.getHttpServer())
48+
.post('/file/pass-validation-skip-magic-numbers-validation')
3549
.attach('file', './package.json')
3650
.field('name', 'test')
3751
.expect(201)
274 KB
Loading

sample/29-file-upload/src/app.controller.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,33 @@ export class AppController {
3636
@UseInterceptors(FileInterceptor('file'))
3737
@Post('file/pass-validation')
3838
uploadFileAndPassValidation(
39+
@Body() body: SampleDto,
40+
@UploadedFile(
41+
new ParseFilePipeBuilder()
42+
.addFileTypeValidator({
43+
fileType: 'jpeg',
44+
})
45+
.build({
46+
fileIsRequired: false,
47+
}),
48+
)
49+
file?: Express.Multer.File,
50+
) {
51+
return {
52+
body,
53+
file: file?.buffer.toString(),
54+
};
55+
}
56+
57+
@UseInterceptors(FileInterceptor('file'))
58+
@Post('file/pass-validation-skip-magic-numbers-validation')
59+
uploadFileAndPassValidationWithoutMagicNumbersValidation(
3960
@Body() body: SampleDto,
4061
@UploadedFile(
4162
new ParseFilePipeBuilder()
4263
.addFileTypeValidator({
4364
fileType: 'json',
65+
skipMagicNumbersValidation: true,
4466
})
4567
.build({
4668
fileIsRequired: false,

0 commit comments

Comments
 (0)