@@ -3,14 +3,19 @@ import { IFile } from './interfaces';
3
3
4
4
export type FileTypeValidatorOptions = {
5
5
fileType : string | RegExp ;
6
+
7
+ /**
8
+ * If `true`, the validator will skip the magic numbers validation.
9
+ * This can be useful when you can't identify some files as there are no common magic numbers available for some file types.
10
+ * @default false
11
+ */
12
+ skipMagicNumbersValidation ?: boolean ;
6
13
} ;
7
14
8
15
/**
9
- * Defines the built-in FileType File Validator. It validates incoming files mime-type
10
- * matching a string or a regular expression. Note that this validator uses a naive strategy
11
- * to check the mime-type and could be fooled if the client provided a file with renamed extension.
12
- * (for instance, renaming a 'malicious.bat' to 'malicious.jpeg'). To handle such security issues
13
- * with more reliability, consider checking against the file's [magic-numbers](https://en.wikipedia.org/wiki/Magic_number_%28programming%29)
16
+ * Defines the built-in FileTypeValidator. It validates incoming files by examining
17
+ * their magic numbers using the file-type package, providing more reliable file type validation
18
+ * than just checking the mimetype string.
14
19
*
15
20
* @see [File Validators](https://docs.nestjs.com/techniques/file-upload#validators)
16
21
*
@@ -20,19 +25,42 @@ export class FileTypeValidator extends FileValidator<
20
25
FileTypeValidatorOptions ,
21
26
IFile
22
27
> {
23
- buildErrorMessage ( ) : string {
28
+ buildErrorMessage ( file ?: IFile ) : string {
29
+ if ( file ?. mimetype ) {
30
+ return `Validation failed (current file type is ${ file . mimetype } , expected type is ${ this . validationOptions . fileType } )` ;
31
+ }
24
32
return `Validation failed (expected type is ${ this . validationOptions . fileType } )` ;
25
33
}
26
34
27
- isValid ( file ?: IFile ) : boolean {
35
+ async isValid ( file ?: IFile ) : Promise < boolean > {
28
36
if ( ! this . validationOptions ) {
29
37
return true ;
30
38
}
31
39
32
- return (
33
- ! ! file &&
34
- 'mimetype' in file &&
35
- ! ! file . mimetype . match ( this . validationOptions . fileType )
36
- ) ;
40
+ const isFileValid = ! ! file && 'mimetype' in file ;
41
+
42
+ if ( this . validationOptions . skipMagicNumbersValidation ) {
43
+ return (
44
+ isFileValid && ! ! file . mimetype . match ( this . validationOptions . fileType )
45
+ ) ;
46
+ }
47
+
48
+ if ( ! isFileValid || ! file . buffer ) {
49
+ return false ;
50
+ }
51
+
52
+ try {
53
+ const { fileTypeFromBuffer } = ( await eval (
54
+ 'import ("file-type")' ,
55
+ ) ) as typeof import ( 'file-type' ) ;
56
+
57
+ const fileType = await fileTypeFromBuffer ( file . buffer ) ;
58
+
59
+ return (
60
+ ! ! fileType && ! ! fileType . mime . match ( this . validationOptions . fileType )
61
+ ) ;
62
+ } catch {
63
+ return false ;
64
+ }
37
65
}
38
66
}
0 commit comments