Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

express-fileupload

Package Overview
Dependencies
Maintainers
1
Versions
49
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

express-fileupload - npm Package Compare versions

Comparing version 0.4.0 to 1.0.0-alpha.1

example/index.html

246

lib/index.js
'use strict';
const Busboy = require('busboy');
const fs = require('fs-extra');
const streamifier = require('streamifier');
const md5 = require('md5');
const fileFactory = require('./fileFactory');
const processMultipart = require('./processMultipart');
const isEligibleRequest = require('./isEligibleRequest');
const ACCEPTABLE_MIME = /^(?:multipart\/.+)$/i;
const UNACCEPTABLE_METHODS = [
'GET',
'HEAD'
];
const fileUploadOptionsDefaults = {
safeFileNames: false,
preserveExtension: false,
abortOnLimit: false,
createParentPath: false
};
module.exports = function(options) {
options = options || {};
/**
* Expose the file upload middleware
*/
module.exports = function(fileUploadOptions) {
fileUploadOptions = Object.assign({}, fileUploadOptionsDefaults, fileUploadOptions || {});
return function(req, res, next) {
if (!hasBody(req) || !hasAcceptableMethod(req) || !hasAcceptableMime(req)) {
if (!isEligibleRequest(req)) {
return next();
}
processMultipart(options, req, res, next);
processMultipart(fileUploadOptions, req, res, next);
};

@@ -27,217 +30,4 @@ };

/**
* Processes multipart request
* Builds a req.body object for fields
* Builds a req.files object for files
* @param {Object} options expressFileupload and Busboy options
* @param {Object} req Express request object
* @param {Object} res Express response object
* @param {Function} next Express next method
* @return {void}
* Quietly expose fileFactory; useful for testing
*/
function processMultipart(options, req, res, next) {
let busboyOptions = {};
let busboy;
req.files = null;
// Build busboy config
for (let k in options) {
if (Object.prototype.hasOwnProperty.call(options, k)) {
busboyOptions[k] = options[k];
}
}
// Attach request headers to busboy config
busboyOptions.headers = req.headers;
// Init busboy instance
busboy = new Busboy(busboyOptions);
// Build multipart req.body fields
busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated, encoding, mime) {
req.body = req.body || {};
let prev = req.body[fieldname];
if (!prev) {
return req.body[fieldname] = val;
}
if (Array.isArray(prev)) {
return prev.push(val);
}
req.body[fieldname] = [prev, val];
});
// Build req.files fields
busboy.on('file', function(fieldname, file, filename, encoding, mime) {
const buffers = [];
let safeFileNameRegex = /[^\w-]/g;
file.on('limit', () => {
if (options.abortOnLimit) {
res.writeHead(413, {'Connection': 'close'});
res.end('File size limit has been reached');
}
});
file.on('data', function(data) {
buffers.push(data);
if (options.debug) {
return console.log('Uploading %s -> %s', fieldname, filename);
}
});
file.on('end', function() {
if (!req.files) {
req.files = {};
}
const buf = Buffer.concat(buffers);
// see: https://github.com/richardgirges/express-fileupload/issues/14
// firefox uploads empty file in case of cache miss when f5ing page.
// resulting in unexpected behavior. if there is no file data, the file is invalid.
if (!buf.length) {
return;
}
if (options.safeFileNames) {
let maxExtensionLength = 3;
let extension = '';
if (typeof options.safeFileNames === 'object') {
safeFileNameRegex = options.safeFileNames;
}
maxExtensionLength = parseInt(options.preserveExtension);
if (options.preserveExtension || maxExtensionLength === 0) {
if (isNaN(maxExtensionLength)) {
maxExtensionLength = 3;
} else {
maxExtensionLength = Math.abs(maxExtensionLength);
}
let filenameParts = filename.split('.');
let filenamePartsLen = filenameParts.length;
if (filenamePartsLen > 1) {
extension = filenameParts.pop();
if (extension.length > maxExtensionLength && maxExtensionLength > 0) {
filenameParts[filenameParts.length - 1] +=
'.' + extension.substr(0, extension.length - maxExtensionLength);
extension = extension.substr(-maxExtensionLength);
}
extension = maxExtensionLength ? '.' + extension.replace(safeFileNameRegex, '') : '';
filename = filenameParts.join('.');
}
}
filename = filename.replace(safeFileNameRegex, '').concat(extension);
}
let newFile = {
name: filename,
data: buf,
encoding: encoding,
truncated: file.truncated,
mimetype: mime,
md5: md5(buf),
mv: function(path, callback) {
// Callback is passed in, use the callback API
if (callback) {
doMove(
() => {
callback(null);
},
(error) => {
callback(error);
}
);
// Otherwise, return a promise
} else {
return new Promise((resolve, reject) => {
doMove(resolve, reject);
});
}
/**
* Local function that moves the file to a different location on the filesystem
* Takes two function arguments to make it compatible w/ Promise or Callback APIs
* @param {Function} successFunc
* @param {Function} errorFunc
*/
function doMove(successFunc, errorFunc) {
const fstream = fs.createWriteStream(path);
streamifier.createReadStream(buf).pipe(fstream);
fstream.on('error', function(error) {
errorFunc(error);
});
fstream.on('close', function() {
successFunc();
});
}
}
};
// Non-array fields
if (!req.files.hasOwnProperty(fieldname)) {
req.files[fieldname] = newFile;
} else {
// Array fields
if (req.files[fieldname] instanceof Array) {
req.files[fieldname].push(newFile);
} else {
req.files[fieldname] = [req.files[fieldname], newFile];
}
}
});
file.on('error', next);
});
busboy.on('finish', next);
busboy.on('error', next);
req.pipe(busboy);
}
// Methods below were copied from, or heavily inspired by the Connect and connect-busboy packages
/**
* Ensures the request is not using a non-compliant multipart method
* such as GET or HEAD
* @param {Object} req Express req object
* @return {Boolean}
*/
function hasAcceptableMethod(req) {
return (UNACCEPTABLE_METHODS.indexOf(req.method) < 0);
}
/**
* Ensures that only multipart requests are processed by express-fileupload
* @param {Object} req Express req object
* @return {Boolean}
*/
function hasAcceptableMime(req) {
let str = (req.headers['content-type'] || '').split(';')[0];
return ACCEPTABLE_MIME.test(str);
}
/**
* Ensures the request contains a content body
* @param {Object} req Express req object
* @return {Boolean}
*/
function hasBody(req) {
return ('transfer-encoding' in req.headers) ||
('content-length' in req.headers && req.headers['content-length'] !== '0');
}
module.exports.fileFactory = fileFactory;
{
"name": "express-fileupload",
"version": "0.4.0",
"version": "1.0.0-alpha.1",
"author": "Richard Girges <richardgirges@gmail.com>",

@@ -14,3 +14,2 @@ "description": "Simple express file upload middleware that wraps around Busboy",

"busboy": "^0.2.14",
"fs-extra": "^4.0.1",
"md5": "^2.2.1",

@@ -35,11 +34,11 @@ "streamifier": "^0.1.1"

"devDependencies": {
"body-parser": "^1.17.2",
"coveralls": "^2.13.1",
"eslint": "^4.5.0",
"eslint-config-google": "^0.9.1",
"express": "^4.15.4",
"body-parser": "^1.18.3",
"coveralls": "^3.0.2",
"eslint": "^5.6.0",
"express": "^4.16.3",
"istanbul": "^0.4.5",
"mocha": "^3.5.0",
"supertest": "^3.0.0"
"mocha": "^5.2.0",
"rimraf": "^2.6.2",
"supertest": "^3.3.0"
}
}

@@ -0,1 +1,4 @@

## !! LOOKING FOR ADDITIONAL MAINTAINERS !!
Contact @richardgirges if you're interested.
# express-fileupload

@@ -44,2 +47,3 @@ Simple express middleware for uploading files.

* `req.files.foo.truncated`: A boolean that represents if the file is over the size limit
* `req.files.foo.md5`: A function that returns an MD5 checksum of the uploaded file

@@ -123,2 +127,3 @@ ### Full Example

--- | --- | ---
createParentPath | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>true</code></ul> | Automatically creates the directory path specified in `.mv(filePathName)`
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 }))`

@@ -125,0 +130,0 @@ 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*

@@ -48,3 +48,3 @@ 'use strict';

.expect(200)
.end(function(err, res) {
.end(function(err) {
if (err) {

@@ -68,3 +68,3 @@ return done(err);

.expect(200)
.end(function(err, res) {
.end(function(err) {
if (err) {

@@ -121,3 +121,3 @@ return done(err);

.expect(200)
.end(function(err, res) {
.end(function(err) {
if (err) {

@@ -141,3 +141,3 @@ return done(err);

.expect(200)
.end(function(err, res) {
.end(function(err) {
if (err) {

@@ -194,3 +194,3 @@ return done(err);

.expect(200)
.end(function(err, res) {
.end(function(err) {
if (err) {

@@ -229,3 +229,3 @@ return done(err);

.expect(200)
.end(function(err, res) {
.end(function(err) {
if (err) {

@@ -265,3 +265,3 @@ return done(err);

},
function(err, res) {
function(err) {
if (err) {

@@ -292,3 +292,3 @@ return done(err);

},
function(err, res) {
function(err) {
if (err) {

@@ -295,0 +295,0 @@ return done(err);

@@ -71,3 +71,3 @@ const fs = require('fs');

function(done) {
const fileUploadOptions = {safeFileNames: /[\$#]/g};
const fileUploadOptions = {safeFileNames: /[$#]/g};
const actualFileName = 'my$Invalid#fileName.png123';

@@ -74,0 +74,0 @@ const expectedFileName = 'myInvalidfileName.png123';

'use strict';
const path = require('path');
const fileDir = path.join(__dirname, 'files');
const uploadDir = path.join(__dirname, 'uploads');
const fs = require('fs-extra');
const fs = require('fs');
const rimraf = require('rimraf');

@@ -11,3 +13,4 @@ const clearUploadsDir = function() {

} else {
fs.emptyDirSync(uploadDir);
rimraf.sync(uploadDir);
fs.mkdirSync(uploadDir);
}

@@ -35,2 +38,3 @@ };

if (err) {
console.log('ERR', err); // eslint-disable-line
return res.status(500).send(err);

@@ -37,0 +41,0 @@ }

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc