diff --git a/README.md b/README.md index 0f6e3a6..e87fbbe 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ Pass in non-Busboy options directly to the middleware. These are express-fileupl Option | Acceptable Values | Details --- | --- | --- safeFileNames | | 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.

**Example #1 (strip slashes from file names):** `app.use(fileUpload({ safeFileNames: /\\/g }))`
**Example #2:** `app.use(fileUpload({ safeFileNames: true }))` -preserveExtension | | Preserves filename extension when using 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.

**Example #1 (true):**
app.use(fileUpload({ safeFileNames: true, preserveExtension: true }));
*myFileName.ext* --> *myFileName.ext*

**Example #2 (max extension length 2, extension truncated):**
app.use(fileUpload({ safeFileNames: true, preserveExtension: 2 }));
*myFileName.ext* --> *myFileNamee.xt* +preserveExtension | | Preserves filename extension when using 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.

**Example #1 (true):**
app.use(fileUpload({ safeFileNames: true, preserveExtension: true }));
*myFileName.ext* --> *myFileName.ext*

**Example #2 (max extension length 2, extension shifted):**
app.use(fileUpload({ safeFileNames: true, preserveExtension: 2 }));
*myFileName.ext* --> *myFileNamee.xt* # Help Wanted Pull Requests are welcomed! diff --git a/package.json b/package.json index cb8731a..a620ca3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "express-fileupload", - "version": "0.1.2", + "version": "0.1.3", "author": "Richard Girges ", "description": "Simple express file upload middleware that wraps around Busboy", "main": "./lib/index", diff --git a/test/files/basket.ball.bp b/test/files/basket.ball.bp new file mode 100644 index 0000000..a2a1571 Binary files /dev/null and b/test/files/basket.ball.bp differ diff --git a/test/files/my$Invalid#fileName.png123 b/test/files/my$Invalid#fileName.png123 new file mode 100644 index 0000000..510b859 Binary files /dev/null and b/test/files/my$Invalid#fileName.png123 differ diff --git a/test/multipartFields.spec.js b/test/multipartFields.spec.js index 034164e..bbddfe6 100644 --- a/test/multipartFields.spec.js +++ b/test/multipartFields.spec.js @@ -2,7 +2,7 @@ const request = require('supertest'); const server = require('./server'); -const app = server.app; +const app = server.setup(); let mockUser = { firstName: 'Joe', diff --git a/test/multipartUploads.spec.js b/test/multipartUploads.spec.js index 0dc1e1c..4e69f5b 100644 --- a/test/multipartUploads.spec.js +++ b/test/multipartUploads.spec.js @@ -4,7 +4,7 @@ const fs = require('fs'); const path = require('path'); const request = require('supertest'); const server = require('./server'); -const app = server.app; +const app = server.setup(); const clearUploadsDir = server.clearUploadsDir; const fileDir = server.fileDir; const uploadDir = server.uploadDir; @@ -212,4 +212,4 @@ describe('Test Upload With Fields', function() { }); }); } -}); +}); \ No newline at end of file diff --git a/test/options.spec.js b/test/options.spec.js new file mode 100644 index 0000000..32e49c5 --- /dev/null +++ b/test/options.spec.js @@ -0,0 +1,151 @@ +const fs = require('fs'); +const path = require('path'); +const request = require('supertest'); +const server = require('./server'); +// const clearUploadsDir = server.clearUploadsDir; +const fileDir = server.fileDir; +const uploadDir = server.uploadDir; + +describe('SafeFileNames', function() { + it(`Does nothing to your filename when disabled.`, function(done) { + const app = server.setup({safeFileNames: false}); + + request(app) + .post('/upload/single') + .attach('testFile', path.join(fileDir, 'my$Invalid#fileName.png123')) + .expect(200) + .end(function(err, res) { + if (err) + return done(err); + + let uploadedFilePath = path.join(uploadDir, 'my$Invalid#fileName.png123'); + + fs.stat(uploadedFilePath, done); + }); + }); + + it(`Strips away all illegal characters (including spaces) when enabled.`, function(done) { + const app = server.setup({safeFileNames: true}); + + request(app) + .post('/upload/single') + .attach('testFile', path.join(fileDir, 'my$Invalid#fileName.png123')) + .expect(200) + .end(function(err, res) { + if (err) + return done(err); + + let uploadedFilePath = path.join(uploadDir, 'myInvalidfileNamepng123'); + + fs.stat(uploadedFilePath, done); + }); + }); + + it(`Respects a regex for stripping 'invalid' characters from filename.`, function(done) { + const app = server.setup({safeFileNames: /[\$#]/g}); + + request(app) + .post('/upload/single') + .attach('testFile', path.join(fileDir, 'my$Invalid#fileName.png123')) + .expect(200) + .end(function(err, res) { + if (err) + return done(err); + + let uploadedFilePath = path.join(uploadDir, 'myInvalidfileName.png123'); + + fs.stat(uploadedFilePath, done); + }); + }); +}); + +describe(`preserveExtension`, function() { + it(`Does nothing to your filename when disabled.`, function(done) { + const app = server.setup({safeFileNames: true, preserveExtension: false}); + + request(app) + .post('/upload/single') + .attach('testFile', path.join(fileDir, 'my$Invalid#fileName.png123')) + .expect(200) + .end(function(err, res) { + if (err) + return done(err); + + let uploadedFilePath = path.join(uploadDir, 'myInvalidfileNamepng123'); + + fs.stat(uploadedFilePath, done); + }); + }); + + it(`Shortens your extension to the default(3) when enabled, if the extension found is larger`, + function(done) { + const app = server.setup({safeFileNames: true, preserveExtension: true}); + + request(app) + .post('/upload/single') + .attach('testFile', path.join(fileDir, 'my$Invalid#fileName.png123')) + .expect(200) + .end(function(err, res) { + if (err) + return done(err); + + let uploadedFilePath = path.join(uploadDir, 'myInvalidfileNamepng.123'); + + fs.stat(uploadedFilePath, done); + }); + }); + + it(`Leaves your extension alone when enabled, if the extension found is <= default(3) length`, + function(done) { + const app = server.setup({safeFileNames: true, preserveExtension: true}); + + request(app) + .post('/upload/single') + .attach('testFile', path.join(fileDir, 'car.png')) + .expect(200) + .end(function(err, res) { + if (err) + return done(err); + + let uploadedFilePath = path.join(uploadDir, 'car.png'); + + fs.stat(uploadedFilePath, done); + }); + }); + + it(`Leaves your extension alone when set to a number >= the extension length.`, + function(done) { + const app = server.setup({safeFileNames: true, preserveExtension: 7}); + + request(app) + .post('/upload/single') + .attach('testFile', path.join(fileDir, 'my$Invalid#fileName.png123')) + .expect(200) + .end(function(err, res) { + if (err) + return done(err); + + let uploadedFilePath = path.join(uploadDir, 'myInvalidfileName.png123'); + + fs.stat(uploadedFilePath, done); + }); + }); + + it(`Only considers the last dotted part the extension.`, + function(done) { + const app = server.setup({safeFileNames: true, preserveExtension: true}); + + request(app) + .post('/upload/single') + .attach('testFile', path.join(fileDir, 'basket.ball.bp')) + .expect(200) + .end(function(err, res) { + if (err) + return done(err); + + let uploadedFilePath = path.join(uploadDir, 'basketball.bp'); + + fs.stat(uploadedFilePath, done); + }); + }); +}); diff --git a/test/server.js b/test/server.js index 41a80b7..7d014fe 100644 --- a/test/server.js +++ b/test/server.js @@ -1,14 +1,8 @@ 'use strict'; - -const fs = require('fs-extra'); const path = require('path'); -const express = require('express'); -const expressFileupload = require('../lib/index'); - const fileDir = path.join(__dirname, 'files'); const uploadDir = path.join(__dirname, 'uploads'); - -const app = express(); +const fs = require('fs-extra'); const clearUploadsDir = function() { if (!fs.existsSync(uploadDir)) { @@ -18,157 +12,166 @@ const clearUploadsDir = function() { } }; -app.use(expressFileupload()); +var setup = function(fileUploadOptions) { + const express = require('express'); + const expressFileupload = require('../lib/index'); -app.all('/upload/single', function(req, res) { - if (!req.files) - return res.status(400).send('No files were uploaded.'); + const app = express(); - let testFile = req.files.testFile; - let uploadPath = path.join(uploadDir, testFile.name); + fileUploadOptions = fileUploadOptions || {}; + app.use(expressFileupload(fileUploadOptions)); - testFile.mv(uploadPath, function(err) { - if (err) - return res.status(500).send(err); + app.all('/upload/single', function(req, res) { + if (!req.files) + return res.status(400).send('No files were uploaded.'); + + let testFile = req.files.testFile; + let uploadPath = path.join(uploadDir, testFile.name); + + testFile.mv(uploadPath, function (err) { + if (err) + return res.status(500).send(err); - res.send('File uploaded to ' + uploadPath); + res.send('File uploaded to ' + uploadPath); + }); }); -}); -app.all('/upload/single/withfields', function(req, res) { - if (!req.files) - return res.status(400).send('No files were uploaded.'); + app.all('/upload/single/withfields', function(req, res) { + if (!req.files) + return res.status(400).send('No files were uploaded.'); - if (!req.body) - return res.status(400).send('No request body found'); + if (!req.body) + return res.status(400).send('No request body found'); - if (!req.body.firstName || !req.body.firstName.trim()) - return res.status(400).send('Invalid first name'); + if (!req.body.firstName || !req.body.firstName.trim()) + return res.status(400).send('Invalid first name'); - if (!req.body.lastName || !req.body.lastName.trim()) - return res.status(400).send('Invalid last name'); + if (!req.body.lastName || !req.body.lastName.trim()) + return res.status(400).send('Invalid last name'); - if (!req.body.email || !req.body.email.trim()) - return res.status(400).send('Invalid email'); + if (!req.body.email || !req.body.email.trim()) + return res.status(400).send('Invalid email'); - let testFile = req.files.testFile; - let uploadPath = path.join(uploadDir, testFile.name); + let testFile = req.files.testFile; + let uploadPath = path.join(uploadDir, testFile.name); - testFile.mv(uploadPath, function(err) { - if (err) - return res.status(500).send(err); + testFile.mv(uploadPath, function (err) { + if (err) + return res.status(500).send(err); - res.json({ - firstName: req.body.firstName, - lastName: req.body.lastName, - email: req.body.email + res.json({ + firstName: req.body.firstName, + lastName: req.body.lastName, + email: req.body.email + }); }); }); -}); -app.all('/upload/multiple', function(req, res) { - if (!req.files) - return res.status(400).send('No files were uploaded.'); + app.all('/upload/multiple', function(req, res) { + if (!req.files) + return res.status(400).send('No files were uploaded.'); - let testFile1 = req.files.testFile1; - let testFile2 = req.files.testFile2; - let testFile3 = req.files.testFile3; - let uploadPath1 = path.join(uploadDir, testFile1.name); - let uploadPath2 = path.join(uploadDir, testFile2.name); - let uploadPath3 = path.join(uploadDir, testFile3.name); + let testFile1 = req.files.testFile1; + let testFile2 = req.files.testFile2; + let testFile3 = req.files.testFile3; + let uploadPath1 = path.join(uploadDir, testFile1.name); + let uploadPath2 = path.join(uploadDir, testFile2.name); + let uploadPath3 = path.join(uploadDir, testFile3.name); - if (!testFile1) - return res.status(400).send('testFile1 was not uploaded'); + if (!testFile1) + return res.status(400).send('testFile1 was not uploaded'); - if (!testFile2) - return res.status(400).send('testFile2 was not uploaded'); + if (!testFile2) + return res.status(400).send('testFile2 was not uploaded'); - if (!testFile3) - return res.status(400).send('testFile3 was not uploaded'); + if (!testFile3) + return res.status(400).send('testFile3 was not uploaded'); - testFile1.mv(uploadPath1, function(err) { - if (err) - return res.status(500).send(err); - - testFile2.mv(uploadPath2, function(err) { + testFile1.mv(uploadPath1, function (err) { if (err) return res.status(500).send(err); - testFile3.mv(uploadPath3, function(err) { + testFile2.mv(uploadPath2, function (err) { if (err) return res.status(500).send(err); - res.send('Files uploaded to ' + uploadDir); + testFile3.mv(uploadPath3, function (err) { + if (err) + return res.status(500).send(err); + + res.send('Files uploaded to ' + uploadDir); + }); }); }); }); -}); -app.all('/upload/array', function(req, res) { - if (!req.files) - return res.status(400).send('No files were uploaded.'); + app.all('/upload/array', function(req, res) { + if (!req.files) + return res.status(400).send('No files were uploaded.'); - let testFiles = req.files.testFiles; + let testFiles = req.files.testFiles; - if (!testFiles) - return res.status(400).send('No files were uploaded'); + if (!testFiles) + return res.status(400).send('No files were uploaded'); - if (!Array.isArray(testFiles)) - return res.status(400).send('Files were not uploaded as an array'); + if (!Array.isArray(testFiles)) + return res.status(400).send('Files were not uploaded as an array'); - if (!testFiles.length) - return res.status(400).send('Files array is empty'); + if (!testFiles.length) + return res.status(400).send('Files array is empty'); - let filesUploaded = 0; - for (let i = 0; i < testFiles.length; i++) { - let uploadPath = path.join(uploadDir, testFiles[i].name); + let filesUploaded = 0; + for (let i = 0; i < testFiles.length; i++) { + let uploadPath = path.join(uploadDir, testFiles[i].name); - testFiles[i].mv(uploadPath, function(err) { - if (err) - return res.status(500).send(err); + testFiles[i].mv(uploadPath, function (err) { + if (err) + return res.status(500).send(err); - if (++filesUploaded === testFiles.length) - res.send('File uploaded to ' + uploadPath); - }); - } -}); + if (++filesUploaded === testFiles.length) + res.send('File uploaded to ' + uploadPath); + }); + } + }); -app.all('/fields/user', function(req, res) { - if (!req.body) - return res.status(400).send('No request body found'); + app.all('/fields/user', function(req, res) { + if (!req.body) + return res.status(400).send('No request body found'); - if (!req.body.firstName || !req.body.firstName.trim()) - return res.status(400).send('Invalid first name'); + if (!req.body.firstName || !req.body.firstName.trim()) + return res.status(400).send('Invalid first name'); - if (!req.body.lastName || !req.body.lastName.trim()) - return res.status(400).send('Invalid last name'); + if (!req.body.lastName || !req.body.lastName.trim()) + return res.status(400).send('Invalid last name'); - if (!req.body.email || !req.body.email.trim()) - return res.status(400).send('Invalid email'); + if (!req.body.email || !req.body.email.trim()) + return res.status(400).send('Invalid email'); - res.json({ - firstName: req.body.firstName, - lastName: req.body.lastName, - email: req.body.email + res.json({ + firstName: req.body.firstName, + lastName: req.body.lastName, + email: req.body.email + }); }); -}); -app.all('/fields/array', function(req, res) { - if (!req.body) - return res.status(400).send('No request body found'); + app.all('/fields/array', function(req, res) { + if (!req.body) + return res.status(400).send('No request body found'); - if (!req.body.testField) - return res.status(400).send('Invalid field'); + if (!req.body.testField) + return res.status(400).send('Invalid field'); - if (!Array.isArray(req.body.testField)) - return res.status(400).send('Field is not an array'); + if (!Array.isArray(req.body.testField)) + return res.status(400).send('Field is not an array'); - res.json(req.body.testField); -}); + res.json(req.body.testField); + }); + return app; +}; module.exports = { - app, + setup, fileDir, uploadDir, clearUploadsDir