Socket
Socket
Sign inDemoInstall

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 1.1.3-alpha.1 to 1.1.3-alpha.2

9

lib/index.js

@@ -7,2 +7,3 @@ 'use strict';

const processNested = require('./processNested');
const {buildOptions} = require('./utilities');

@@ -13,2 +14,4 @@ const fileUploadOptionsDefaults = {

abortOnLimit: false,
responseOnLimit: 'File size limit has been reached',
limitHandler: false,
createParentPath: false,

@@ -23,6 +26,6 @@ parseNested: false,

*/
module.exports = function(fileUploadOptions) {
fileUploadOptions = Object.assign({}, fileUploadOptionsDefaults, fileUploadOptions || {});
module.exports = (fileUploadOptions) => {
fileUploadOptions = buildOptions(fileUploadOptionsDefaults, fileUploadOptions);
return function(req, res, next) {
return function(req, res, next){
if (!isEligibleRequest(req)) {

@@ -29,0 +32,0 @@ return next();

const crypto = require('crypto');
const {debugLog} = require('./utilities');

@@ -14,2 +15,5 @@ /**

const getBuffer = () => Buffer.concat(buffers);
const emptyFunc = () => '';
return {

@@ -20,12 +24,6 @@ dataHandler: function(data) {

fileSize += data.length;
if (options.debug) {
return console.log('Uploading %s -> %s, bytes: %d', fieldname, filename, fileSize); // eslint-disable-line
}
debugLog(options, `Uploading ${fieldname} -> ${filename}, bytes: ${fileSize}`);
},
getBuffer: function(){
return Buffer.concat(buffers);
},
getFilePath: function(){
return '';
},
getBuffer: getBuffer,
getFilePath: emptyFunc,
getFileSize: function(){

@@ -37,8 +35,5 @@ return fileSize;

},
complete: function(){
return Buffer.concat(buffers);
},
cleanup: function(){
}
complete: getBuffer,
cleanup: emptyFunc
};
};

@@ -6,2 +6,6 @@ const Busboy = require('busboy');

const processNested = require('./processNested');
const {
buildOptions,
parseFileName
} = require('./utilities');

@@ -19,20 +23,8 @@ /**

module.exports = function processMultipart(options, req, res, next) {
let busboyOptions = {};
let busboy;
req.files = null;
// Build busboy options and init busboy instance.
let busboyOptions = buildOptions(options, {headers: req.headers});
let busboy = new Busboy(busboyOptions);
// 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

@@ -65,3 +57,3 @@ busboy.on('field', function(fieldname, val) {

res.writeHead(413, { Connection: 'close' });
res.end('File size limit has been reached');
res.end(options.responseOnLimit);
}

@@ -85,47 +77,5 @@ });

if (options.safeFileNames) {
let safeFileNameRegex = /[^\w-]/g;
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);
}
const newFile = fileFactory(
{
name: filename,
name: parseFileName(options, filename),
buffer,

@@ -132,0 +82,0 @@ tempFilePath: getFilePath(),

const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const {checkAndMakeDir} = require('./utilities.js');
const {
debugLog,
checkAndMakeDir,
getTempFilename
} = require('./utilities');
module.exports = function(options, fieldname, filename) {
const dir = path.normalize(options.tempFileDir || process.cwd() + '/tmp/');
const tempFilePath = path.join(dir, 'tmp' + Date.now());
const tempFilePath = path.join(dir, getTempFilename());

@@ -21,9 +25,3 @@ checkAndMakeDir({createParentPath: true}, tempFilePath);

fileSize += data.length;
if (options.debug) {
return console.log( // eslint-disable-line
`Uploaded ${data.length} bytes for `,
fieldname,
filename
);
}
debugLog(options, `Uploading ${fieldname} -> ${filename}, bytes: ${fileSize}`);
},

@@ -30,0 +28,0 @@ getFilePath: function(){

@@ -7,3 +7,36 @@ 'use strict';

//Parameters for safe file name parsing
const SAFE_FILE_NAME_REGEX = /[^\w-]/g;
const MAX_EXTENSION_LENGTH = 3;
//Parameters which used to generate unique temporary file names:
const TEMP_COUNTER_MAX = 65536;
const TEMP_PREFIX = 'tmp';
let tempCounter = 0;
/**
* Logs message to console if debug option set to true.
* @param {Object} options - options object.
* @param {String} msg - message to log.
* @return {Boolean}
*/
const debugLog = (options, msg) => {
options = options || {};
if (!options.debug) return false;
console.log(msg); // eslint-disable-line
return true;
};
/**
* Generates unique temporary file name like: tmp-5000-156788789789.
* @param prefix {String} - a prefix for generated unique file name.
*/
const getTempFilename = (prefix) => {
prefix = prefix || TEMP_PREFIX;
tempCounter ++;
if (tempCounter > TEMP_COUNTER_MAX) tempCounter = 1;
return `${prefix}-${tempCounter}-${Date.now()}`;
};
/**
* Returns true if argument is function.

@@ -14,2 +47,15 @@ */

/**
* Builds instance options from arguments objects.
* @returns {Object} - result options.
*/
const buildOptions = function(){
let result = {};
[...arguments].forEach(options => {
if (!options || typeof options !== 'object') return;
Object.keys(options).forEach(key => result[key] = options[key]);
});
return result;
};
/**
* Creates a folder for file specified in the path variable

@@ -83,7 +129,70 @@ * @param {Object} fileUploadOptions

/**
* Parses filename and extension and returns object {name, extension}.
* @param preserveExtension {Boolean, Integer} - true/false or number of characters for extension.
* @param fileName {String} - file name to parse.
* @returns {Object} - {name, extension}.
*/
const parseFileNameExtension = (preserveExtension, fileName) => {
let preserveExtensionLengh = parseInt(preserveExtension);
let result = {name: fileName, extension: ''};
if (!preserveExtension && preserveExtensionLengh !== 0){
return result;
}
// Define maximum extension length
let maxExtLength = isNaN(preserveExtensionLengh)
? MAX_EXTENSION_LENGTH
: Math.abs(preserveExtensionLengh);
let nameParts = fileName.split('.');
if (nameParts.length < 2) {
return result;
}
let extension = nameParts.pop();
if (
extension.length > maxExtLength &&
maxExtLength > 0
) {
nameParts[nameParts.length - 1] +=
'.' +
extension.substr(0, extension.length - maxExtLength);
extension = extension.substr(-maxExtLength);
}
result.extension = maxExtLength ? extension : '';
result.name = nameParts.join('.');
return result;
};
/**
* Parse file name and extension.
* @param opts {Object} - middleware options.
* @param fileName {String} - Uploaded file name.
* @returns {String}
*/
const parseFileName = (opts, fileName) => {
if (!opts.safeFileNames) {
return fileName;
}
// Set regular expression for the file name.
let safeNameRegex = typeof opts.safeFileNames === 'object' && opts.safeFileNames instanceof RegExp
? opts.safeFileNames
: SAFE_FILE_NAME_REGEX;
// Parse file name extension.
let {name, extension} = parseFileNameExtension(opts.preserveExtension, fileName);
if (extension.length) extension = '.' + extension.replace(safeNameRegex, '');
return name.replace(safeNameRegex, '').concat(extension);
};
module.exports = {
debugLog,
isFunc,
buildOptions,
checkAndMakeDir,
copyFile,
saveBufferToFile
saveBufferToFile,
parseFileName,
getTempFilename
};
{
"name": "express-fileupload",
"version": "1.1.3-alpha.1",
"version": "1.1.3-alpha.2",
"author": "Richard Girges <richardgirges@gmail.com>",

@@ -5,0 +5,0 @@ "description": "Simple express file upload middleware that wraps around Busboy",

@@ -10,5 +10,5 @@ # express-fileupload

# Version 1.1.1 Breaking Changes
Breaking change to `md5` handling.
md5 again returns a hash value instead of function which compute the hash.
md5 hashes now can be used with tempFiles.
Breaking change to `md5` handling:
* `md5` value contains md5 hash instead of a function to compute it.
* `md5` now can be used with `useTempFiles: true`.

@@ -45,2 +45,3 @@ # Version 1.0.0 Breaking Changes

* `req.files.foo.data`: A buffer representation of your file
* `req.files.foo.tempFilePath`: A path to the temporary file in case useTempFiles option was set to true.
* `req.files.foo.truncated`: A boolean that represents if the file is over the size limit

@@ -83,2 +84,3 @@ * `req.files.foo.size`: Uploaded size in bytes

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>truncate = 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.
useTempFiles | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>true</code></ul> | Will use temporary files at the specified tempDir for managing uploads rather than using buffers in memory. This avoids memory issues when uploading large files.

@@ -85,0 +87,0 @@ tempFileDir | <ul><li><code>String</code>&nbsp;**(path)**</li></ul> | Used with the <code>useTempFiles</code> option. Path to the directory where temp files will be stored during the upload process. Feel free to add trailing slash, but it is not necessary.

@@ -12,6 +12,10 @@ 'use strict';

const {
debugLog,
isFunc,
getTempFilename,
buildOptions,
checkAndMakeDir,
copyFile,
saveBufferToFile
saveBufferToFile,
parseFileName
} = require('../lib/utilities.js');

@@ -29,2 +33,20 @@

});
//debugLog tests
describe('Test debugLog function', () => {
let testMessage = 'Test message';
it('debugLog returns false if no options passed', () => {
assert.equal(debugLog(null, testMessage), false);
});
it('debugLog returns false if option debug is false', () => {
assert.equal(debugLog({debug: false}, testMessage), false);
});
it('debugLog returns true if option debug is true', () => {
assert.equal(debugLog({debug: true}, testMessage), true);
});
});
//isFunc tests

@@ -51,2 +73,108 @@ describe('Test isFunc function', () => {

});
//getTempFilename tests
describe('Test getTempFilename function', () => {
const nameRegexp = /tmp-\d{1,5}-\d{1,}/;
it('getTempFilename result matches regexp /tmp-d{1,5}-d{1,}/', () => {
let errCounter = 0;
let tempName = '';
for (var i = 0; i < 65537; i++) {
tempName = getTempFilename();
if (!nameRegexp.test(tempName)) errCounter ++;
}
assert.equal(errCounter, 0);
});
it('getTempFilename current and previous results are not equal', () => {
let errCounter = 0;
let tempName = '';
let previousName = '';
for (var i = 0; i < 65537; i++) {
previousName = tempName;
tempName = getTempFilename();
if (previousName === tempName) errCounter ++;
}
assert.equal(errCounter, 0);
});
});
//parseFileName
describe('Test parseFileName function', () => {
it('Does nothing to your filename when disabled.', () => {
const opts = {safeFileNames: false};
const name = 'my$Invalid#fileName.png123';
const expected = 'my$Invalid#fileName.png123';
let result = parseFileName(opts, name);
assert.equal(result, expected);
});
it(
'Strips away all non-alphanumeric characters (excluding hyphens/underscores) when enabled.',
() => {
const opts = {safeFileNames: true};
const name = 'my$Invalid#fileName.png123';
const expected = 'myInvalidfileNamepng123';
let result = parseFileName(opts, name);
assert.equal(result, expected);
});
it(
'Strips away all non-alphanumeric chars when preserveExtension: true for a name without dots',
() => {
const opts = {safeFileNames: true, preserveExtension: true};
const name = 'my$Invalid#fileName';
const expected = 'myInvalidfileName';
let result = parseFileName(opts, name);
assert.equal(result, expected);
});
it('Accepts a regex for stripping (decidedly) "invalid" characters from filename.', () => {
const opts = {safeFileNames: /[$#]/g};
const name = 'my$Invalid#fileName.png123';
const expected = 'myInvalidfileName.png123';
let result = parseFileName(opts, name);
assert.equal(result, expected);
});
it(
'Returns correct filename if name contains dots characters and preserveExtension: true.',
() => {
const opts = {safeFileNames: true, preserveExtension: true};
const name = 'basket.ball.png';
const expected = 'basketball.png';
let result = parseFileName(opts, name);
assert.equal(result, expected);
});
});
//buildOptions tests
describe('Test buildOptions function', () => {
const source = { option1: '1', option2: '2' };
const sourceAddon = { option3: '3'};
const expected = { option1: '1', option2: '2' };
const expectedAddon = { option1: '1', option2: '2', option3: '3'};
it('buildOptions returns and equal object to the object which was paased', () => {
let result = buildOptions(source);
assert.deepStrictEqual(result, source);
});
it('buildOptions doesnt add non object or null arguments to the result', () => {
let result = buildOptions(source, 2, '3', null);
assert.deepStrictEqual(result, expected);
});
it('buildOptions adds value to the result from the several source argumets', () => {
let result = buildOptions(source, sourceAddon);
assert.deepStrictEqual(result, expectedAddon);
});
});
//checkAndMakeDir tests

@@ -53,0 +181,0 @@ describe('Test checkAndMakeDir function', () => {

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