Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Simple express middleware for uploading files.
[![Coverage Status](https://img.shields.io/coveralls/richardgirges/express-fileupload.svg)](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)
Expand Down Expand Up @@ -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)
Expand All @@ -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/'
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -117,9 +116,9 @@ uriDecodeFileNames | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code
safeFileNames | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>true</code></li><li>regex</li></ul> | Strips characters from the upload's filename. You can use custom regex to determine what to strip. If set to `true`, non-alphanumeric characters _except_ dashes and underscores will be stripped. This option is off by default.<br /><br />**Example #1 (strip slashes from file names):** `app.use(fileUpload({ safeFileNames: /\\/g }))`<br />**Example #2:** `app.use(fileUpload({ safeFileNames: true }))`
preserveExtension | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>true</code></li><li><code>*Number*</code></li></ul> | Preserves filename extension when using <code>safeFileNames</code> option. If set to <code>true</code>, will default to an extension length of 3. If set to <code>*Number*</code>, 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.<br /><br />**Example #1 (true):**<br /><code>app.use(fileUpload({ safeFileNames: true, preserveExtension: true }));</code><br />*myFileName.ext* --> *myFileName.ext*<br /><br />**Example #2 (max extension length 2, extension shifted):**<br /><code>app.use(fileUpload({ safeFileNames: true, preserveExtension: 2 }));</code><br />*myFileName.ext* --> *myFileNamee.xt*
abortOnLimit | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>true</code></ul> | Returns a HTTP 413 when the file is bigger than the size limit if true. Otherwise, it will add a <code>truncated = true</code> to the resulting file structure.
responseOnLimit | <ul><li><code>'File size limit has been reached'</code>&nbsp;**(default)**</li><li><code>*String*</code></ul> | Response which will be send to client if file size limit exceeded when abortOnLimit set to true.
responseOnLimit | <ul><li><code>'File size limit has been reached'</code>&nbsp;**(default)**</li><li><code>*String*</code></ul> | Response which will be sent to client if file size limit exceeded when abortOnLimit set to true.
limitHandler | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>function(req, res, next)</code></li></ul> | User defined limit handler which will be invoked if the file is bigger than configured limits.
useTempFiles | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>true</code></ul> | By default this module uploads files into RAM. Setting this option to True turns on using temporary files instead of utilising RAM. This avoids memory overflow issues when uploading large files or in case of uploading lots of files at same time.
useTempFiles | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>true</code></ul> | By default this module uploads files into RAM. Setting this option to True turns on using temporary files instead of utilizing RAM. This avoids memory overflow issues when uploading large files or in case of uploading lots of files at the same time.
tempFileDir | <ul><li><code>String</code>&nbsp;**(path)**</li></ul> | Path to store temporary files.<br />Used along with the <code>useTempFiles</code> option. By default this module uses 'tmp' folder in the current working directory.<br />You can use trailing slash, but it is not necessary.
tempFilePermissions | <ul><li>644&nbsp;**(default)**</li><li><code>Integer</code></li></ul> | Permissions applied to temporary files.<br />Used along with the <code>useTempFiles</code> option. By default this module uses '644' permissions.<br />You should use this option if using shared hosting - to protect user data from being accessed by other users on the server.
parseNested | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>true</code></li></ul> | By default, req.body and req.files are flattened like this: <code>{'name': 'John', 'hobbies[0]': 'Cinema', 'hobbies[1]': 'Bike'}</code><br /><br/>When this option is enabled they are parsed in order to be nested like this: <code>{'name': 'John', 'hobbies': ['Cinema', 'Bike']}</code>
Expand Down
17 changes: 10 additions & 7 deletions lib/fileFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ const {
moveFile,
promiseCallback,
checkAndMakeDir,
saveBufferToFile
saveBufferToFile,
parseFileName
} = require('./utilities');

/**
Expand All @@ -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));
};

/**
Expand All @@ -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 = {}) => {
Expand All @@ -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);
Expand All @@ -62,4 +65,4 @@ module.exports = (options, fileUploadOptions = {}) => {
return isFunc(callback) ? moveFunc(callback) : new Promise(moveFunc);
}
};
};
};
19 changes: 15 additions & 4 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
};

/**
Expand All @@ -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);
});
};
};
};
11 changes: 9 additions & 2 deletions lib/processMultipart.js
Original file line number Diff line number Diff line change
@@ -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');
Expand Down Expand Up @@ -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.');
}
Comment on lines +66 to +71

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is not mandatory dude..

// Define methods and handlers for upload process.
const {
dataHandler,
Expand Down Expand Up @@ -182,4 +189,4 @@ module.exports = (options, req, res, next) => {
});

req.pipe(busboy);
};
};
Binary file modified lib/utilities.js
Binary file not shown.
2 changes: 1 addition & 1 deletion test/multipartUploads.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -472,4 +472,4 @@ describe('multipartUploads: Test Aborting/Canceling during upload', function() {
});
});
});
});
});