Repository: nestjs/nest
Pull request #9832 (2022-06-24 21:29:44)

feat(common): file type validator
add regex support when checking a file mime-type as requested [here](https://github.com/nestjs/nest/pull/9718#issuecomment-1164127445).

## PR Checklist
Please check if your PR fulfills the following requirements:

- [x] The commit message follows our guidelines: https://github.com/nestjs/nest/blob/master/CONTRIBUTING.md
- [x] Tests for the changes have been added (for bug fixes / features)
- [x] Docs have been added / updated (for bug fixes / features)


## PR Type
What kind of change does this PR introduce?

<!-- Please check the one that applies to this PR using "x". -->
- [ ] Bugfix
- [x] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Other... Please describe:

## What is the current behavior?
FileTypePipe only checks against common strings.

## What is the new behavior?
FileTypePipe now can check against regular expressions.

## Does this PR introduce a breaking change?
- [ ] Yes
- [x] No

<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. -->


## Other information


Patch:
diff --git a/packages/common/pipes/file/file-type.validator.ts b/packages/common/pipes/file/file-type.validator.ts
index 334049149..240d3efe8 100644
--- a/packages/common/pipes/file/file-type.validator.ts
+++ b/packages/common/pipes/file/file-type.validator.ts
@@ -1,11 +1,15 @@
 import { FileValidator } from './file-validator.interface';
 
 export type FileTypeValidatorOptions = {
-  fileType: string;
+  fileType: string | RegExp;
 };
 
 /**
- * Defines the built-in FileType File Validator
+ * Defines the built-in FileType File Validator. It validates incoming files mime-type
+ * matching a string or a regular expression. Note that this validator uses a naive strategy
+ * to check the mime-type and could be fooled if the client provided a file with renamed extension.
+ * (for instance, renaming a 'malicious.bat' to 'malicious.jpeg'). To handle such security issues
+ * with more reliability, consider checking against the file's [magic-numbers](https://en.wikipedia.org/wiki/Magic_number_%28programming%29)
  *
  * @see [File Validators](https://docs.nestjs.com/techniques/file-upload#validators)
  *
@@ -25,6 +29,8 @@ export class FileTypeValidator extends FileValidator<FileTypeValidatorOptions> {
       return false;
     }
 
-    return (file.mimetype as string).endsWith(this.validationOptions.fileType);
+    return Boolean(
+      (file.mimetype as string).match(this.validationOptions.fileType),
+    );
   }
 }


Test patch:
diff --git a/packages/common/test/pipes/file/file-type.validator.spec.ts b/packages/common/test/pipes/file/file-type.validator.spec.ts
index c4248f66a..8b58d1140 100644
--- a/packages/common/test/pipes/file/file-type.validator.spec.ts
+++ b/packages/common/test/pipes/file/file-type.validator.spec.ts
@@ -27,6 +27,18 @@ describe('FileTypeValidator', () => {
       expect(fileTypeValidator.isValid(requestFile)).to.equal(true);
     });
 
+    it('should return true when the file mimetype matches the specified regexp', () => {
+      const fileTypeValidator = new FileTypeValidator({
+        fileType: /word/,
+      });
+
+      const requestFile = {
+        mimetype: 'application/msword',
+      };
+
+      expect(fileTypeValidator.isValid(requestFile)).to.equal(true);
+    });
+
     it('should return false when the file mimetype is different from the specified', () => {
       const fileTypeValidator = new FileTypeValidator({
         fileType: 'image/jpeg',
@@ -39,6 +51,18 @@ describe('FileTypeValidator', () => {
       expect(fileTypeValidator.isValid(requestFile)).to.equal(false);
     });
 
+    it('should return false when the file mimetype does not match the provided regexp', () => {
+      const fileTypeValidator = new FileTypeValidator({
+        fileType: /mp4/,
+      });
+
+      const requestFile = {
+        mimetype: 'image/png',
+      };
+
+      expect(fileTypeValidator.isValid(requestFile)).to.equal(false);
+    });
+
     it('should return false when the file mimetype was not provided', () => {
       const fileTypeValidator = new FileTypeValidator({
         fileType: 'image/jpeg',