From 777a04f5c13179bad82937bed6d9251b3757a39f Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Thu, 18 Feb 2021 18:07:29 +0100 Subject: [PATCH 01/35] feat: allow to use filename option without keepExtension --- src/Formidable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Formidable.js b/src/Formidable.js index 7021b9bc..cafba35e 100644 --- a/src/Formidable.js +++ b/src/Formidable.js @@ -541,7 +541,7 @@ class IncomingForm extends EventEmitter { _setUpRename() { const hasRename = typeof this.options.filename === 'function'; - if (this.options.keepExtensions === true && hasRename) { + if (hasRename) { this._rename = (part) => { const resultFilepath = this.options.filename.call(this, part, this); From 5531a57cc0baf7c82d87a9a855d5d16175f76f11 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Thu, 18 Feb 2021 18:15:50 +0100 Subject: [PATCH 02/35] feat: can use uploadDir option and filename together --- src/Formidable.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Formidable.js b/src/Formidable.js index cafba35e..13fda712 100644 --- a/src/Formidable.js +++ b/src/Formidable.js @@ -528,14 +528,15 @@ class IncomingForm extends EventEmitter { } _uploadPath(part, fp) { - const name = fp || `${this.uploadDir}${path.sep}${toHexoId()}`; + const name = fp || toHexoId(); + const filePath = `${this.uploadDir}${path.sep}${name}`; if (part && this.options.keepExtensions) { const filename = typeof part === 'string' ? part : part.filename; - return `${name}${this._getExtension(filename)}`; + return `${filePath}${this._getExtension(filename)}`; } - return name; + return filePath; } _setUpRename() { From 33dd6c432f8173f14ca50efc14578a2d3fe996a2 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Thu, 18 Feb 2021 18:43:37 +0100 Subject: [PATCH 03/35] refactor: rename --- src/Formidable.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Formidable.js b/src/Formidable.js index 13fda712..5077d444 100644 --- a/src/Formidable.js +++ b/src/Formidable.js @@ -317,7 +317,7 @@ class IncomingForm extends EventEmitter { this._flushing += 1; const file = this._newFile({ - path: this._rename(part), + path: this._getFinalPath(part), filename: part.filename, mime: part.mime, }); @@ -543,13 +543,13 @@ class IncomingForm extends EventEmitter { const hasRename = typeof this.options.filename === 'function'; if (hasRename) { - this._rename = (part) => { + this._getFinalPath = (part) => { const resultFilepath = this.options.filename.call(this, part, this); return this._uploadPath(part, resultFilepath); }; } else { - this._rename = (part) => this._uploadPath(part); + this._getFinalPath = (part) => this._uploadPath(part); } } From ebb7a18ee68adf787fc1926caa7867844c9ecc9a Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Thu, 18 Feb 2021 19:10:54 +0100 Subject: [PATCH 04/35] feat: by default prevent directory traversal attacks filename takes ext part, if filename returns extension it will not be duplicated anymore --- src/Formidable.js | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/Formidable.js b/src/Formidable.js index 5077d444..49cc8937 100644 --- a/src/Formidable.js +++ b/src/Formidable.js @@ -27,6 +27,7 @@ const DEFAULT_OPTIONS = { multiples: false, enabledPlugins: ['octetstream', 'querystring', 'multipart', 'json'], fileWriteStreamHandler: null, + defaultInvalidName: 'invalid-name', }; const PersistentFile = require('./PersistentFile'); @@ -47,7 +48,7 @@ class IncomingForm extends EventEmitter { this.options = { ...DEFAULT_OPTIONS, ...options }; - const dir = this.options.uploadDir || this.options.uploaddir || os.tmpdir(); + const dir = path.resolve(this.options.uploadDir || this.options.uploaddir || os.tmpdir()); this.uploaddir = dir; this.uploadDir = dir; @@ -529,24 +530,41 @@ class IncomingForm extends EventEmitter { _uploadPath(part, fp) { const name = fp || toHexoId(); - const filePath = `${this.uploadDir}${path.sep}${name}`; if (part && this.options.keepExtensions) { const filename = typeof part === 'string' ? part : part.filename; - return `${filePath}${this._getExtension(filename)}`; + return `${name}${this._getExtension(filename)}`; } - return filePath; + return this._joinDirectoryName(name); + } + + _joinDirectoryName(name) { + const newPath = path.join(this.uploadDir, name); + + // prevent directory traversal attacks + if (!newPath.startsWith(targetPath)) { + return path.join(this.uploadDir, this.options.defaultInvalidName) + } + + return newPath } _setUpRename() { const hasRename = typeof this.options.filename === 'function'; - if (hasRename) { this._getFinalPath = (part) => { - const resultFilepath = this.options.filename.call(this, part, this); + let ext = ""; + let name = this.options.defaultInvalidName; + if (part.filename) { // can be null + ({ext, name} = path.parse(part.filename)); + if (!this.options.keepExtensions) { + ext = "" + } + } + const resultFilepath = this.options.filename.call(this, name, ext, part, this); - return this._uploadPath(part, resultFilepath); + return this._joinDirectoryName(resultFilepath); }; } else { this._getFinalPath = (part) => this._uploadPath(part); From cb3d35914cc8e31be5b9bdbfd4a35ed9259c4e3b Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Thu, 18 Feb 2021 20:45:53 +0100 Subject: [PATCH 05/35] refactor: make octet stream less divergent feat: store newName --- README.md | 2 +- src/Formidable.js | 22 ++++++++++++---------- src/plugins/octetstream.js | 9 ++++++++- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index a73d537a..552a8e8d 100644 --- a/README.md +++ b/README.md @@ -634,7 +634,7 @@ file system. ```js form.on('fileBegin', (formName, file) => { // accessible here - // formName the name in the form () + // formName the name in the form () or http filename for octetstream // file.name http filename or null if there was a parsing error // file.path default pathnme as per options.uploadDir and options.filename // file.path = CUSTOM_PATH // to change the final path diff --git a/src/Formidable.js b/src/Formidable.js index 49cc8937..58a7212d 100644 --- a/src/Formidable.js +++ b/src/Formidable.js @@ -317,8 +317,11 @@ class IncomingForm extends EventEmitter { this._flushing += 1; + const newName = this._getNewName(part) + const finalPath = this._joinDirectoryName(newName); const file = this._newFile({ - path: this._getFinalPath(part), + newName: newName, + path: finalPath, filename: part.filename, mime: part.mime, }); @@ -528,15 +531,15 @@ class IncomingForm extends EventEmitter { return basename.slice(firstDot, lastDot) + extname; } - _uploadPath(part, fp) { - const name = fp || toHexoId(); + _uploadPath(part) { + const name = toHexoId(); if (part && this.options.keepExtensions) { const filename = typeof part === 'string' ? part : part.filename; return `${name}${this._getExtension(filename)}`; } - return this._joinDirectoryName(name); + return name; } _joinDirectoryName(name) { @@ -544,16 +547,16 @@ class IncomingForm extends EventEmitter { // prevent directory traversal attacks if (!newPath.startsWith(targetPath)) { - return path.join(this.uploadDir, this.options.defaultInvalidName) + return path.join(this.uploadDir, this.options.defaultInvalidName); } - return newPath + return newPath; } _setUpRename() { const hasRename = typeof this.options.filename === 'function'; if (hasRename) { - this._getFinalPath = (part) => { + this._getNewName = (part) => { let ext = ""; let name = this.options.defaultInvalidName; if (part.filename) { // can be null @@ -562,12 +565,11 @@ class IncomingForm extends EventEmitter { ext = "" } } - const resultFilepath = this.options.filename.call(this, name, ext, part, this); + return this.options.filename.call(this, name, ext, part, this); - return this._joinDirectoryName(resultFilepath); }; } else { - this._getFinalPath = (part) => this._uploadPath(part); + this._getNewName = (part) => this._uploadPath(part); } } diff --git a/src/plugins/octetstream.js b/src/plugins/octetstream.js index 4ef2cfb1..ba9083b1 100644 --- a/src/plugins/octetstream.js +++ b/src/plugins/octetstream.js @@ -27,8 +27,15 @@ function init(_self, _opts) { const filename = this.headers['x-file-name']; const mime = this.headers['content-type']; + const thisPart = { + filename, + mime + }; + const newName = this._getNewName(part) + const finalPath = this._joinDirectoryName(newName); const file = this._newFile({ - path: this._uploadPath(filename), + newName: newName, + path: finalPath, filename, mime, }); From aafebea337683cd99d975d8e84bf832637f44a4e Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Thu, 18 Feb 2021 20:52:18 +0100 Subject: [PATCH 06/35] refactor: pass through, avoid renaming variable names --- src/Formidable.js | 12 ++++++------ src/PersistentFile.js | 10 +++++----- src/VolatileFile.js | 12 ++++++------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Formidable.js b/src/Formidable.js index 58a7212d..79d6fab4 100644 --- a/src/Formidable.js +++ b/src/Formidable.js @@ -481,18 +481,18 @@ class IncomingForm extends EventEmitter { return new MultipartParser(this.options); } - _newFile({ path: filePath, filename: name, mime: type }) { + _newFile({ path, filename, mime }) { return this.options.fileWriteStreamHandler ? new VolatileFile({ - name, - type, + filename, + mime, createFileWriteStream: this.options.fileWriteStreamHandler, hash: this.options.hash, }) : new PersistentFile({ - path: filePath, - name, - type, + path, + filename, + mime, hash: this.options.hash, }); } diff --git a/src/PersistentFile.js b/src/PersistentFile.js index 3ff35853..1dcaae14 100644 --- a/src/PersistentFile.js +++ b/src/PersistentFile.js @@ -12,8 +12,8 @@ class PersistentFile extends EventEmitter { this.size = 0; this.path = null; - this.name = null; - this.type = null; + this.filename = null; + this.mime = null; this.hash = null; this.lastModifiedDate = null; @@ -42,8 +42,8 @@ class PersistentFile extends EventEmitter { const json = { size: this.size, path: this.path, - name: this.name, - type: this.type, + name: this.filename, + type: this.mime, mtime: this.lastModifiedDate, length: this.length, filename: this.filename, @@ -56,7 +56,7 @@ class PersistentFile extends EventEmitter { } toString() { - return `PersistentFile: ${this.name}, Path: ${this.path}`; + return `PersistentFile: ${this.filename}, Path: ${this.path}`; } write(buffer, cb) { diff --git a/src/VolatileFile.js b/src/VolatileFile.js index e0ef454b..7e075679 100644 --- a/src/VolatileFile.js +++ b/src/VolatileFile.js @@ -10,8 +10,8 @@ class VolatileFile extends EventEmitter { super(); this.size = 0; - this.name = null; - this.type = null; + this.filename = null; + this.mime = null; this.hash = null; this._writeStream = null; @@ -29,7 +29,7 @@ class VolatileFile extends EventEmitter { } open() { - this._writeStream = this.createFileWriteStream(this.name); + this._writeStream = this.createFileWriteStream(this.filename); this._writeStream.on('error', (err) => { this.emit('error', err); }); @@ -42,8 +42,8 @@ class VolatileFile extends EventEmitter { toJSON() { const json = { size: this.size, - name: this.name, - type: this.type, + name: this.filename, + type: this.mime, length: this.length, filename: this.filename, mime: this.mime, @@ -55,7 +55,7 @@ class VolatileFile extends EventEmitter { } toString() { - return `VolatileFile: ${this.name}`; + return `VolatileFile: ${this.filename}`; } write(buffer, cb) { From 0a5bad465a2ea6bfbad2fef00a2df96d2f956992 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Thu, 18 Feb 2021 20:55:58 +0100 Subject: [PATCH 07/35] refactor: prefer Object.assign --- src/PersistentFile.js | 13 ++----------- src/VolatileFile.js | 11 ++--------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/src/PersistentFile.js b/src/PersistentFile.js index 1dcaae14..8595b5ac 100644 --- a/src/PersistentFile.js +++ b/src/PersistentFile.js @@ -9,21 +9,12 @@ const { EventEmitter } = require('events'); class PersistentFile extends EventEmitter { constructor(properties) { super(); + + Object.assign(this, properties); this.size = 0; - this.path = null; - this.filename = null; - this.mime = null; - this.hash = null; - this.lastModifiedDate = null; - this._writeStream = null; - // eslint-disable-next-line guard-for-in, no-restricted-syntax - for (const key in properties) { - this[key] = properties[key]; - } - if (typeof this.hash === 'string') { this.hash = crypto.createHash(properties.hash); } else { diff --git a/src/VolatileFile.js b/src/VolatileFile.js index 7e075679..e4f68a34 100644 --- a/src/VolatileFile.js +++ b/src/VolatileFile.js @@ -9,18 +9,11 @@ class VolatileFile extends EventEmitter { constructor(properties) { super(); - this.size = 0; - this.filename = null; - this.mime = null; - this.hash = null; + Object.assign(this, properties); + this.size = 0; this._writeStream = null; - // eslint-disable-next-line guard-for-in, no-restricted-syntax - for (const key in properties) { - this[key] = properties[key]; - } - if (typeof this.hash === 'string') { this.hash = crypto.createHash(properties.hash); } else { From 032e66b961e0a55d1768ac62286b21b746275d70 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Thu, 18 Feb 2021 20:58:46 +0100 Subject: [PATCH 08/35] feat: pass newName --- src/Formidable.js | 7 +++++-- src/plugins/octetstream.js | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Formidable.js b/src/Formidable.js index 79d6fab4..8cab2351 100644 --- a/src/Formidable.js +++ b/src/Formidable.js @@ -320,7 +320,7 @@ class IncomingForm extends EventEmitter { const newName = this._getNewName(part) const finalPath = this._joinDirectoryName(newName); const file = this._newFile({ - newName: newName, + newName, path: finalPath, filename: part.filename, mime: part.mime, @@ -481,15 +481,18 @@ class IncomingForm extends EventEmitter { return new MultipartParser(this.options); } - _newFile({ path, filename, mime }) { + _newFile({ path, filename, mime, newName }) { return this.options.fileWriteStreamHandler ? new VolatileFile({ + newName, + path, filename, mime, createFileWriteStream: this.options.fileWriteStreamHandler, hash: this.options.hash, }) : new PersistentFile({ + newName, path, filename, mime, diff --git a/src/plugins/octetstream.js b/src/plugins/octetstream.js index ba9083b1..69a2379a 100644 --- a/src/plugins/octetstream.js +++ b/src/plugins/octetstream.js @@ -34,7 +34,7 @@ function init(_self, _opts) { const newName = this._getNewName(part) const finalPath = this._joinDirectoryName(newName); const file = this._newFile({ - newName: newName, + newName, path: finalPath, filename, mime, From ad1664ab6f3bbce5421d2093330d934d7b98d555 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Thu, 18 Feb 2021 21:01:02 +0100 Subject: [PATCH 09/35] feat: give createFileWriteStream more, including newName --- src/VolatileFile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VolatileFile.js b/src/VolatileFile.js index e4f68a34..2dcf4497 100644 --- a/src/VolatileFile.js +++ b/src/VolatileFile.js @@ -22,7 +22,7 @@ class VolatileFile extends EventEmitter { } open() { - this._writeStream = this.createFileWriteStream(this.filename); + this._writeStream = this.createFileWriteStream(this); this._writeStream.on('error', (err) => { this.emit('error', err); }); From c178080f5177550e35e10c372dc51ea7376dde68 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Thu, 18 Feb 2021 22:19:09 +0100 Subject: [PATCH 10/35] docs: update --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 552a8e8d..642553da 100644 --- a/README.md +++ b/README.md @@ -354,6 +354,10 @@ See it's defaults in [src/Formidable.js DEFAULT_OPTIONS](./src/Formidable.js) files for inputs which submit multiple files using the HTML5 `multiple` attribute. Also, the `fields` argument will contain arrays of values for fields that have names ending with '[]'. +- `options.filename` **{function}** - default `undefined` Use it to control + newName. Must return a string. Will be joined with options.uploadDir. + +#### `options.filename` **{function}** function (name, ext, part, form) -> string _**Note:** If this size of combined fields, or size of some file is exceeded, an `'error'` event is fired._ @@ -567,7 +571,7 @@ form.onPart = function (part) { // let formidable handle only non-file parts if (part.filename === '' || !part.mime) { // used internally, please do not override! - form.handlePart(part); + form._handlePart(part); } }; ``` @@ -636,6 +640,7 @@ form.on('fileBegin', (formName, file) => { // accessible here // formName the name in the form () or http filename for octetstream // file.name http filename or null if there was a parsing error + // file.newName generated hexoid or what options.filename returned // file.path default pathnme as per options.uploadDir and options.filename // file.path = CUSTOM_PATH // to change the final path }); From f600f327c50fb2a26ab701d78c22ad3b3d9883f3 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Thu, 18 Feb 2021 22:19:23 +0100 Subject: [PATCH 11/35] docs: update examples --- examples/log-file-content-to-console.js | 2 +- examples/store-files-on-s3.js | 4 ++-- examples/with-http.js | 24 ++++++++++++++++-------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/examples/log-file-content-to-console.js b/examples/log-file-content-to-console.js index 48b7d15a..cbb33008 100644 --- a/examples/log-file-content-to-console.js +++ b/examples/log-file-content-to-console.js @@ -8,7 +8,7 @@ const server = http.createServer((req, res) => { if (req.url === '/api/upload' && req.method.toLowerCase() === 'post') { // parse a file upload const form = formidable({ - fileWriteStreamHandler: () => { + fileWriteStreamHandler: (/* file */) => { const writable = Writable(); // eslint-disable-next-line no-underscore-dangle writable._write = (chunk, enc, next) => { diff --git a/examples/store-files-on-s3.js b/examples/store-files-on-s3.js index f5a2f453..b5fd8869 100644 --- a/examples/store-files-on-s3.js +++ b/examples/store-files-on-s3.js @@ -15,12 +15,12 @@ const s3Client = new AWS.S3({ }, }); -const uploadStream = (filename) => { +const uploadStream = (file) => { const pass = PassThrough(); s3Client.upload( { Bucket: 'demo-bucket', - Key: filename, + Key: file.newName, Body: pass, }, (err, data) => { diff --git a/examples/with-http.js b/examples/with-http.js index e4c7a88e..5a78a722 100644 --- a/examples/with-http.js +++ b/examples/with-http.js @@ -6,15 +6,23 @@ const formidable = require('../src/index'); const server = http.createServer((req, res) => { if (req.url === '/api/upload' && req.method.toLowerCase() === 'post') { // parse a file upload - const form = formidable({ multiples: true }); - - form.on('fileBegin', (formName, file) => { - if (file.name === null) { - // todo change lint rules because that is what we recommend - // eslint-disable-next-line - file.name = 'invalid-characters'; - } + const form = formidable({ + multiples: true, + uploadDir: `uploads`, + keepExtensions: true, + filename: function (name, ext, part, form) { + /* name basename of the http filename + ext with the dot ".txt" only if keepExtension is true + */ + // slugify to avoid invalid filenames + // substr to define a maximum length + // return `${slugify(name).${slugify(ext, separator: '')}`.substr(0, 100); + return 'yo.txt'; // or completly different name + // return 'z/yo.txt'; // subdirectory + }, }); + + form.parse(req, (err, fields, files) => { if (err) { console.error(err); From 3ca6382df08d5218b9a80d4829088f494af58652 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Thu, 18 Feb 2021 22:19:54 +0100 Subject: [PATCH 12/35] fix: fix missing variables --- src/Formidable.js | 2 +- src/plugins/octetstream.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Formidable.js b/src/Formidable.js index 8cab2351..350273d2 100644 --- a/src/Formidable.js +++ b/src/Formidable.js @@ -549,7 +549,7 @@ class IncomingForm extends EventEmitter { const newPath = path.join(this.uploadDir, name); // prevent directory traversal attacks - if (!newPath.startsWith(targetPath)) { + if (!newPath.startsWith(this.uploadDir)) { return path.join(this.uploadDir, this.options.defaultInvalidName); } diff --git a/src/plugins/octetstream.js b/src/plugins/octetstream.js index 69a2379a..529b7848 100644 --- a/src/plugins/octetstream.js +++ b/src/plugins/octetstream.js @@ -31,7 +31,7 @@ function init(_self, _opts) { filename, mime }; - const newName = this._getNewName(part) + const newName = this._getNewName(thisPart) const finalPath = this._joinDirectoryName(newName); const file = this._newFile({ newName, From df66b4fedffc8f475bb82ff08a5046d04a6445b8 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Thu, 18 Feb 2021 22:20:20 +0100 Subject: [PATCH 13/35] feat: remove duplicate fix tests --- src/PersistentFile.js | 5 ++--- src/VolatileFile.js | 3 +-- .../file-write-stream-handler-option.test.js | 5 ++--- test/integration/fixtures.test.js | 2 +- test/integration/store-files-option.test.js | 2 +- test/unit/persistent-file.test.js | 5 ----- test/unit/volatile-file.test.js | 11 +---------- 7 files changed, 8 insertions(+), 25 deletions(-) diff --git a/src/PersistentFile.js b/src/PersistentFile.js index 8595b5ac..63f0511b 100644 --- a/src/PersistentFile.js +++ b/src/PersistentFile.js @@ -33,12 +33,11 @@ class PersistentFile extends EventEmitter { const json = { size: this.size, path: this.path, - name: this.filename, - type: this.mime, + newName: this.newName, + mime: this.mime, mtime: this.lastModifiedDate, length: this.length, filename: this.filename, - mime: this.mime, }; if (this.hash && this.hash !== '') { json.hash = this.hash; diff --git a/src/VolatileFile.js b/src/VolatileFile.js index 2dcf4497..e91a0463 100644 --- a/src/VolatileFile.js +++ b/src/VolatileFile.js @@ -35,8 +35,7 @@ class VolatileFile extends EventEmitter { toJSON() { const json = { size: this.size, - name: this.filename, - type: this.mime, + newName: this.newName, length: this.length, filename: this.filename, mime: this.mime, diff --git a/test/integration/file-write-stream-handler-option.test.js b/test/integration/file-write-stream-handler-option.test.js index 03a54544..c1ec83eb 100644 --- a/test/integration/file-write-stream-handler-option.test.js +++ b/test/integration/file-write-stream-handler-option.test.js @@ -38,8 +38,7 @@ test('file write stream handler', (done) => { createDirs([DEFAULT_UPLOAD_DIR, CUSTOM_UPLOAD_DIR]); const form = formidable({ uploadDir: DEFAULT_UPLOAD_DIR, - fileWriteStreamHandler: () => - fs.createWriteStream(CUSTOM_UPLOAD_FILE_PATH), + fileWriteStreamHandler: () => fs.createWriteStream(CUSTOM_UPLOAD_FILE_PATH) }); form.parse(req, (err, fields, files) => { @@ -47,7 +46,7 @@ test('file write stream handler', (done) => { const { file } = files; assert.strictEqual(file.size, 301); - assert.ok(file.path === undefined); + assert.strictEqual(typeof file.path, 'string'); const dirFiles = fs.readdirSync(DEFAULT_UPLOAD_DIR); assert.ok(dirFiles.length === 0); diff --git a/test/integration/fixtures.test.js b/test/integration/fixtures.test.js index 46275e74..5fa3d95b 100644 --- a/test/integration/fixtures.test.js +++ b/test/integration/fixtures.test.js @@ -69,7 +69,7 @@ test('fixtures', (done) => { if (parsedPart.type === 'file') { const file = parsedPart.value; - assert.strictEqual(file.name, expectedPart.filename); + assert.strictEqual(file.filename, expectedPart.filename); if (expectedPart.sha1) { assert.strictEqual( diff --git a/test/integration/store-files-option.test.js b/test/integration/store-files-option.test.js index e1940c6e..5c7d073a 100644 --- a/test/integration/store-files-option.test.js +++ b/test/integration/store-files-option.test.js @@ -36,7 +36,7 @@ test('store files option', (done) => { const { file } = files; assert.strictEqual(file.size, 301); - assert.ok(file.path === undefined); + assert.strictEqual(typeof file.path, 'string'); const uploadedFileStats = fs.statSync(CUSTOM_UPLOAD_FILE_PATH); assert.ok(uploadedFileStats.size === file.size); diff --git a/test/unit/persistent-file.test.js b/test/unit/persistent-file.test.js index 1129f035..5fd65876 100644 --- a/test/unit/persistent-file.test.js +++ b/test/unit/persistent-file.test.js @@ -15,14 +15,9 @@ const file = new PersistentFile({ test('PersistentFile#toJSON()', () => { const obj = file.toJSON(); - const len = Object.keys(obj).length; - expect(1024).toBe(obj.size); expect('/tmp/cat.png').toBe(obj.path); - expect('cat.png').toBe(obj.name); - expect('image/png').toBe(obj.type); expect('image/png').toBe(obj.mime); expect('cat.png').toBe(obj.filename); expect(now).toBe(obj.mtime); - expect(len).toBe(8); }); diff --git a/test/unit/volatile-file.test.js b/test/unit/volatile-file.test.js index 8ffe600f..d8499000 100644 --- a/test/unit/volatile-file.test.js +++ b/test/unit/volatile-file.test.js @@ -17,9 +17,7 @@ describe('VolatileFile', () => { writeStreamMock = jest.fn(() => writeStreamInstanceMock); file = new VolatileFile({ - size: 1024, name: 'cat.png', - type: 'image/png', filename: 'cat.png', mime: 'image/png', createFileWriteStream: writeStreamMock, @@ -37,7 +35,7 @@ describe('VolatileFile', () => { file.emit('error', error); - expect(writeStreamMock).toBeCalledWith('cat.png'); + expect(writeStreamMock).toBeCalled(); expect(writeStreamInstanceMock.on).toBeCalledWith( 'error', expect.any(Function), @@ -48,12 +46,8 @@ describe('VolatileFile', () => { const obj = file.toJSON(); const len = Object.keys(obj).length; - expect(obj.size).toBe(1024); - expect(obj.name).toBe('cat.png'); - expect(obj.type).toBe('image/png'); expect(obj.mime).toBe('image/png'); expect(obj.filename).toBe('cat.png'); - expect(6).toBe(len); }); test('toString()', () => { @@ -66,9 +60,6 @@ describe('VolatileFile', () => { expect(buffer).toBe(writeBuffer); cb(); }); - file.on('progress', (size) => { - expect(size).toBe(1029); - }); file.write(buffer, () => { done(); From e0576e1a5018beae971ae8337a5f2bb5dfeef7b5 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Thu, 18 Feb 2021 22:55:29 +0100 Subject: [PATCH 14/35] lint: lint --- examples/with-http.js | 5 +- src/Formidable.js | 26 ++-- src/FormidableError.js | 1 - src/PersistentFile.js | 2 +- src/parsers/StreamingQuerystring.js | 115 ++++++++++-------- src/plugins/octetstream.js | 6 +- .../file-write-stream-handler-option.test.js | 3 +- test/unit/querystring-parser.test.js | 2 - test/unit/volatile-file.test.js | 1 - 9 files changed, 84 insertions(+), 77 deletions(-) diff --git a/examples/with-http.js b/examples/with-http.js index 5a78a722..0b71da55 100644 --- a/examples/with-http.js +++ b/examples/with-http.js @@ -10,19 +10,18 @@ const server = http.createServer((req, res) => { multiples: true, uploadDir: `uploads`, keepExtensions: true, - filename: function (name, ext, part, form) { + filename(/*name, ext, part, form*/) { /* name basename of the http filename ext with the dot ".txt" only if keepExtension is true */ // slugify to avoid invalid filenames // substr to define a maximum length - // return `${slugify(name).${slugify(ext, separator: '')}`.substr(0, 100); + // return `${slugify(name).${slugify(ext, separator: '')}`.substr(0, 100); return 'yo.txt'; // or completly different name // return 'z/yo.txt'; // subdirectory }, }); - form.parse(req, (err, fields, files) => { if (err) { console.error(err); diff --git a/src/Formidable.js b/src/Formidable.js index 350273d2..9544e402 100644 --- a/src/Formidable.js +++ b/src/Formidable.js @@ -48,7 +48,9 @@ class IncomingForm extends EventEmitter { this.options = { ...DEFAULT_OPTIONS, ...options }; - const dir = path.resolve(this.options.uploadDir || this.options.uploaddir || os.tmpdir()); + const dir = path.resolve( + this.options.uploadDir || this.options.uploaddir || os.tmpdir(), + ); this.uploaddir = dir; this.uploadDir = dir; @@ -317,8 +319,8 @@ class IncomingForm extends EventEmitter { this._flushing += 1; - const newName = this._getNewName(part) - const finalPath = this._joinDirectoryName(newName); + const newName = this._getNewName(part); + const finalPath = this._joinDirectoryName(newName); const file = this._newFile({ newName, path: finalPath, @@ -481,11 +483,11 @@ class IncomingForm extends EventEmitter { return new MultipartParser(this.options); } - _newFile({ path, filename, mime, newName }) { + _newFile({ path: filePath, filename, mime, newName }) { return this.options.fileWriteStreamHandler ? new VolatileFile({ newName, - path, + path: filePath, // avoid shadow filename, mime, createFileWriteStream: this.options.fileWriteStreamHandler, @@ -493,7 +495,7 @@ class IncomingForm extends EventEmitter { }) : new PersistentFile({ newName, - path, + path: filePath, filename, mime, hash: this.options.hash, @@ -544,7 +546,7 @@ class IncomingForm extends EventEmitter { return name; } - + _joinDirectoryName(name) { const newPath = path.join(this.uploadDir, name); @@ -560,16 +562,16 @@ class IncomingForm extends EventEmitter { const hasRename = typeof this.options.filename === 'function'; if (hasRename) { this._getNewName = (part) => { - let ext = ""; + let ext = ''; let name = this.options.defaultInvalidName; - if (part.filename) { // can be null - ({ext, name} = path.parse(part.filename)); + if (part.filename) { + // can be null + ({ ext, name } = path.parse(part.filename)); if (!this.options.keepExtensions) { - ext = "" + ext = ''; } } return this.options.filename.call(this, name, ext, part, this); - }; } else { this._getNewName = (part) => this._uploadPath(part); diff --git a/src/FormidableError.js b/src/FormidableError.js index 80d6c7bf..da5c25f5 100644 --- a/src/FormidableError.js +++ b/src/FormidableError.js @@ -1,6 +1,5 @@ /* eslint-disable no-plusplus */ - const missingPlugin = 1000; const pluginFunction = 1001; const aborted = 1002; diff --git a/src/PersistentFile.js b/src/PersistentFile.js index 63f0511b..aa3277e8 100644 --- a/src/PersistentFile.js +++ b/src/PersistentFile.js @@ -9,7 +9,7 @@ const { EventEmitter } = require('events'); class PersistentFile extends EventEmitter { constructor(properties) { super(); - + Object.assign(this, properties); this.size = 0; diff --git a/src/parsers/StreamingQuerystring.js b/src/parsers/StreamingQuerystring.js index 967a3a29..06d7577a 100644 --- a/src/parsers/StreamingQuerystring.js +++ b/src/parsers/StreamingQuerystring.js @@ -8,17 +8,16 @@ const errors = require('../FormidableError.js'); const { FormidableError } = errors; - -var AMPERSAND = 38, - EQUALS = 61; +const AMPERSAND = 38; +const EQUALS = 61; class QuerystringParser extends Transform { constructor(options = {}) { super({ readableObjectMode: true }); - - const {maxFieldSize} = options + + const { maxFieldSize } = options; this.maxFieldLength = maxFieldSize; - this.buffer = Buffer.from(""); + this.buffer = Buffer.from(''); this.fieldCount = 0; this.sectionStart = 0; this.key = ''; @@ -26,77 +25,87 @@ class QuerystringParser extends Transform { } _transform(buffer, encoding, callback) { - var len = buffer.length; - if(this.buffer && this.buffer.length) { - //we have some data left over from the last write which we are in the middle of processing - len += this.buffer.length; - buffer = Buffer.concat([this.buffer, buffer], len) + let len = buffer.length; + if (this.buffer && this.buffer.length) { + // we have some data left over from the last write which we are in the middle of processing + len += this.buffer.length; + buffer = Buffer.concat([this.buffer, buffer], len); } - for (var i = this.buffer.length || 0; i < len; i += 1) { - var c = buffer[i]; - if(this.readingKey) { - //KEY, check for = - if(c===EQUALS) { - this.key = this.getSection(buffer, i); - this.readingKey = false; - this.sectionStart = i + 1 - } else if(c===AMPERSAND) { - //just key, no value. Prepare to read another key - this.emitField(this.getSection(buffer, i)); - this.sectionStart = i + 1 - } - //VALUE, check for & - } else if(c===AMPERSAND) { - this.emitField(this.key, this.getSection(buffer, i)); - this.sectionStart = i + 1; - } - - - if(this.maxFieldLength && i - this.sectionStart === this.maxFieldLength) { - callback(new FormidableError( - (this.readingKey ? 'Key' : 'Value for ' + this.key) + ' longer than maxFieldLength'), - errors.maxFieldsSizeExceeded, - 413, - ) + for (let i = this.buffer.length || 0; i < len; i += 1) { + const c = buffer[i]; + if (this.readingKey) { + // KEY, check for = + if (c === EQUALS) { + this.key = this.getSection(buffer, i); + this.readingKey = false; + this.sectionStart = i + 1; + } else if (c === AMPERSAND) { + // just key, no value. Prepare to read another key + this.emitField(this.getSection(buffer, i)); + this.sectionStart = i + 1; } + // VALUE, check for & + } else if (c === AMPERSAND) { + this.emitField(this.key, this.getSection(buffer, i)); + this.sectionStart = i + 1; + } + + if ( + this.maxFieldLength && + i - this.sectionStart === this.maxFieldLength + ) { + callback( + new FormidableError( + `${ + this.readingKey ? 'Key' : `Value for ${this.key}` + } longer than maxFieldLength`, + ), + errors.maxFieldsSizeExceeded, + 413, + ); + } } - //Prepare the remaining key or value (from sectionStart to the end) for the next write() or for end() + // Prepare the remaining key or value (from sectionStart to the end) for the next write() or for end() len -= this.sectionStart; - if(len){ - // i.e. Unless the last character was a & or = - this.buffer = Buffer.from(this.buffer,0, this.sectionStart); - } - else this.buffer = null; + if (len) { + // i.e. Unless the last character was a & or = + this.buffer = Buffer.from(this.buffer, 0, this.sectionStart); + } else this.buffer = null; this.sectionStart = 0; callback(); } _flush(callback) { - //Emit the last field - if(this.readingKey) { - //we only have a key if there's something in the buffer. We definitely have no value - this.buffer && this.buffer.length && this.emitField(this.buffer.toString('ascii')); + // Emit the last field + if (this.readingKey) { + // we only have a key if there's something in the buffer. We definitely have no value + if (this.buffer && this.buffer.length){ + this.emitField(this.buffer.toString('ascii')); + } } else { - //We have a key, we may or may not have a value - this.emitField(this.key, this.buffer && this.buffer.length && this.buffer.toString('ascii')); + // We have a key, we may or may not have a value + this.emitField( + this.key, + this.buffer && this.buffer.length && this.buffer.toString('ascii'), + ); } this.buffer = ''; callback(); } getSection(buffer, i) { - if(i===this.sectionStart) return ''; + if (i === this.sectionStart) return ''; return buffer.toString('ascii', this.sectionStart, i); } emitField(key, val) { - this.key = ''; - this.readingKey = true; - this.push({key, value: val || ''}); + this.key = ''; + this.readingKey = true; + this.push({ key, value: val || '' }); } } diff --git a/src/plugins/octetstream.js b/src/plugins/octetstream.js index 529b7848..fb63aa09 100644 --- a/src/plugins/octetstream.js +++ b/src/plugins/octetstream.js @@ -29,10 +29,10 @@ function init(_self, _opts) { const thisPart = { filename, - mime + mime, }; - const newName = this._getNewName(thisPart) - const finalPath = this._joinDirectoryName(newName); + const newName = this._getNewName(thisPart); + const finalPath = this._joinDirectoryName(newName); const file = this._newFile({ newName, path: finalPath, diff --git a/test/integration/file-write-stream-handler-option.test.js b/test/integration/file-write-stream-handler-option.test.js index c1ec83eb..92487cb7 100644 --- a/test/integration/file-write-stream-handler-option.test.js +++ b/test/integration/file-write-stream-handler-option.test.js @@ -38,7 +38,8 @@ test('file write stream handler', (done) => { createDirs([DEFAULT_UPLOAD_DIR, CUSTOM_UPLOAD_DIR]); const form = formidable({ uploadDir: DEFAULT_UPLOAD_DIR, - fileWriteStreamHandler: () => fs.createWriteStream(CUSTOM_UPLOAD_FILE_PATH) + fileWriteStreamHandler: () => + fs.createWriteStream(CUSTOM_UPLOAD_FILE_PATH), }); form.parse(req, (err, fields, files) => { diff --git a/test/unit/querystring-parser.test.js b/test/unit/querystring-parser.test.js index 5ca08663..32ee4eb6 100644 --- a/test/unit/querystring-parser.test.js +++ b/test/unit/querystring-parser.test.js @@ -7,8 +7,6 @@ test('on constructor', () => { expect(parser.constructor.name).toBe('QuerystringParser'); }); - - // ! skip // test(function end => // const FIELDS = { a: ['b', { c: 'd' }], e: 'f' }; diff --git a/test/unit/volatile-file.test.js b/test/unit/volatile-file.test.js index d8499000..4357fee3 100644 --- a/test/unit/volatile-file.test.js +++ b/test/unit/volatile-file.test.js @@ -44,7 +44,6 @@ describe('VolatileFile', () => { test('toJSON()', () => { const obj = file.toJSON(); - const len = Object.keys(obj).length; expect(obj.mime).toBe('image/png'); expect(obj.filename).toBe('cat.png'); From 59a9b2bcc26f0f2555c2d5dc9b094e70e008c6b2 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Fri, 19 Feb 2021 15:33:03 +0100 Subject: [PATCH 15/35] refactor: rename mime into mimetype --- README.md | 2 +- src/Formidable.js | 12 ++++++------ src/PersistentFile.js | 2 +- src/VolatileFile.js | 2 +- src/plugins/multipart.js | 4 ++-- src/plugins/octetstream.js | 6 +++--- test/unit/formidable.test.js | 10 +++++----- test/unit/persistent-file.test.js | 4 ++-- test/unit/volatile-file.test.js | 4 ++-- 9 files changed, 23 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 642553da..d5817e77 100644 --- a/README.md +++ b/README.md @@ -569,7 +569,7 @@ const form = formidable(); form.onPart = function (part) { // let formidable handle only non-file parts - if (part.filename === '' || !part.mime) { + if (part.filename === '' || !part.mimetype) { // used internally, please do not override! form._handlePart(part); } diff --git a/src/Formidable.js b/src/Formidable.js index 9544e402..f9b15598 100644 --- a/src/Formidable.js +++ b/src/Formidable.js @@ -288,9 +288,9 @@ class IncomingForm extends EventEmitter { // ? NOTE(@tunnckocore): no it can be any falsey value, it most probably depends on what's returned // from somewhere else. Where recently I changed the return statements // and such thing because code style - // ? NOTE(@tunnckocore): or even better, if there is no mime, then it's for sure a field + // ? NOTE(@tunnckocore): or even better, if there is no mimetype, then it's for sure a field // ? NOTE(@tunnckocore): filename is an empty string when a field? - if (!part.mime) { + if (!part.mimetype) { let value = ''; const decoder = new StringDecoder( part.transferEncoding || this.options.encoding, @@ -325,7 +325,7 @@ class IncomingForm extends EventEmitter { newName, path: finalPath, filename: part.filename, - mime: part.mime, + mimetype: part.mimetype, }); file.on('error', (err) => { this._error(err); @@ -483,13 +483,13 @@ class IncomingForm extends EventEmitter { return new MultipartParser(this.options); } - _newFile({ path: filePath, filename, mime, newName }) { + _newFile({ path: filePath, filename, mimetype, newName }) { return this.options.fileWriteStreamHandler ? new VolatileFile({ newName, path: filePath, // avoid shadow filename, - mime, + mimetype, createFileWriteStream: this.options.fileWriteStreamHandler, hash: this.options.hash, }) @@ -497,7 +497,7 @@ class IncomingForm extends EventEmitter { newName, path: filePath, filename, - mime, + mimetype, hash: this.options.hash, }); } diff --git a/src/PersistentFile.js b/src/PersistentFile.js index aa3277e8..b08dfd57 100644 --- a/src/PersistentFile.js +++ b/src/PersistentFile.js @@ -34,7 +34,7 @@ class PersistentFile extends EventEmitter { size: this.size, path: this.path, newName: this.newName, - mime: this.mime, + mimetype: this.mimetype, mtime: this.lastModifiedDate, length: this.length, filename: this.filename, diff --git a/src/VolatileFile.js b/src/VolatileFile.js index e91a0463..88921f02 100644 --- a/src/VolatileFile.js +++ b/src/VolatileFile.js @@ -38,7 +38,7 @@ class VolatileFile extends EventEmitter { newName: this.newName, length: this.length, filename: this.filename, - mime: this.mime, + mimetype: this.mimetype, }; if (this.hash && this.hash !== '') { json.hash = this.hash; diff --git a/src/plugins/multipart.js b/src/plugins/multipart.js index df3835bc..328cd622 100644 --- a/src/plugins/multipart.js +++ b/src/plugins/multipart.js @@ -59,7 +59,7 @@ function createInitMultipart(boundary) { part.headers = {}; part.name = null; part.filename = null; - part.mime = null; + part.mimetype = null; part.transferEncoding = this.options.encoding; part.transferBuffer = ''; @@ -86,7 +86,7 @@ function createInitMultipart(boundary) { part.filename = this._getFileName(headerValue); } else if (headerField === 'content-type') { - part.mime = headerValue; + part.mimetype = headerValue; } else if (headerField === 'content-transfer-encoding') { part.transferEncoding = headerValue.toLowerCase(); } diff --git a/src/plugins/octetstream.js b/src/plugins/octetstream.js index fb63aa09..e95c712e 100644 --- a/src/plugins/octetstream.js +++ b/src/plugins/octetstream.js @@ -25,11 +25,11 @@ module.exports = function plugin(formidable, options) { function init(_self, _opts) { this.type = 'octet-stream'; const filename = this.headers['x-file-name']; - const mime = this.headers['content-type']; + const mimetype = this.headers['content-type']; const thisPart = { filename, - mime, + mimetype, }; const newName = this._getNewName(thisPart); const finalPath = this._joinDirectoryName(newName); @@ -37,7 +37,7 @@ function init(_self, _opts) { newName, path: finalPath, filename, - mime, + mimetype, }); this.emit('fileBegin', filename, file); diff --git a/test/unit/formidable.test.js b/test/unit/formidable.test.js index a8878ae6..5df419a2 100644 --- a/test/unit/formidable.test.js +++ b/test/unit/formidable.test.js @@ -180,7 +180,7 @@ function makeHeader(filename) { }); const part = new Stream(); - part.mime = 'text/plain'; + part.mimetype = 'text/plain'; // eslint-disable-next-line max-nested-callbacks form.on('error', (error) => { expect(error.message).toBe( @@ -202,7 +202,7 @@ function makeHeader(filename) { const formEmitSpy = jest.spyOn(form, 'emit'); const part = new Stream(); - part.mime = 'text/plain'; + part.mimetype = 'text/plain'; form.onPart(part); part.emit('data', Buffer.alloc(1)); expect(formEmitSpy).not.toBeCalledWith('error'); @@ -216,7 +216,7 @@ function makeHeader(filename) { const formEmitSpy = jest.spyOn(form, 'emit'); const part = new Stream(); - part.mime = 'text/plain'; + part.mimetype = 'text/plain'; form.onPart(part); part.emit('end'); expect(formEmitSpy).not.toBeCalledWith('error'); @@ -228,7 +228,7 @@ function makeHeader(filename) { const form = getForm(name, { multiples: true, minFileSize: 5 }); const part = new Stream(); - part.mime = 'text/plain'; + part.mimetype = 'text/plain'; form.on('error', (error) => { expect(error.message).toBe( 'options.minFileSize (5 bytes) inferior, received 4 bytes of file data', @@ -246,7 +246,7 @@ function makeHeader(filename) { const formEmitSpy = jest.spyOn(form, 'emit'); const part = new Stream(); - part.mime = 'text/plain'; + part.mimetype = 'text/plain'; form.onPart(part); part.emit('data', Buffer.alloc(11)); expect(formEmitSpy).not.toBeCalledWith('error'); diff --git a/test/unit/persistent-file.test.js b/test/unit/persistent-file.test.js index 5fd65876..79f9a81f 100644 --- a/test/unit/persistent-file.test.js +++ b/test/unit/persistent-file.test.js @@ -10,14 +10,14 @@ const file = new PersistentFile({ type: 'image/png', lastModifiedDate: now, filename: 'cat.png', - mime: 'image/png', + mimetype: 'image/png', }); test('PersistentFile#toJSON()', () => { const obj = file.toJSON(); expect('/tmp/cat.png').toBe(obj.path); - expect('image/png').toBe(obj.mime); + expect('image/png').toBe(obj.mimetype); expect('cat.png').toBe(obj.filename); expect(now).toBe(obj.mtime); }); diff --git a/test/unit/volatile-file.test.js b/test/unit/volatile-file.test.js index 4357fee3..bd2c0cd3 100644 --- a/test/unit/volatile-file.test.js +++ b/test/unit/volatile-file.test.js @@ -19,7 +19,7 @@ describe('VolatileFile', () => { file = new VolatileFile({ name: 'cat.png', filename: 'cat.png', - mime: 'image/png', + mimetype: 'image/png', createFileWriteStream: writeStreamMock, }); @@ -45,7 +45,7 @@ describe('VolatileFile', () => { test('toJSON()', () => { const obj = file.toJSON(); - expect(obj.mime).toBe('image/png'); + expect(obj.mimetype).toBe('image/png'); expect(obj.filename).toBe('cat.png'); }); From 2ca0b78d98974a64501373f0a38b96fbe546a00b Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Fri, 19 Feb 2021 15:35:13 +0100 Subject: [PATCH 16/35] refactor: explicit this.options.keepExtensions !== true --- src/Formidable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Formidable.js b/src/Formidable.js index f9b15598..05e9acdc 100644 --- a/src/Formidable.js +++ b/src/Formidable.js @@ -567,7 +567,7 @@ class IncomingForm extends EventEmitter { if (part.filename) { // can be null ({ ext, name } = path.parse(part.filename)); - if (!this.options.keepExtensions) { + if (this.options.keepExtensions !== true) { ext = ''; } } From e4b7fe64d0ce693bad2f43bf6bf771b7c48a0818 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Fri, 19 Feb 2021 15:37:14 +0100 Subject: [PATCH 17/35] tests: reverse expectation order --- test/unit/persistent-file.test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/unit/persistent-file.test.js b/test/unit/persistent-file.test.js index 79f9a81f..92035d2e 100644 --- a/test/unit/persistent-file.test.js +++ b/test/unit/persistent-file.test.js @@ -16,8 +16,8 @@ const file = new PersistentFile({ test('PersistentFile#toJSON()', () => { const obj = file.toJSON(); - expect('/tmp/cat.png').toBe(obj.path); - expect('image/png').toBe(obj.mimetype); - expect('cat.png').toBe(obj.filename); - expect(now).toBe(obj.mtime); + expect(obj.path).toBe('/tmp/cat.png'); + expect(obj.mimetype).toBe('image/png'); + expect(obj.filename).toBe('cat.png'); + expect(obj.mtime).toBe(now); }); From 3481057626d587822389a152473ad6aace868499 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Fri, 19 Feb 2021 15:38:57 +0100 Subject: [PATCH 18/35] tests: rename to xname to avoid confusion --- test/unit/volatile-file.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/volatile-file.test.js b/test/unit/volatile-file.test.js index bd2c0cd3..b3516180 100644 --- a/test/unit/volatile-file.test.js +++ b/test/unit/volatile-file.test.js @@ -17,7 +17,7 @@ describe('VolatileFile', () => { writeStreamMock = jest.fn(() => writeStreamInstanceMock); file = new VolatileFile({ - name: 'cat.png', + xname: 'cat.png', filename: 'cat.png', mimetype: 'image/png', createFileWriteStream: writeStreamMock, From bef35f25d68f617803b96b40367e22913e168547 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Fri, 19 Feb 2021 15:46:02 +0100 Subject: [PATCH 19/35] refactor: inline old _uploadPath --- src/Formidable.js | 20 ++++++++++---------- test/unit/formidable.test.js | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Formidable.js b/src/Formidable.js index 05e9acdc..69cdb77a 100644 --- a/src/Formidable.js +++ b/src/Formidable.js @@ -536,16 +536,7 @@ class IncomingForm extends EventEmitter { return basename.slice(firstDot, lastDot) + extname; } - _uploadPath(part) { - const name = toHexoId(); - if (part && this.options.keepExtensions) { - const filename = typeof part === 'string' ? part : part.filename; - return `${name}${this._getExtension(filename)}`; - } - - return name; - } _joinDirectoryName(name) { const newPath = path.join(this.uploadDir, name); @@ -574,7 +565,16 @@ class IncomingForm extends EventEmitter { return this.options.filename.call(this, name, ext, part, this); }; } else { - this._getNewName = (part) => this._uploadPath(part); + this._getNewName = (part) => { + const name = toHexoId(); + + if (part && this.options.keepExtensions) { + const filename = typeof part === 'string' ? part : part.filename; + return `${name}${this._getExtension(filename)}`; + } + + return name; + } } } diff --git a/test/unit/formidable.test.js b/test/unit/formidable.test.js index 5df419a2..20f7a46a 100644 --- a/test/unit/formidable.test.js +++ b/test/unit/formidable.test.js @@ -60,10 +60,10 @@ function makeHeader(filename) { expect(form._getFileName(makeHeader(filename))).toBe('my☃.txt'); }); - test(`${name}#_uploadPath strips harmful characters from extension when keepExtensions`, () => { + test(`${name}#_getNewName strips harmful characters from extension when keepExtensions`, () => { const form = getForm(name, { keepExtensions: true }); - const getBasename = (part) => path.basename(form._uploadPath(part)); + const getBasename = (part) => path.basename(form._getNewName(part)); let basename = getBasename('fine.jpg?foo=bar'); expect(basename).toHaveLength(29); From 45c9213576a70589156c31aa425993006303c940 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Fri, 19 Feb 2021 15:49:48 +0100 Subject: [PATCH 20/35] refactor: rename newName into newFileName --- README.md | 4 ++-- src/Formidable.js | 12 ++++++------ src/PersistentFile.js | 2 +- src/VolatileFile.js | 2 +- src/plugins/octetstream.js | 6 +++--- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index d5817e77..1ee71ce3 100644 --- a/README.md +++ b/README.md @@ -355,7 +355,7 @@ See it's defaults in [src/Formidable.js DEFAULT_OPTIONS](./src/Formidable.js) attribute. Also, the `fields` argument will contain arrays of values for fields that have names ending with '[]'. - `options.filename` **{function}** - default `undefined` Use it to control - newName. Must return a string. Will be joined with options.uploadDir. + newFilename. Must return a string. Will be joined with options.uploadDir. #### `options.filename` **{function}** function (name, ext, part, form) -> string @@ -640,7 +640,7 @@ form.on('fileBegin', (formName, file) => { // accessible here // formName the name in the form () or http filename for octetstream // file.name http filename or null if there was a parsing error - // file.newName generated hexoid or what options.filename returned + // file.newFilename generated hexoid or what options.filename returned // file.path default pathnme as per options.uploadDir and options.filename // file.path = CUSTOM_PATH // to change the final path }); diff --git a/src/Formidable.js b/src/Formidable.js index 69cdb77a..1fdf382c 100644 --- a/src/Formidable.js +++ b/src/Formidable.js @@ -319,10 +319,10 @@ class IncomingForm extends EventEmitter { this._flushing += 1; - const newName = this._getNewName(part); - const finalPath = this._joinDirectoryName(newName); + const newFilename = this._getNewName(part); + const finalPath = this._joinDirectoryName(newFilename); const file = this._newFile({ - newName, + newFilename, path: finalPath, filename: part.filename, mimetype: part.mimetype, @@ -483,10 +483,10 @@ class IncomingForm extends EventEmitter { return new MultipartParser(this.options); } - _newFile({ path: filePath, filename, mimetype, newName }) { + _newFile({ path: filePath, filename, mimetype, newFilename }) { return this.options.fileWriteStreamHandler ? new VolatileFile({ - newName, + newFilename, path: filePath, // avoid shadow filename, mimetype, @@ -494,7 +494,7 @@ class IncomingForm extends EventEmitter { hash: this.options.hash, }) : new PersistentFile({ - newName, + newFilename, path: filePath, filename, mimetype, diff --git a/src/PersistentFile.js b/src/PersistentFile.js index b08dfd57..a99db1ab 100644 --- a/src/PersistentFile.js +++ b/src/PersistentFile.js @@ -33,7 +33,7 @@ class PersistentFile extends EventEmitter { const json = { size: this.size, path: this.path, - newName: this.newName, + newFilename: this.newFilename, mimetype: this.mimetype, mtime: this.lastModifiedDate, length: this.length, diff --git a/src/VolatileFile.js b/src/VolatileFile.js index 88921f02..d788ce3f 100644 --- a/src/VolatileFile.js +++ b/src/VolatileFile.js @@ -35,7 +35,7 @@ class VolatileFile extends EventEmitter { toJSON() { const json = { size: this.size, - newName: this.newName, + newFilename: this.newFilename, length: this.length, filename: this.filename, mimetype: this.mimetype, diff --git a/src/plugins/octetstream.js b/src/plugins/octetstream.js index e95c712e..c9454683 100644 --- a/src/plugins/octetstream.js +++ b/src/plugins/octetstream.js @@ -31,10 +31,10 @@ function init(_self, _opts) { filename, mimetype, }; - const newName = this._getNewName(thisPart); - const finalPath = this._joinDirectoryName(newName); + const newFilename = this._getNewName(thisPart); + const finalPath = this._joinDirectoryName(newFilename); const file = this._newFile({ - newName, + newFilename, path: finalPath, filename, mimetype, From 7edf4bb7c7fa8d5bcf792c87ab4f3966e60b51cd Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Fri, 19 Feb 2021 16:06:59 +0100 Subject: [PATCH 21/35] refactor: rename filename into originalFilename --- README.md | 4 +- examples/with-http.js | 2 +- src/Formidable.js | 32 ++++++++-------- src/PersistentFile.js | 4 +- src/VolatileFile.js | 4 +- src/plugins/multipart.js | 4 +- src/plugins/octetstream.js | 8 ++-- test/fixture/js/encoding.js | 10 ++--- test/fixture/js/no-filename.js | 6 +-- test/fixture/js/preamble.js | 4 +- test/fixture/js/special-chars-in-filename.js | 6 +-- test/fixture/js/workarounds.js | 4 +- test/integration/fixtures.test.js | 2 +- .../content-transfer-encoding.test.js | 4 +- test/unit/formidable.test.js | 38 +++++++++---------- test/unit/persistent-file.test.js | 4 +- test/unit/volatile-file.test.js | 4 +- 17 files changed, 70 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 1ee71ce3..29fcee69 100644 --- a/README.md +++ b/README.md @@ -569,7 +569,7 @@ const form = formidable(); form.onPart = function (part) { // let formidable handle only non-file parts - if (part.filename === '' || !part.mimetype) { + if (part.originalFilename === '' || !part.mimetype) { // used internally, please do not override! form._handlePart(part); } @@ -639,7 +639,7 @@ file system. form.on('fileBegin', (formName, file) => { // accessible here // formName the name in the form () or http filename for octetstream - // file.name http filename or null if there was a parsing error + // file.originalFilename http filename or null if there was a parsing error // file.newFilename generated hexoid or what options.filename returned // file.path default pathnme as per options.uploadDir and options.filename // file.path = CUSTOM_PATH // to change the final path diff --git a/examples/with-http.js b/examples/with-http.js index 0b71da55..d5d3c7b2 100644 --- a/examples/with-http.js +++ b/examples/with-http.js @@ -11,7 +11,7 @@ const server = http.createServer((req, res) => { uploadDir: `uploads`, keepExtensions: true, filename(/*name, ext, part, form*/) { - /* name basename of the http filename + /* name basename of the http originalFilename ext with the dot ".txt" only if keepExtension is true */ // slugify to avoid invalid filenames diff --git a/src/Formidable.js b/src/Formidable.js index 1fdf382c..214afdb7 100644 --- a/src/Formidable.js +++ b/src/Formidable.js @@ -270,17 +270,17 @@ class IncomingForm extends EventEmitter { } _handlePart(part) { - if (part.filename && typeof part.filename !== 'string') { + if (part.originalFilename && typeof part.originalFilename !== 'string') { this._error( new FormidableError( - `the part.filename should be string when it exists`, + `the part.originalFilename should be string when it exists`, errors.filenameNotString, ), ); return; } - // This MUST check exactly for undefined. You can not change it to !part.filename. + // This MUST check exactly for undefined. You can not change it to !part.originalFilename. // todo: uncomment when switch tests to Jest // console.log(part); @@ -289,7 +289,7 @@ class IncomingForm extends EventEmitter { // from somewhere else. Where recently I changed the return statements // and such thing because code style // ? NOTE(@tunnckocore): or even better, if there is no mimetype, then it's for sure a field - // ? NOTE(@tunnckocore): filename is an empty string when a field? + // ? NOTE(@tunnckocore): originalFilename is an empty string when a field? if (!part.mimetype) { let value = ''; const decoder = new StringDecoder( @@ -324,7 +324,7 @@ class IncomingForm extends EventEmitter { const file = this._newFile({ newFilename, path: finalPath, - filename: part.filename, + originalFilename: part.originalFilename, mimetype: part.mimetype, }); file.on('error', (err) => { @@ -483,12 +483,12 @@ class IncomingForm extends EventEmitter { return new MultipartParser(this.options); } - _newFile({ path: filePath, filename, mimetype, newFilename }) { + _newFile({ path: filePath, originalFilename, mimetype, newFilename }) { return this.options.fileWriteStreamHandler ? new VolatileFile({ newFilename, path: filePath, // avoid shadow - filename, + originalFilename, mimetype, createFileWriteStream: this.options.fileWriteStreamHandler, hash: this.options.hash, @@ -496,7 +496,7 @@ class IncomingForm extends EventEmitter { : new PersistentFile({ newFilename, path: filePath, - filename, + originalFilename, mimetype, hash: this.options.hash, }); @@ -510,13 +510,13 @@ class IncomingForm extends EventEmitter { if (!m) return null; const match = m[2] || m[3] || ''; - let filename = match.substr(match.lastIndexOf('\\') + 1); - filename = filename.replace(/%22/g, '"'); - filename = filename.replace(/&#([\d]{4});/g, (_, code) => + let originalFilename = match.substr(match.lastIndexOf('\\') + 1); + originalFilename = originalFilename.replace(/%22/g, '"'); + originalFilename = originalFilename.replace(/&#([\d]{4});/g, (_, code) => String.fromCharCode(code), ); - return filename; + return originalFilename; } _getExtension(str) { @@ -555,9 +555,9 @@ class IncomingForm extends EventEmitter { this._getNewName = (part) => { let ext = ''; let name = this.options.defaultInvalidName; - if (part.filename) { + if (part.originalFilename) { // can be null - ({ ext, name } = path.parse(part.filename)); + ({ ext, name } = path.parse(part.originalFilename)); if (this.options.keepExtensions !== true) { ext = ''; } @@ -569,8 +569,8 @@ class IncomingForm extends EventEmitter { const name = toHexoId(); if (part && this.options.keepExtensions) { - const filename = typeof part === 'string' ? part : part.filename; - return `${name}${this._getExtension(filename)}`; + const originalFilename = typeof part === 'string' ? part : part.originalFilename; + return `${name}${this._getExtension(originalFilename)}`; } return name; diff --git a/src/PersistentFile.js b/src/PersistentFile.js index a99db1ab..fca4374e 100644 --- a/src/PersistentFile.js +++ b/src/PersistentFile.js @@ -37,7 +37,7 @@ class PersistentFile extends EventEmitter { mimetype: this.mimetype, mtime: this.lastModifiedDate, length: this.length, - filename: this.filename, + originalFilename: this.originalFilename, }; if (this.hash && this.hash !== '') { json.hash = this.hash; @@ -46,7 +46,7 @@ class PersistentFile extends EventEmitter { } toString() { - return `PersistentFile: ${this.filename}, Path: ${this.path}`; + return `PersistentFile: ${this.originalFilename}, Path: ${this.path}`; } write(buffer, cb) { diff --git a/src/VolatileFile.js b/src/VolatileFile.js index d788ce3f..4c77132e 100644 --- a/src/VolatileFile.js +++ b/src/VolatileFile.js @@ -37,7 +37,7 @@ class VolatileFile extends EventEmitter { size: this.size, newFilename: this.newFilename, length: this.length, - filename: this.filename, + originalFilename: this.originalFilename, mimetype: this.mimetype, }; if (this.hash && this.hash !== '') { @@ -47,7 +47,7 @@ class VolatileFile extends EventEmitter { } toString() { - return `VolatileFile: ${this.filename}`; + return `VolatileFile: ${this.originalFilename}`; } write(buffer, cb) { diff --git a/src/plugins/multipart.js b/src/plugins/multipart.js index 328cd622..ba07373b 100644 --- a/src/plugins/multipart.js +++ b/src/plugins/multipart.js @@ -58,7 +58,7 @@ function createInitMultipart(boundary) { part.readable = true; part.headers = {}; part.name = null; - part.filename = null; + part.originalFilename = null; part.mimetype = null; part.transferEncoding = this.options.encoding; @@ -84,7 +84,7 @@ function createInitMultipart(boundary) { part.name = m[2] || m[3] || ''; } - part.filename = this._getFileName(headerValue); + part.originalFilename = this._getFileName(headerValue); } else if (headerField === 'content-type') { part.mimetype = headerValue; } else if (headerField === 'content-transfer-encoding') { diff --git a/src/plugins/octetstream.js b/src/plugins/octetstream.js index c9454683..f76be9e4 100644 --- a/src/plugins/octetstream.js +++ b/src/plugins/octetstream.js @@ -24,11 +24,11 @@ module.exports = function plugin(formidable, options) { // to test the plugin you can pass custom `this` context to it (and so `this.options`) function init(_self, _opts) { this.type = 'octet-stream'; - const filename = this.headers['x-file-name']; + const originalFilename = this.headers['x-file-name']; const mimetype = this.headers['content-type']; const thisPart = { - filename, + originalFilename, mimetype, }; const newFilename = this._getNewName(thisPart); @@ -36,11 +36,11 @@ function init(_self, _opts) { const file = this._newFile({ newFilename, path: finalPath, - filename, + originalFilename, mimetype, }); - this.emit('fileBegin', filename, file); + this.emit('fileBegin', originalFilename, file); file.open(); this.openedFiles.push(file); this._flushing += 1; diff --git a/test/fixture/js/encoding.js b/test/fixture/js/encoding.js index 94230fbb..16742fb1 100644 --- a/test/fixture/js/encoding.js +++ b/test/fixture/js/encoding.js @@ -2,7 +2,7 @@ module.exports['menu_seperator.png.http'] = [ { type: 'file', name: 'image', - filename: 'menu_separator.png', + originalFilename: 'menu_separator.png', fixture: 'menu_separator.png', sha1: 'c845ca3ea794be298f2a1b79769b71939eaf4e54', }, @@ -12,7 +12,7 @@ module.exports['beta-sticker-1.png.http'] = [ { type: 'file', name: 'sticker', - filename: 'beta-sticker-1.png', + originalFilename: 'beta-sticker-1.png', fixture: 'beta-sticker-1.png', sha1: '6abbcffd12b4ada5a6a084fe9e4584f846331bc4', }, @@ -22,7 +22,7 @@ module.exports['blank.gif.http'] = [ { type: 'file', name: 'file', - filename: 'blank.gif', + originalFilename: 'blank.gif', fixture: 'blank.gif', sha1: 'a1fdee122b95748d81cee426d717c05b5174fe96', }, @@ -32,7 +32,7 @@ module.exports['binaryfile.tar.gz.http'] = [ { type: 'file', name: 'file', - filename: 'binaryfile.tar.gz', + originalFilename: 'binaryfile.tar.gz', fixture: 'binaryfile.tar.gz', sha1: 'cfabe13b348e5e69287d677860880c52a69d2155', }, @@ -42,7 +42,7 @@ module.exports['plain.txt.http'] = [ { type: 'file', name: 'file', - filename: 'plain.txt', + originalFilename: 'plain.txt', fixture: 'plain.txt', sha1: 'b31d07bac24ac32734de88b3687dddb10e976872', }, diff --git a/test/fixture/js/no-filename.js b/test/fixture/js/no-filename.js index 37e73d03..6fdc02ed 100644 --- a/test/fixture/js/no-filename.js +++ b/test/fixture/js/no-filename.js @@ -2,17 +2,17 @@ module.exports['generic.http'] = [ { type: 'file', name: 'upload', - filename: '', + originalFilename: '', fixture: 'plain.txt', sha1: 'b31d07bac24ac32734de88b3687dddb10e976872', }, ]; -module.exports['filename-name.http'] = [ +module.exports['originalFilename-name.http'] = [ { type: 'file', name: 'upload', - filename: 'plain.txt', + originalFilename: 'plain.txt', fixture: 'plain.txt', sha1: 'b31d07bac24ac32734de88b3687dddb10e976872', }, diff --git a/test/fixture/js/preamble.js b/test/fixture/js/preamble.js index 2160b04d..06e1f2d0 100644 --- a/test/fixture/js/preamble.js +++ b/test/fixture/js/preamble.js @@ -2,7 +2,7 @@ module.exports['crlf.http'] = [ { type: 'file', name: 'upload', - filename: 'plain.txt', + originalFilename: 'plain.txt', fixture: 'plain.txt', sha1: 'b31d07bac24ac32734de88b3687dddb10e976872', }, @@ -12,7 +12,7 @@ module.exports['preamble.http'] = [ { type: 'file', name: 'upload', - filename: 'plain.txt', + originalFilename: 'plain.txt', fixture: 'plain.txt', sha1: 'a47f7a8a7959f36c3f151ba8b0bd28f2d6b606e2', }, diff --git a/test/fixture/js/special-chars-in-filename.js b/test/fixture/js/special-chars-in-filename.js index 2d6ad202..0bc8d859 100644 --- a/test/fixture/js/special-chars-in-filename.js +++ b/test/fixture/js/special-chars-in-filename.js @@ -1,12 +1,12 @@ const properFilename = 'funkyfilename.txt'; -function expect(filename) { +function expect(originalFilename) { return [ - { type: 'field', name: 'title', value: 'Weird filename' }, + { type: 'field', name: 'title', value: 'Weird originalFilename' }, { type: 'file', name: 'upload', - filename, + originalFilename, fixture: properFilename, }, ]; diff --git a/test/fixture/js/workarounds.js b/test/fixture/js/workarounds.js index e02c279c..9b483c4f 100644 --- a/test/fixture/js/workarounds.js +++ b/test/fixture/js/workarounds.js @@ -2,7 +2,7 @@ module.exports['missing-hyphens1.http'] = [ { type: 'file', name: 'upload', - filename: 'plain.txt', + originalFilename: 'plain.txt', fixture: 'plain.txt', sha1: '8c26b82ec9107e99b3486844644e92558efe0c73', }, @@ -11,7 +11,7 @@ module.exports['missing-hyphens2.http'] = [ { type: 'file', name: 'upload', - filename: 'second-plaintext.txt', + originalFilename: 'second-plaintext.txt', fixture: 'second-plaintext.txt', sha1: '798e39a4a1034232ed26e0aadd67f5d1ff10b966', }, diff --git a/test/integration/fixtures.test.js b/test/integration/fixtures.test.js index 5fa3d95b..451746c8 100644 --- a/test/integration/fixtures.test.js +++ b/test/integration/fixtures.test.js @@ -69,7 +69,7 @@ test('fixtures', (done) => { if (parsedPart.type === 'file') { const file = parsedPart.value; - assert.strictEqual(file.filename, expectedPart.filename); + assert.strictEqual(file.originalFilename, expectedPart.originalFilename); if (expectedPart.sha1) { assert.strictEqual( diff --git a/test/standalone/content-transfer-encoding.test.js b/test/standalone/content-transfer-encoding.test.js index e551d93e..59dfa822 100644 --- a/test/standalone/content-transfer-encoding.test.js +++ b/test/standalone/content-transfer-encoding.test.js @@ -29,12 +29,12 @@ test('content transfer encoding', (done) => { const body = '--foo\r\n' + - 'Content-Disposition: form-data; name="file1"; filename="file1"\r\n' + + 'Content-Disposition: form-data; name="file1"; originalFilename="file1"\r\n' + 'Content-Type: application/octet-stream\r\n' + '\r\nThis is the first file\r\n' + '--foo\r\n' + 'Content-Type: application/octet-stream\r\n' + - 'Content-Disposition: form-data; name="file2"; filename="file2"\r\n' + + 'Content-Disposition: form-data; name="file2"; originalFilename="file2"\r\n' + 'Content-Transfer-Encoding: unknown\r\n' + '\r\nThis is the second file\r\n' + '--foo--\r\n'; diff --git a/test/unit/formidable.test.js b/test/unit/formidable.test.js index 20f7a46a..15a07c29 100644 --- a/test/unit/formidable.test.js +++ b/test/unit/formidable.test.js @@ -12,52 +12,52 @@ const mod = require('../../src/index'); function getForm(name, opts) { return name === 'formidable' ? mod.formidable(opts) : new mod[name](opts); } -function makeHeader(filename) { - return `Content-Disposition: form-data; name="upload"; filename="${filename}"`; +function makeHeader(originalFilename) { + return `Content-Disposition: form-data; name="upload"; filename="${originalFilename}"`; } ['IncomingForm', 'Formidable', 'formidable'].forEach((name) => { test(`${name}#_getFileName with regular characters`, () => { - const filename = 'foo.txt'; + const originalFilename = 'foo.txt'; const form = getForm(name); - expect(form._getFileName(makeHeader(filename))).toBe('foo.txt'); + expect(form._getFileName(makeHeader(originalFilename))).toBe('foo.txt'); }); test(`${name}#_getFileName with unescaped quote`, () => { - const filename = 'my".txt'; + const originalFilename = 'my".txt'; const form = getForm(name); - expect(form._getFileName(makeHeader(filename))).toBe('my".txt'); + expect(form._getFileName(makeHeader(originalFilename))).toBe('my".txt'); }); test(`${name}#_getFileName with escaped quote`, () => { - const filename = 'my%22.txt'; + const originalFilename = 'my%22.txt'; const form = getForm(name); - expect(form._getFileName(makeHeader(filename))).toBe('my".txt'); + expect(form._getFileName(makeHeader(originalFilename))).toBe('my".txt'); }); test(`${name}#_getFileName with bad quote and additional sub-header`, () => { - const filename = 'my".txt'; + const originalFilename = 'my".txt'; const form = getForm(name); - const header = `${makeHeader(filename)}; foo="bar"`; - expect(form._getFileName(header)).toBe(filename); + const header = `${makeHeader(originalFilename)}; foo="bar"`; + expect(form._getFileName(header)).toBe(originalFilename); }); test(`${name}#_getFileName with semicolon`, () => { - const filename = 'my;.txt'; + const originalFilename = 'my;.txt'; const form = getForm(name); - expect(form._getFileName(makeHeader(filename))).toBe('my;.txt'); + expect(form._getFileName(makeHeader(originalFilename))).toBe('my;.txt'); }); test(`${name}#_getFileName with utf8 character`, () => { - const filename = 'my☃.txt'; + const originalFilename = 'my☃.txt'; const form = getForm(name); - expect(form._getFileName(makeHeader(filename))).toBe('my☃.txt'); + expect(form._getFileName(makeHeader(originalFilename))).toBe('my☃.txt'); }); test(`${name}#_getNewName strips harmful characters from extension when keepExtensions`, () => { @@ -75,12 +75,12 @@ function makeHeader(filename) { ext = path.extname(basename); expect(ext).toBe(''); - basename = getBasename({ filename: 'super.cr2+dsad' }); + basename = getBasename({ originalFilename: 'super.cr2+dsad' }); expect(basename).toHaveLength(29); ext = path.extname(basename); expect(ext).toBe('.cr2'); - basename = getBasename({ filename: 'super.gz' }); + basename = getBasename({ originalFilename: 'super.gz' }); expect(basename).toHaveLength(28); ext = path.extname(basename); expect(ext).toBe('.gz'); @@ -272,9 +272,9 @@ function makeHeader(filename) { }); }); - // test(`${name}: use custom options.filename instead of form._uploadPath`, () => { + // test(`${name}: use custom options.originalFilename instead of form._uploadPath`, () => { // const form = getForm(name, { - // filename: (_) => path.join(__dirname, 'sasa'), + // originalFilename: (_) => path.join(__dirname, 'sasa'), // }); // }); }); diff --git a/test/unit/persistent-file.test.js b/test/unit/persistent-file.test.js index 92035d2e..d70833e1 100644 --- a/test/unit/persistent-file.test.js +++ b/test/unit/persistent-file.test.js @@ -9,7 +9,7 @@ const file = new PersistentFile({ name: 'cat.png', type: 'image/png', lastModifiedDate: now, - filename: 'cat.png', + originalFilename: 'cat.png', mimetype: 'image/png', }); @@ -18,6 +18,6 @@ test('PersistentFile#toJSON()', () => { expect(obj.path).toBe('/tmp/cat.png'); expect(obj.mimetype).toBe('image/png'); - expect(obj.filename).toBe('cat.png'); + expect(obj.originalFilename).toBe('cat.png'); expect(obj.mtime).toBe(now); }); diff --git a/test/unit/volatile-file.test.js b/test/unit/volatile-file.test.js index b3516180..a17a08b2 100644 --- a/test/unit/volatile-file.test.js +++ b/test/unit/volatile-file.test.js @@ -18,7 +18,7 @@ describe('VolatileFile', () => { file = new VolatileFile({ xname: 'cat.png', - filename: 'cat.png', + originalFilename: 'cat.png', mimetype: 'image/png', createFileWriteStream: writeStreamMock, }); @@ -46,7 +46,7 @@ describe('VolatileFile', () => { const obj = file.toJSON(); expect(obj.mimetype).toBe('image/png'); - expect(obj.filename).toBe('cat.png'); + expect(obj.originalFilename).toBe('cat.png'); }); test('toString()', () => { From 8340df26020a68397f4ed15a9965c9ee32a6882c Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Fri, 26 Feb 2021 17:24:15 +0100 Subject: [PATCH 22/35] refactor: direcly filepath --- README.md | 8 ++++---- src/Formidable.js | 10 +++++----- src/PersistentFile.js | 6 +++--- src/plugins/octetstream.js | 2 +- test/fixture/js/no-filename.js | 2 +- .../file-write-stream-handler-option.test.js | 2 +- test/integration/fixtures.test.js | 2 +- test/integration/octet-stream.test.js | 4 ++-- test/integration/store-files-option.test.js | 2 +- test/unit/persistent-file.test.js | 4 ++-- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 29fcee69..e14d5480 100644 --- a/README.md +++ b/README.md @@ -587,7 +587,7 @@ export interface File { // The path this file is being written to. You can modify this in the `'fileBegin'` event in // case you are unhappy with the way formidable generates a temporary path for your files. - file.path: string; + file.filepath: string; // The name this file had according to the uploading client. file.name: string | null; @@ -641,8 +641,8 @@ form.on('fileBegin', (formName, file) => { // formName the name in the form () or http filename for octetstream // file.originalFilename http filename or null if there was a parsing error // file.newFilename generated hexoid or what options.filename returned - // file.path default pathnme as per options.uploadDir and options.filename - // file.path = CUSTOM_PATH // to change the final path + // file.filepath default pathnme as per options.uploadDir and options.filename + // file.filepath = CUSTOM_PATH // to change the final path }); ``` @@ -654,7 +654,7 @@ Emitted whenever a field / file pair has been received. `file` is an instance of ```js form.on('file', (formname, file) => { // same as fileBegin, except - // it is too late to change file.path + // it is too late to change file.filepath // file.hash is available if options.hash was used }); ``` diff --git a/src/Formidable.js b/src/Formidable.js index 214afdb7..a4d081ea 100644 --- a/src/Formidable.js +++ b/src/Formidable.js @@ -323,7 +323,7 @@ class IncomingForm extends EventEmitter { const finalPath = this._joinDirectoryName(newFilename); const file = this._newFile({ newFilename, - path: finalPath, + filepath: finalPath, originalFilename: part.originalFilename, mimetype: part.mimetype, }); @@ -461,7 +461,7 @@ class IncomingForm extends EventEmitter { if (Array.isArray(this.openedFiles)) { this.openedFiles.forEach((file) => { file.destroy(); - setTimeout(fs.unlink, 0, file.path, () => {}); + setTimeout(fs.unlink, 0, file.filepath, () => {}); }); } } @@ -483,11 +483,11 @@ class IncomingForm extends EventEmitter { return new MultipartParser(this.options); } - _newFile({ path: filePath, originalFilename, mimetype, newFilename }) { + _newFile({ filepath, originalFilename, mimetype, newFilename }) { return this.options.fileWriteStreamHandler ? new VolatileFile({ newFilename, - path: filePath, // avoid shadow + filepath, originalFilename, mimetype, createFileWriteStream: this.options.fileWriteStreamHandler, @@ -495,7 +495,7 @@ class IncomingForm extends EventEmitter { }) : new PersistentFile({ newFilename, - path: filePath, + filepath, originalFilename, mimetype, hash: this.options.hash, diff --git a/src/PersistentFile.js b/src/PersistentFile.js index fca4374e..df48b36e 100644 --- a/src/PersistentFile.js +++ b/src/PersistentFile.js @@ -23,7 +23,7 @@ class PersistentFile extends EventEmitter { } open() { - this._writeStream = new fs.WriteStream(this.path); + this._writeStream = new fs.WriteStream(this.filepath); this._writeStream.on('error', (err) => { this.emit('error', err); }); @@ -32,7 +32,7 @@ class PersistentFile extends EventEmitter { toJSON() { const json = { size: this.size, - path: this.path, + filepath: this.filepath, newFilename: this.newFilename, mimetype: this.mimetype, mtime: this.lastModifiedDate, @@ -46,7 +46,7 @@ class PersistentFile extends EventEmitter { } toString() { - return `PersistentFile: ${this.originalFilename}, Path: ${this.path}`; + return `PersistentFile: ${this.originalFilename}, filepath: ${this.filepath}`; } write(buffer, cb) { diff --git a/src/plugins/octetstream.js b/src/plugins/octetstream.js index f76be9e4..d3e52742 100644 --- a/src/plugins/octetstream.js +++ b/src/plugins/octetstream.js @@ -35,7 +35,7 @@ function init(_self, _opts) { const finalPath = this._joinDirectoryName(newFilename); const file = this._newFile({ newFilename, - path: finalPath, + filepath: finalPath, originalFilename, mimetype, }); diff --git a/test/fixture/js/no-filename.js b/test/fixture/js/no-filename.js index 6fdc02ed..a64bdfa6 100644 --- a/test/fixture/js/no-filename.js +++ b/test/fixture/js/no-filename.js @@ -8,7 +8,7 @@ module.exports['generic.http'] = [ }, ]; -module.exports['originalFilename-name.http'] = [ +module.exports['filename-name.http'] = [ { type: 'file', name: 'upload', diff --git a/test/integration/file-write-stream-handler-option.test.js b/test/integration/file-write-stream-handler-option.test.js index 92487cb7..59a0e3e1 100644 --- a/test/integration/file-write-stream-handler-option.test.js +++ b/test/integration/file-write-stream-handler-option.test.js @@ -47,7 +47,7 @@ test('file write stream handler', (done) => { const { file } = files; assert.strictEqual(file.size, 301); - assert.strictEqual(typeof file.path, 'string'); + assert.strictEqual(typeof file.filepath, 'string'); const dirFiles = fs.readdirSync(DEFAULT_UPLOAD_DIR); assert.ok(dirFiles.length === 0); diff --git a/test/integration/fixtures.test.js b/test/integration/fixtures.test.js index 451746c8..7ceb6633 100644 --- a/test/integration/fixtures.test.js +++ b/test/integration/fixtures.test.js @@ -75,7 +75,7 @@ test('fixtures', (done) => { assert.strictEqual( file.hash, expectedPart.sha1, - `SHA1 error ${file.name} on ${file.path}`, + `SHA1 error ${file.name} on ${file.filepath}`, ); } } diff --git a/test/integration/octet-stream.test.js b/test/integration/octet-stream.test.js index 63067294..6e3d5fa2 100644 --- a/test/integration/octet-stream.test.js +++ b/test/integration/octet-stream.test.js @@ -25,8 +25,8 @@ test('octet stream', (done) => { assert.strictEqual(file.size, 301); - const uploaded = fs.readFileSync(file.path); - const original = fs.readFileSync(testFilePath); + const uploaded = fs.readFileSync(file.filepath); + const original = fs.readFileSync(testfilepath); assert.deepStrictEqual(uploaded, original); diff --git a/test/integration/store-files-option.test.js b/test/integration/store-files-option.test.js index 5c7d073a..cdf5ff47 100644 --- a/test/integration/store-files-option.test.js +++ b/test/integration/store-files-option.test.js @@ -36,7 +36,7 @@ test('store files option', (done) => { const { file } = files; assert.strictEqual(file.size, 301); - assert.strictEqual(typeof file.path, 'string'); + assert.strictEqual(typeof file.filepath, 'string'); const uploadedFileStats = fs.statSync(CUSTOM_UPLOAD_FILE_PATH); assert.ok(uploadedFileStats.size === file.size); diff --git a/test/unit/persistent-file.test.js b/test/unit/persistent-file.test.js index d70833e1..b8d04d8a 100644 --- a/test/unit/persistent-file.test.js +++ b/test/unit/persistent-file.test.js @@ -5,7 +5,7 @@ const PersistentFile = require('../../src/PersistentFile'); const now = new Date(); const file = new PersistentFile({ size: 1024, - path: '/tmp/cat.png', + filepath: '/tmp/cat.png', name: 'cat.png', type: 'image/png', lastModifiedDate: now, @@ -16,7 +16,7 @@ const file = new PersistentFile({ test('PersistentFile#toJSON()', () => { const obj = file.toJSON(); - expect(obj.path).toBe('/tmp/cat.png'); + expect(obj.filepath).toBe('/tmp/cat.png'); expect(obj.mimetype).toBe('image/png'); expect(obj.originalFilename).toBe('cat.png'); expect(obj.mtime).toBe(now); From 63402a1b4671ecd53e826ed894d0f3369f13ad88 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Fri, 26 Feb 2021 17:29:40 +0100 Subject: [PATCH 23/35] fix: test --- test/integration/octet-stream.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/octet-stream.test.js b/test/integration/octet-stream.test.js index 6e3d5fa2..95e69d5e 100644 --- a/test/integration/octet-stream.test.js +++ b/test/integration/octet-stream.test.js @@ -26,7 +26,7 @@ test('octet stream', (done) => { assert.strictEqual(file.size, 301); const uploaded = fs.readFileSync(file.filepath); - const original = fs.readFileSync(testfilepath); + const original = fs.readFileSync(testFilePath); assert.deepStrictEqual(uploaded, original); From 9e54b19fd954162254b38330c50f7586b4ef426b Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Fri, 26 Feb 2021 17:32:32 +0100 Subject: [PATCH 24/35] refactor: split hash into hashAlgorithm and hash --- README.md | 9 +++++---- src/Formidable.js | 6 +++--- src/PersistentFile.js | 4 ++-- src/VolatileFile.js | 4 ++-- test/integration/fixtures.test.js | 2 +- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index e14d5480..ae1c86c4 100644 --- a/README.md +++ b/README.md @@ -335,7 +335,7 @@ See it's defaults in [src/Formidable.js DEFAULT_OPTIONS](./src/Formidable.js) - `options.maxFieldsSize` **{number}** - default `20 * 1024 * 1024` (20mb); limit the amount of memory all fields together (except files) can allocate in bytes. -- `options.hash` **{string | false}** - default `false`; include checksums calculated +- `options.hashAlgorithm` **{string | false}** - default `false`; include checksums calculated for incoming files, set this to some hash algorithm, see [crypto.createHash](https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm_options) for available algorithms @@ -532,7 +532,7 @@ you can remove it from the `options.enabledPlugins`, like so const { Formidable } = require('formidable'); const form = new Formidable({ - hash: 'sha1', + hashAlgorithm: 'sha1', enabledPlugins: ['octetstream', 'querystring', 'json'], }); ``` @@ -599,8 +599,9 @@ export interface File { // Mostly here for compatibility with the [W3C File API Draft](http://dev.w3.org/2006/webapi/FileAPI/). file.lastModifiedDate: Date | null; - // If `options.hash` calculation was set, you can read the hex digest out of this var. - file.hash: string | 'sha1' | 'md5' | 'sha256' | null; + file.hashAlgorithm: false | |'sha1' | 'md5' | 'sha256' + // If `options.hashAlgorithm` calculation was set, you can read the hex digest out of this var. + file.hash: string | object | null; } ``` diff --git a/src/Formidable.js b/src/Formidable.js index a4d081ea..a0ecf33f 100644 --- a/src/Formidable.js +++ b/src/Formidable.js @@ -22,7 +22,7 @@ const DEFAULT_OPTIONS = { allowEmptyFiles: true, keepExtensions: false, encoding: 'utf-8', - hash: false, + hashAlgorithm: false, uploadDir: os.tmpdir(), multiples: false, enabledPlugins: ['octetstream', 'querystring', 'multipart', 'json'], @@ -491,14 +491,14 @@ class IncomingForm extends EventEmitter { originalFilename, mimetype, createFileWriteStream: this.options.fileWriteStreamHandler, - hash: this.options.hash, + hashAlgorithm: this.options.hashAlgorithm, }) : new PersistentFile({ newFilename, filepath, originalFilename, mimetype, - hash: this.options.hash, + hashAlgorithm: this.options.hashAlgorithm, }); } diff --git a/src/PersistentFile.js b/src/PersistentFile.js index df48b36e..ead47a03 100644 --- a/src/PersistentFile.js +++ b/src/PersistentFile.js @@ -15,8 +15,8 @@ class PersistentFile extends EventEmitter { this.size = 0; this._writeStream = null; - if (typeof this.hash === 'string') { - this.hash = crypto.createHash(properties.hash); + if (typeof this.hashAlgorithm === 'string') { + this.hash = crypto.createHash(this.hashAlgorithm); } else { this.hash = null; } diff --git a/src/VolatileFile.js b/src/VolatileFile.js index 4c77132e..9c371d9f 100644 --- a/src/VolatileFile.js +++ b/src/VolatileFile.js @@ -14,8 +14,8 @@ class VolatileFile extends EventEmitter { this.size = 0; this._writeStream = null; - if (typeof this.hash === 'string') { - this.hash = crypto.createHash(properties.hash); + if (typeof this.hashAlgorithm === 'string') { + this.hash = crypto.createHash(this.hashAlgorithm); } else { this.hash = null; } diff --git a/test/integration/fixtures.test.js b/test/integration/fixtures.test.js index 7ceb6633..d51a312f 100644 --- a/test/integration/fixtures.test.js +++ b/test/integration/fixtures.test.js @@ -89,7 +89,7 @@ test('fixtures', (done) => { server.once('request', (req, res) => { const form = formidable({ uploadDir: UPLOAD_DIR, - hash: 'sha1', + hashAlgorithm: 'sha1', multiples: true, }); form.parse(req); From 4f0988328ac65d73a541768787aad3f935c79eea Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Fri, 26 Feb 2021 17:34:49 +0100 Subject: [PATCH 25/35] feat: this.lastModifiedDate = null remains --- src/PersistentFile.js | 1 + src/VolatileFile.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/PersistentFile.js b/src/PersistentFile.js index ead47a03..ac6a01b4 100644 --- a/src/PersistentFile.js +++ b/src/PersistentFile.js @@ -13,6 +13,7 @@ class PersistentFile extends EventEmitter { Object.assign(this, properties); this.size = 0; + this.lastModifiedDate = null; this._writeStream = null; if (typeof this.hashAlgorithm === 'string') { diff --git a/src/VolatileFile.js b/src/VolatileFile.js index 9c371d9f..b5ca94ee 100644 --- a/src/VolatileFile.js +++ b/src/VolatileFile.js @@ -12,6 +12,7 @@ class VolatileFile extends EventEmitter { Object.assign(this, properties); this.size = 0; + this.lastModifiedDate = null; this._writeStream = null; if (typeof this.hashAlgorithm === 'string') { From 2d85616c5fce89a4bddb0e66bc4c960295176bc1 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Fri, 26 Feb 2021 17:37:58 +0100 Subject: [PATCH 26/35] refactor: finalpath: filepath --- src/Formidable.js | 4 ++-- src/plugins/octetstream.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Formidable.js b/src/Formidable.js index a0ecf33f..8f213e2f 100644 --- a/src/Formidable.js +++ b/src/Formidable.js @@ -320,10 +320,10 @@ class IncomingForm extends EventEmitter { this._flushing += 1; const newFilename = this._getNewName(part); - const finalPath = this._joinDirectoryName(newFilename); + const filepath = this._joinDirectoryName(newFilename); const file = this._newFile({ newFilename, - filepath: finalPath, + filepath, originalFilename: part.originalFilename, mimetype: part.mimetype, }); diff --git a/src/plugins/octetstream.js b/src/plugins/octetstream.js index d3e52742..96fee406 100644 --- a/src/plugins/octetstream.js +++ b/src/plugins/octetstream.js @@ -32,10 +32,10 @@ function init(_self, _opts) { mimetype, }; const newFilename = this._getNewName(thisPart); - const finalPath = this._joinDirectoryName(newFilename); + const filepath = this._joinDirectoryName(newFilename); const file = this._newFile({ newFilename, - filepath: finalPath, + filepath, originalFilename, mimetype, }); From 48f25fc91bf879286b90733869e19b7980b3064a Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Fri, 26 Feb 2021 17:39:06 +0100 Subject: [PATCH 27/35] fix: change order --- src/PersistentFile.js | 2 +- src/VolatileFile.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PersistentFile.js b/src/PersistentFile.js index ac6a01b4..9f09d6ce 100644 --- a/src/PersistentFile.js +++ b/src/PersistentFile.js @@ -10,10 +10,10 @@ class PersistentFile extends EventEmitter { constructor(properties) { super(); + this.lastModifiedDate = null; Object.assign(this, properties); this.size = 0; - this.lastModifiedDate = null; this._writeStream = null; if (typeof this.hashAlgorithm === 'string') { diff --git a/src/VolatileFile.js b/src/VolatileFile.js index b5ca94ee..ab1b403f 100644 --- a/src/VolatileFile.js +++ b/src/VolatileFile.js @@ -9,10 +9,10 @@ class VolatileFile extends EventEmitter { constructor(properties) { super(); + this.lastModifiedDate = null; Object.assign(this, properties); this.size = 0; - this.lastModifiedDate = null; this._writeStream = null; if (typeof this.hashAlgorithm === 'string') { From 3c144938d0620ecedd13faf7944f5948f0a98180 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Fri, 26 Feb 2021 17:46:14 +0100 Subject: [PATCH 28/35] refactor: better be explicit --- src/PersistentFile.js | 4 ++-- src/VolatileFile.js | 4 ++-- test/unit/persistent-file.test.js | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/PersistentFile.js b/src/PersistentFile.js index 9f09d6ce..cfa00ace 100644 --- a/src/PersistentFile.js +++ b/src/PersistentFile.js @@ -7,11 +7,11 @@ const crypto = require('crypto'); const { EventEmitter } = require('events'); class PersistentFile extends EventEmitter { - constructor(properties) { + constructor({ filepath, newFilename, originalFilename, mimetype, hashAlgorithm }) { super(); this.lastModifiedDate = null; - Object.assign(this, properties); + Object.assign(this, { filepath, newFilename, originalFilename, mimetype, hashAlgorithm }); this.size = 0; this._writeStream = null; diff --git a/src/VolatileFile.js b/src/VolatileFile.js index ab1b403f..01af4286 100644 --- a/src/VolatileFile.js +++ b/src/VolatileFile.js @@ -6,11 +6,11 @@ const crypto = require('crypto'); const { EventEmitter } = require('events'); class VolatileFile extends EventEmitter { - constructor(properties) { + constructor({ filepath, newFilename, originalFilename, mimetype, hashAlgorithm, createFileWriteStream }) { super(); this.lastModifiedDate = null; - Object.assign(this, properties); + Object.assign(this, { filepath, newFilename, originalFilename, mimetype, hashAlgorithm, createFileWriteStream }); this.size = 0; this._writeStream = null; diff --git a/test/unit/persistent-file.test.js b/test/unit/persistent-file.test.js index b8d04d8a..61a3e46d 100644 --- a/test/unit/persistent-file.test.js +++ b/test/unit/persistent-file.test.js @@ -19,5 +19,4 @@ test('PersistentFile#toJSON()', () => { expect(obj.filepath).toBe('/tmp/cat.png'); expect(obj.mimetype).toBe('image/png'); expect(obj.originalFilename).toBe('cat.png'); - expect(obj.mtime).toBe(now); }); From 58e4a612b5836dcaf7dcdc93966b960161b36256 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Fri, 26 Feb 2021 17:49:03 +0100 Subject: [PATCH 29/35] feat: display more in toString --- src/PersistentFile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PersistentFile.js b/src/PersistentFile.js index cfa00ace..efea9dba 100644 --- a/src/PersistentFile.js +++ b/src/PersistentFile.js @@ -47,7 +47,7 @@ class PersistentFile extends EventEmitter { } toString() { - return `PersistentFile: ${this.originalFilename}, filepath: ${this.filepath}`; + return `PersistentFile: ${this._file.newFilename}, Original: ${this._file.originalFilename}, Path: ${this._file.filepath}`; } write(buffer, cb) { From 4ff84472e813c2bf47f3a1181c8d6ad836da4d34 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Tue, 2 Mar 2021 13:49:06 +0100 Subject: [PATCH 30/35] docs: update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b16c9d01..c7dc86d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ### Unreleased (`canary` & `dev` dist-tags) + * rename option hash into hashAlgorithm + * rename file.path into file.filepath + * rename file.type into file.mimetype + * split file.name into file.newFilename and file.originalFilename + * prevent directory traversal attacks by default + * options.fileWriteStreamHandler receives the entire file instance (including file.newFilename) * Add fileWriteStreamHandler option * Add allowEmptyFiles and minFileSize options * Test only on Node.js >= v10. Support only Node LTS and latest ([#515](https://github.com/node-formidable/node-formidable/pull/515)) From fec82d37e32d3d71651beb12c7bed683ea6611f8 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Tue, 2 Mar 2021 13:49:20 +0100 Subject: [PATCH 31/35] chore: update version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8b7ac256..2c70f54d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "formidable", - "version": "2.0.0-canary.20210216", + "version": "2.0.0-canary.20210302", "license": "MIT", "description": "A node.js module for parsing form data, especially file uploads.", "homepage": "https://github.com/node-formidable/formidable", From 4bce5f75ee816cb1e0b8055dee3fcc4dafd0a74d Mon Sep 17 00:00:00 2001 From: Walle Cyril Date: Fri, 5 Mar 2021 00:59:15 +0100 Subject: [PATCH 32/35] fix: revert, renamed too much --- test/standalone/content-transfer-encoding.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/standalone/content-transfer-encoding.test.js b/test/standalone/content-transfer-encoding.test.js index 59dfa822..e551d93e 100644 --- a/test/standalone/content-transfer-encoding.test.js +++ b/test/standalone/content-transfer-encoding.test.js @@ -29,12 +29,12 @@ test('content transfer encoding', (done) => { const body = '--foo\r\n' + - 'Content-Disposition: form-data; name="file1"; originalFilename="file1"\r\n' + + 'Content-Disposition: form-data; name="file1"; filename="file1"\r\n' + 'Content-Type: application/octet-stream\r\n' + '\r\nThis is the first file\r\n' + '--foo\r\n' + 'Content-Type: application/octet-stream\r\n' + - 'Content-Disposition: form-data; name="file2"; originalFilename="file2"\r\n' + + 'Content-Disposition: form-data; name="file2"; filename="file2"\r\n' + 'Content-Transfer-Encoding: unknown\r\n' + '\r\nThis is the second file\r\n' + '--foo--\r\n'; From 97181fe5c9829d2df5dcf290ebf423f048193e4f Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Fri, 5 Mar 2021 16:57:21 +0100 Subject: [PATCH 33/35] fix: _flush is more appropriate --- src/parsers/Multipart.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parsers/Multipart.js b/src/parsers/Multipart.js index b148ddfd..23a298aa 100644 --- a/src/parsers/Multipart.js +++ b/src/parsers/Multipart.js @@ -62,7 +62,7 @@ class MultipartParser extends Transform { this.flags = 0; } - _final(done) { + _flush(done) { if ( (this.state === STATE.HEADER_FIELD_START && this.index === 0) || (this.state === STATE.PART_DATA && this.index === this.boundary.length) From 7700225065183e64a5cd444259ad3554e7b6cf49 Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Thu, 11 Mar 2021 17:32:11 +0100 Subject: [PATCH 34/35] feat: add upload filter --- examples/with-http.js | 24 ++++++++++++++---------- src/Formidable.js | 7 +++++++ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/examples/with-http.js b/examples/with-http.js index d5d3c7b2..16a14239 100644 --- a/examples/with-http.js +++ b/examples/with-http.js @@ -10,16 +10,20 @@ const server = http.createServer((req, res) => { multiples: true, uploadDir: `uploads`, keepExtensions: true, - filename(/*name, ext, part, form*/) { - /* name basename of the http originalFilename - ext with the dot ".txt" only if keepExtension is true - */ - // slugify to avoid invalid filenames - // substr to define a maximum length - // return `${slugify(name).${slugify(ext, separator: '')}`.substr(0, 100); - return 'yo.txt'; // or completly different name - // return 'z/yo.txt'; // subdirectory - }, + // filename(/*name, ext, part, form*/) { + // /* name basename of the http originalFilename + // ext with the dot ".txt" only if keepExtension is true + // */ + // // slugify to avoid invalid filenames + // // substr to define a maximum length + // // return `${slugify(name).${slugify(ext, separator: '')}`.substr(0, 100); + // return 'yo.txt'; // or completly different name + // // return 'z/yo.txt'; // subdirectory + // }, + filter: function ({name, originalFilename, mimetype}) { + // keep only images + return mimetype && mimetype.includes("image"); + } }); form.parse(req, (err, fields, files) => { diff --git a/src/Formidable.js b/src/Formidable.js index 46b4a752..05427008 100644 --- a/src/Formidable.js +++ b/src/Formidable.js @@ -27,6 +27,9 @@ const DEFAULT_OPTIONS = { enabledPlugins: ['octetstream', 'querystring', 'multipart', 'json'], fileWriteStreamHandler: null, defaultInvalidName: 'invalid-name', + filter: function () { + return true; + }, }; const PersistentFile = require('./PersistentFile'); @@ -316,6 +319,10 @@ class IncomingForm extends EventEmitter { return; } + if (!this.options.filter(part)) { + return; + } + this._flushing += 1; const newFilename = this._getNewName(part); From a206d9a9547c758625f144b76dc484e53812bc8c Mon Sep 17 00:00:00 2001 From: Cyril Walle Date: Thu, 11 Mar 2021 17:47:12 +0100 Subject: [PATCH 35/35] docs: document options.filter --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index ae1c86c4..eb65757c 100644 --- a/README.md +++ b/README.md @@ -356,6 +356,8 @@ See it's defaults in [src/Formidable.js DEFAULT_OPTIONS](./src/Formidable.js) fields that have names ending with '[]'. - `options.filename` **{function}** - default `undefined` Use it to control newFilename. Must return a string. Will be joined with options.uploadDir. +- `options.filter` **{function}** - default function that always returns true. + Use it to filter files before they are uploaded. Must return a boolean. #### `options.filename` **{function}** function (name, ext, part, form) -> string @@ -372,6 +374,20 @@ form.bytesReceived; form.bytesExpected; ``` +#### `options.filter` **{function}** function ({name, originalFilename, mimetype}) -> boolean + +**Note:** use an outside variable to cancel all uploads upon the first error + +```js +const options { + filter: function ({name, originalFilename, mimetype}) { + // keep only images + return mimetype && mimetype.includes("image"); + } +}; +``` + + ### .parse(request, callback) Parses an incoming Node.js `request` containing form data. If `callback` is