diff --git a/README.md b/README.md index 66ddfe4..7d5b378 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Simple express middleware for uploading files. [](https://coveralls.io/r/richardgirges/express-fileupload) # Help us Improve express-fileupload -This package is still very much supported and maintained. But the more help the better. If you're interested any of the following: +This package is still very much supported and maintained. But the more help the better. If you're interested in any of the following: * Ticket and PR triage * Feature scoping and implementation * Maintenance (upgrading packages, fixing security vulnerabilities, etc) @@ -53,7 +53,6 @@ The **req.files.foo** object will contain the following: * From 1.1.1 until 1.5.1, `md5` is reverted back to MD5 checksum value and also added full MD5 support in case you are using temporary files. * From 1.5.1 onward, `md5` still holds the checksum value, but the checksum is generated with the provided `hashAlgorithm` option. The property name remains `md5` for backwards compatibility. - ### Examples * [Example Project](https://github.com/richardgirges/express-fileupload/tree/master/example) * [Basic File Upload](https://github.com/richardgirges/express-fileupload/tree/master/example#basic-file-upload) @@ -72,7 +71,7 @@ app.use(fileUpload({ Use temp files instead of memory for managing the upload process. ```javascript -// Note that this option available for versions 1.0.0 and newer. +// Note that this option is available for versions 1.0.0 and newer. app.use(fileUpload({ useTempFiles : true, tempFileDir : '/tmp/' @@ -81,8 +80,8 @@ app.use(fileUpload({ ### Using debug option -You can set `debug` option to `true` to see some logging about upload process. -In this case middleware uses `console.log` and adds `Express-file-upload` prefix for outputs. +You can set `debug` option to `true` to see some logging about the upload process. +In this case, middleware uses `console.log` and adds `Express-file-upload` prefix for outputs. You can set a custom logger having `.log()` method to the `logger` option. It will show you whether the request is invalid and also common events triggered during upload. @@ -105,7 +104,7 @@ Express-file-upload: Cleaning up temporary file /node/express-fileupload/test/te * `New upload started testFile->car.png` says that new upload started with field `testFile` and file name `car.png`. * `Uploading testFile->car.png, bytes:21232...` shows current progress for each new data chunk. * `Upload timeout` means that no data came during `uploadTimeout`. -* `Cleaning up temporary file` Here finaly we see cleaning up of the temporary file because of upload timeout reached. +* `Cleaning up temporary file` Here finally we see cleaning up of the temporary file because of upload timeout reached. ### Available Options Pass in non-Busboy options directly to the middleware. These are express-fileupload specific options. @@ -117,9 +116,9 @@ uriDecodeFileNames |
false **(default)**false **(default)**true- regex
false **(default)**true*Number*safeFileNames option. If set to true, will default to an extension length of 3. If set to *Number*, this will be the max allowable extension length. If an extension is smaller than the extension length, it remains untouched. If the extension is longer, it is shifted.app.use(fileUpload({ safeFileNames: true, preserveExtension: true }));app.use(fileUpload({ safeFileNames: true, preserveExtension: 2 }));false **(default)**truetruncated = true to the resulting file structure.
-responseOnLimit | 'File size limit has been reached' **(default)***String*'File size limit has been reached' **(default)***String*false **(default)**function(req, res, next)false **(default)**truefalse **(default)**trueString **(path)**useTempFiles option. By default this module uses 'tmp' folder in the current working directory.IntegeruseTempFiles option. By default this module uses '644' permissions.false **(default)**true{'name': 'John', 'hobbies[0]': 'Cinema', 'hobbies[1]': 'Bike'}{'name': 'John', 'hobbies': ['Cinema', 'Bike']}
diff --git a/lib/fileFactory.js b/lib/fileFactory.js
index 02b82df..24cc1a5 100644
--- a/lib/fileFactory.js
+++ b/lib/fileFactory.js
@@ -6,7 +6,8 @@ const {
moveFile,
promiseCallback,
checkAndMakeDir,
- saveBufferToFile
+ saveBufferToFile,
+ parseFileName
} = require('./utilities');
/**
@@ -18,8 +19,9 @@ const {
* @returns {Function}
*/
const moveFromTemp = (filePath, options, fileUploadOptions) => (resolve, reject) => {
- debugLog(fileUploadOptions, `Moving temporary file ${options.tempFilePath} to ${filePath}`);
- moveFile(options.tempFilePath, filePath, promiseCallback(resolve, reject));
+ const sanitizedFilePath = parseFileName(fileUploadOptions, filePath);
+ debugLog(fileUploadOptions, `Moving temporary file ${options.tempFilePath} to ${sanitizedFilePath}`);
+ moveFile(options.tempFilePath, sanitizedFilePath, promiseCallback(resolve, reject));
};
/**
@@ -31,8 +33,9 @@ const moveFromTemp = (filePath, options, fileUploadOptions) => (resolve, reject)
* @returns {Function}
*/
const moveFromBuffer = (filePath, options, fileUploadOptions) => (resolve, reject) => {
- debugLog(fileUploadOptions, `Moving uploaded buffer to ${filePath}`);
- saveBufferToFile(options.buffer, filePath, promiseCallback(resolve, reject));
+ const sanitizedFilePath = parseFileName(fileUploadOptions, filePath);
+ debugLog(fileUploadOptions, `Moving uploaded buffer to ${sanitizedFilePath}`);
+ saveBufferToFile(options.buffer, sanitizedFilePath, promiseCallback(resolve, reject));
};
module.exports = (options, fileUploadOptions = {}) => {
@@ -52,7 +55,7 @@ module.exports = (options, fileUploadOptions = {}) => {
mimetype: options.mimetype,
md5: options.hash,
mv: (filePath, callback) => {
- // Define a propper move function.
+ // Define a proper move function.
const moveFunc = fileUploadOptions.useTempFiles
? moveFromTemp(filePath, options, fileUploadOptions)
: moveFromBuffer(filePath, options, fileUploadOptions);
@@ -62,4 +65,4 @@ module.exports = (options, fileUploadOptions = {}) => {
return isFunc(callback) ? moveFunc(callback) : new Promise(moveFunc);
}
};
-};
+};
\ No newline at end of file
diff --git a/lib/index.js b/lib/index.js
index a08588b..f690937 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -4,7 +4,6 @@ const path = require('path');
const processMultipart = require('./processMultipart');
const isEligibleRequest = require('./isEligibleRequest');
const { buildOptions, debugLog } = require('./utilities');
-const busboy = require('busboy'); // eslint-disable-line no-unused-vars
const DEFAULT_OPTIONS = {
debug: false,
@@ -22,7 +21,11 @@ const DEFAULT_OPTIONS = {
useTempFiles: false,
tempFileDir: path.join(process.cwd(), 'tmp'),
tempFilePermissions: 0o644,
- hashAlgorithm: 'md5'
+ hashAlgorithm: 'md5',
+ enableMimeTypeValidation: false, // New option for enabling MIME type validation
+ acceptableMimeTypes: [], // New option to specify acceptable MIME types
+ rejectPolyglotFiles: false, // New option to reject polyglot files
+ enablePdfSanitization: false // New option to enable PDF sanitization
};
/**
@@ -37,6 +40,14 @@ module.exports = (options) => {
debugLog(uploadOptions, 'Request is not eligible for file upload!');
return next();
}
- processMultipart(uploadOptions, req, res, next);
+ processMultipart(uploadOptions, req, res, (err) => {
+ if (err && err.message.includes('Polyglot file detected')) {
+ debugLog(uploadOptions, 'Polyglot file detected and rejected.');
+ if (uploadOptions.rejectPolyglotFiles) {
+ return res.status(400).send('Polyglot files are not allowed.');
+ }
+ }
+ next(err);
+ });
};
-};
+};
\ No newline at end of file
diff --git a/lib/processMultipart.js b/lib/processMultipart.js
index 5f3d24c..b662822 100644
--- a/lib/processMultipart.js
+++ b/lib/processMultipart.js
@@ -1,4 +1,5 @@
const Busboy = require('busboy');
+const mime = require('mime-types');
const UploadTimer = require('./uploadtimer');
const fileFactory = require('./fileFactory');
const memHandler = require('./memHandler');
@@ -60,8 +61,14 @@ module.exports = (options, req, res, next) => {
// Build req.files fields
busboy.on('file', (field, file, info) => {
// Parse file name(cutting huge names, decoding, etc..).
- const {filename:name, encoding, mimeType: mime} = info;
+ const {filename: name, encoding, mimeType: mime} = info;
const filename = parseFileName(options, name);
+ // Validate MIME type
+ const expectedMimeType = mime.lookup(filename);
+ if (expectedMimeType !== mime) {
+ debugLog(options, `MIME type mismatch: expected ${expectedMimeType}, got ${mime}`);
+ return closeConnection(400, 'Invalid file type.');
+ }
// Define methods and handlers for upload process.
const {
dataHandler,
@@ -182,4 +189,4 @@ module.exports = (options, req, res, next) => {
});
req.pipe(busboy);
-};
+};
\ No newline at end of file
diff --git a/lib/utilities.js b/lib/utilities.js
index ddafae8..0cc71de 100644
Binary files a/lib/utilities.js and b/lib/utilities.js differ
diff --git a/test/multipartUploads.spec.js b/test/multipartUploads.spec.js
index 67e9f68..e390660 100644
--- a/test/multipartUploads.spec.js
+++ b/test/multipartUploads.spec.js
@@ -472,4 +472,4 @@ describe('multipartUploads: Test Aborting/Canceling during upload', function() {
});
});
});
-});
+});
\ No newline at end of file