CVE-2026-12143
Description
CRLF injection in form-data library lets attackers inject headers or form fields when attacker-controlled input is used as a multipart field name or filename.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
CRLF injection in form-data library lets attackers inject headers or form fields when attacker-controlled input is used as a multipart field name or filename.
Vulnerability
The form-data library (versions 4.0.5 and earlier, as well as 3.x and 2.x through the corresponding latest releases before the fix) fails to sanitize carriage return (CR), line feed (LF), and double-quote (") characters in the field argument to FormData#append and the filename option. These values are concatenated verbatim into the Content-Disposition header, enabling CRLF injection (CWE-93) [2]. An application that passes attacker-controlled data as a field name or filename—for example, an API gateway that maps JSON object keys to multipart field names—allows the attacker to terminate the header line and inject additional headers or even entire multipart parts into the forwarded request. This affects all versions through 4.0.5 (and corresponding 3.0.x, 2.5.x prior to the fix releases) [1][3][4].
Exploitation
An attacker needs the ability to control the string used as a field name or filename in a multipart request constructed by the vulnerable form-data library. No authentication is required if the application exposes an endpoint that accepts untrusted input and uses it directly as a form field name. The attacker provides a malicious string containing %0D%0A (CRLF) sequences to break out of the Content-Disposition header line, then injects arbitrary HTTP headers or additional multipart body parts. For example, injecting a complete Content-Disposition header line for a new part with a field name like is_admin and value true would cause the backend to interpret it as an additional form field [1][2].
Impact
Successful exploitation allows the attacker to add or override form fields seen by the downstream multipart parser. This can lead to privilege escalation (e.g., setting an is_admin=true field), data corruption, or bypassing access controls—depending on how the server processes the injected parameters. The impact is at the application logic level, potentially achieving unintended state changes or unauthorized data access. The CIA outcome is primarily integrity violation, with possible confidentiality and availability implications if the injected fields influence backend behavior [1][2].
Mitigation
The vulnerability is fixed in form-data versions 2.5.6, 3.0.5, and 4.0.6, released on 2026-06-12 [3][4]. The fix escapes CR, LF, and " as %0D, %0A, and %22 respectively, matching the WHATWG HTML multipart/form-data encoding algorithm [1]. Applications should upgrade to these patched versions immediately. Workarounds: if upgrade is not possible, applications must sanitize or reject any user-supplied input that will be used as a field name or filename, specifically stripping or encoding CR, LF, and double-quote characters before passing them to FormData#append. The vulnerability is not exploitable if the application only uses fixed, trusted field names [1].
AI Insight generated on Jun 12, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2- Package: https://npmjs.com/package/form-data
Patches
41ecd6d1ca3f77Merge tags v4.0.6, v3.0.5, and v2.5.6
1 file changed · +2 −0
test/integration/test-header-injection.js+2 −0 modified@@ -1,5 +1,7 @@ 'use strict'; +var Buffer = require('safe-buffer').Buffer; // eslint-disable-line no-shadow + var common = require('../common'); var assert = common.assert;
2 files changed · +30 −3
CHANGELOG.md+29 −2 modified@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v4.0.6](https://github.com/form-data/form-data/compare/v4.0.5...v4.0.6) - 2026-06-12 + +### Commits + +- [Fix] escape CR, LF, and `"` in field names and filenames [`8dff42c`](https://github.com/form-data/form-data/commit/8dff42c6da654ed4e7ad4acb7f8ccd3831217c99) +- [Dev Deps] update `@ljharb/eslint-config`, `auto-changelog`, `tape` [`f31d21e`](https://github.com/form-data/form-data/commit/f31d21ef10bf46e46344c3ee4f99acbef6be43e1) +- [Deps] update `hasown`, `mime-types` [`92ae0eb`](https://github.com/form-data/form-data/commit/92ae0eb5da94d6f01925d5f4fcffb2a1e50ed7cd) +- [Dev Deps] update `js-randomness-predictor` [`67b0f65`](https://github.com/form-data/form-data/commit/67b0f65c2e0b065a511d42227d35e4d367644e97) + ## [v4.0.5](https://github.com/form-data/form-data/compare/v4.0.4...v4.0.5) - 2025-11-17 ### Commits @@ -94,7 +103,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix (npmignore): ignore temporary build files [`335ad19`](https://github.com/form-data/form-data/commit/335ad19c6e17dc2d7298ffe0e9b37ba63600e94b) - fix: move util.isArray to Array.isArray [`440d3be`](https://github.com/form-data/form-data/commit/440d3bed752ac2f9213b4c2229dbccefe140e5fa) -## [v4.0.0](https://github.com/form-data/form-data/compare/v3.0.4...v4.0.0) - 2021-02-15 +## [v4.0.0](https://github.com/form-data/form-data/compare/v3.0.5...v4.0.0) - 2021-02-15 ### Merged @@ -105,6 +114,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix typo [`e705c0a`](https://github.com/form-data/form-data/commit/e705c0a1fdaf90d21501f56460b93e43a18bd435) - Update README for custom stream behavior [`6dd8624`](https://github.com/form-data/form-data/commit/6dd8624b2999e32768d62752c9aae5845a803b0d) +## [v3.0.5](https://github.com/form-data/form-data/compare/v3.0.4...v3.0.5) - 2026-06-12 + +### Commits + +- [Fix] escape CR, LF, and `"` in field names and filenames [`8777e67`](https://github.com/form-data/form-data/commit/8777e67fbd0282d3dcba81f974fbdd91062c5b23) +- [Dev Deps] update `@ljharb/eslint-config`, `auto-changelog`, `eslint`, `tape` [`27c61a5`](https://github.com/form-data/form-data/commit/27c61a5deed84798be105c96605cb8bd00502dcd) +- [Deps] update `hasown` [`6a8a1c6`](https://github.com/form-data/form-data/commit/6a8a1c6d04da36e15c80b16ecc4c0265082b3213) + ## [v3.0.4](https://github.com/form-data/form-data/compare/v3.0.3...v3.0.4) - 2025-07-16 ### Fixed @@ -166,7 +183,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: add setBoundary method [`55d90ce`](https://github.com/form-data/form-data/commit/55d90ce4a4c22b0ea0647991d85cb946dfb7395b) -## [v3.0.0](https://github.com/form-data/form-data/compare/v2.5.5...v3.0.0) - 2019-11-05 +## [v3.0.0](https://github.com/form-data/form-data/compare/v2.5.6...v3.0.0) - 2019-11-05 ### Merged @@ -190,6 +207,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Pass options to constructor if not used with new [`4bde68e`](https://github.com/form-data/form-data/commit/4bde68e12de1ba90fefad2e7e643f6375b902763) - Make userHeaders optional [`2b4e478`](https://github.com/form-data/form-data/commit/2b4e4787031490942f2d1ee55c56b85a250875a7) +## [v2.5.6](https://github.com/form-data/form-data/compare/v2.5.5...v2.5.6) - 2026-06-12 + +### Commits + +- [Fix] escape CR, LF, and `"` in field names and filenames [`b620316`](https://github.com/form-data/form-data/commit/b62031603c2d7c329b2a369b49466790f0ba6314) +- [Dev Deps] update `@ljharb/eslint-config`, `auto-changelog`, `eslint`, `tape` [`12be578`](https://github.com/form-data/form-data/commit/12be578e936fd77eee75e2e656955f5343c4b80f) +- [Dev Deps] update `js-randomness-predictor` [`46cfd23`](https://github.com/form-data/form-data/commit/46cfd23bd40be14cfa0391e1c5357c4d74098f23) +- [Tests] use `safe-buffer` so the header-injection test runs on node < 4 [`633044a`](https://github.com/form-data/form-data/commit/633044a57a7b19f41cec2271ffd24afa2f6280af) +- [Deps] update `hasown` [`e3b96ee`](https://github.com/form-data/form-data/commit/e3b96eef1661bca8ea4297de057b78bf2734e900) + ## [v2.5.5](https://github.com/form-data/form-data/compare/v2.5.4...v2.5.5) - 2025-07-18 ### Commits
package.json+1 −1 modified@@ -2,7 +2,7 @@ "author": "Felix Geisendörfer <felix@debuggable.com> (http://debuggable.com/)", "name": "form-data", "description": "A library to create readable \"multipart/form-data\" streams. Can be used to submit forms and file uploads to other web applications.", - "version": "4.0.5", + "version": "4.0.6", "repository": { "type": "git", "url": "git://github.com/form-data/form-data.git"
2 files changed · +58 −3
CHANGELOG.md+57 −2 modified@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v4.0.5](https://github.com/form-data/form-data/compare/v4.0.4...v4.0.5) - 2025-11-17 + +### Commits + +- [Tests] Switch to newer v8 prediction library; enable node 24 testing [`16e0076`](https://github.com/form-data/form-data/commit/16e00765342106876f98a1c9703314006c9e937a) +- [Dev Deps] update `@ljharb/eslint-config`, `eslint` [`5822467`](https://github.com/form-data/form-data/commit/5822467f0ec21f6ad613c1c90856375e498793c7) +- [Fix] set Symbol.toStringTag in the proper place [`76d0dee`](https://github.com/form-data/form-data/commit/76d0dee43933b5e167f7f09e5d9cbbd1cf911aa7) + ## [v4.0.4](https://github.com/form-data/form-data/compare/v4.0.3...v4.0.4) - 2025-07-16 ### Commits @@ -86,7 +94,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix (npmignore): ignore temporary build files [`335ad19`](https://github.com/form-data/form-data/commit/335ad19c6e17dc2d7298ffe0e9b37ba63600e94b) - fix: move util.isArray to Array.isArray [`440d3be`](https://github.com/form-data/form-data/commit/440d3bed752ac2f9213b4c2229dbccefe140e5fa) -## [v4.0.0](https://github.com/form-data/form-data/compare/v3.0.4...v4.0.0) - 2021-02-15 +## [v4.0.0](https://github.com/form-data/form-data/compare/v3.0.5...v4.0.0) - 2021-02-15 ### Merged @@ -97,6 +105,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix typo [`e705c0a`](https://github.com/form-data/form-data/commit/e705c0a1fdaf90d21501f56460b93e43a18bd435) - Update README for custom stream behavior [`6dd8624`](https://github.com/form-data/form-data/commit/6dd8624b2999e32768d62752c9aae5845a803b0d) +## [v3.0.5](https://github.com/form-data/form-data/compare/v3.0.4...v3.0.5) - 2026-06-12 + +### Commits + +- [Fix] escape CR, LF, and `"` in field names and filenames [`8777e67`](https://github.com/form-data/form-data/commit/8777e67fbd0282d3dcba81f974fbdd91062c5b23) +- [Dev Deps] update `@ljharb/eslint-config`, `auto-changelog`, `eslint`, `tape` [`27c61a5`](https://github.com/form-data/form-data/commit/27c61a5deed84798be105c96605cb8bd00502dcd) +- [Deps] update `hasown` [`6a8a1c6`](https://github.com/form-data/form-data/commit/6a8a1c6d04da36e15c80b16ecc4c0265082b3213) + ## [v3.0.4](https://github.com/form-data/form-data/compare/v3.0.3...v3.0.4) - 2025-07-16 ### Fixed @@ -158,7 +174,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: add setBoundary method [`55d90ce`](https://github.com/form-data/form-data/commit/55d90ce4a4c22b0ea0647991d85cb946dfb7395b) -## [v3.0.0](https://github.com/form-data/form-data/compare/v2.5.3...v3.0.0) - 2019-11-05 +## [v3.0.0](https://github.com/form-data/form-data/compare/v2.5.6...v3.0.0) - 2019-11-05 ### Merged @@ -182,6 +198,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Pass options to constructor if not used with new [`4bde68e`](https://github.com/form-data/form-data/commit/4bde68e12de1ba90fefad2e7e643f6375b902763) - Make userHeaders optional [`2b4e478`](https://github.com/form-data/form-data/commit/2b4e4787031490942f2d1ee55c56b85a250875a7) +## [v2.5.6](https://github.com/form-data/form-data/compare/v2.5.5...v2.5.6) - 2026-06-12 + +### Commits + +- [Fix] escape CR, LF, and `"` in field names and filenames [`b620316`](https://github.com/form-data/form-data/commit/b62031603c2d7c329b2a369b49466790f0ba6314) +- [Dev Deps] update `@ljharb/eslint-config`, `auto-changelog`, `eslint`, `tape` [`12be578`](https://github.com/form-data/form-data/commit/12be578e936fd77eee75e2e656955f5343c4b80f) +- [Dev Deps] update `js-randomness-predictor` [`46cfd23`](https://github.com/form-data/form-data/commit/46cfd23bd40be14cfa0391e1c5357c4d74098f23) +- [Tests] use `safe-buffer` so the header-injection test runs on node < 4 [`633044a`](https://github.com/form-data/form-data/commit/633044a57a7b19f41cec2271ffd24afa2f6280af) +- [Deps] update `hasown` [`e3b96ee`](https://github.com/form-data/form-data/commit/e3b96eef1661bca8ea4297de057b78bf2734e900) + +## [v2.5.5](https://github.com/form-data/form-data/compare/v2.5.4...v2.5.5) - 2025-07-18 + +### Commits + +- [meta] actually ensure the readme backup isn’t published [`10626c0`](https://github.com/form-data/form-data/commit/10626c0a9b78c7d3fcaa51772265015ee0afc25c) +- [Fix] use proper dependency [`026abe5`](https://github.com/form-data/form-data/commit/026abe5c5c0489d8a2ccb59d5cfd14fb63078377) + +## [v2.5.4](https://github.com/form-data/form-data/compare/v2.5.3...v2.5.4) - 2025-07-17 + +### Fixed + +- [Fix] `append`: avoid a crash on nullish values [`#577`](https://github.com/form-data/form-data/issues/577) + +### Commits + +- [eslint] update linting config [`8bf2492`](https://github.com/form-data/form-data/commit/8bf2492e0555d41ff58fa04c91593af998f87a3c) +- [meta] add `auto-changelog` [`b5101ad`](https://github.com/form-data/form-data/commit/b5101ad3d5f73cfd0143aae3735b92826fd731ea) +- [Tests] handle predict-v8-randomness failures in node < 17 and node > 23 [`0e93122`](https://github.com/form-data/form-data/commit/0e93122358414942393d9c2dc434ae69e58be7c8) +- [Fix] Switch to using `crypto` random for boundary values [`b88316c`](https://github.com/form-data/form-data/commit/b88316c94bb004323669cd3639dc8bb8262539eb) +- [Fix] validate boundary type in `setBoundary()` method [`131ae5e`](https://github.com/form-data/form-data/commit/131ae5efa30b9c608add4faef3befb38aa2e1bf1) +- [Tests] Switch to newer v8 prediction library; enable node 24 testing [`c97cfbe`](https://github.com/form-data/form-data/commit/c97cfbed9eb6d2d4b5d53090f69ded4bf9fd8a21) +- [Refactor] use `hasown` [`97ac9c2`](https://github.com/form-data/form-data/commit/97ac9c208be0b83faeee04bb3faef1ed3474ee4c) +- [meta] remove local commit hooks [`be99d4e`](https://github.com/form-data/form-data/commit/be99d4eea5ce47139c23c1f0914596194019d7fb) +- [Dev Deps] remove unused deps [`ddbc89b`](https://github.com/form-data/form-data/commit/ddbc89b6d6d64f730bcb27cb33b7544068466a05) +- [meta] fix scripts to use prepublishOnly [`e351a97`](https://github.com/form-data/form-data/commit/e351a97e9f6c57c74ffd01625e83b09de805d08a) +- [Dev Deps] remove unused script [`8f23366`](https://github.com/form-data/form-data/commit/8f233664842da5bd605ce85541defc713d1d1e0a) +- [Dev Deps] add missing peer dep [`02ff026`](https://github.com/form-data/form-data/commit/02ff026fda71f9943cfdd5754727c628adb8d135) +- [meta] fix readme capitalization [`2fd5f61`](https://github.com/form-data/form-data/commit/2fd5f61ebfb526cd015fb8e7b8b8c1add4a38872) + ## [v2.5.3](https://github.com/form-data/form-data/compare/v2.5.2...v2.5.3) - 2025-02-14 ### Merged
package.json+1 −1 modified@@ -2,7 +2,7 @@ "author": "Felix Geisendörfer <felix@debuggable.com> (http://debuggable.com/)", "name": "form-data", "description": "A library to create readable \"multipart/form-data\" streams. Can be used to submit forms and file uploads to other web applications.", - "version": "3.0.4", + "version": "3.0.5", "repository": { "type": "git", "url": "git://github.com/form-data/form-data.git"
2 files changed · +20 −2
CHANGELOG.md+19 −1 modified@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v4.0.5](https://github.com/form-data/form-data/compare/v4.0.4...v4.0.5) - 2025-11-17 + +### Commits + +- [Tests] Switch to newer v8 prediction library; enable node 24 testing [`16e0076`](https://github.com/form-data/form-data/commit/16e00765342106876f98a1c9703314006c9e937a) +- [Dev Deps] update `@ljharb/eslint-config`, `eslint` [`5822467`](https://github.com/form-data/form-data/commit/5822467f0ec21f6ad613c1c90856375e498793c7) +- [Fix] set Symbol.toStringTag in the proper place [`76d0dee`](https://github.com/form-data/form-data/commit/76d0dee43933b5e167f7f09e5d9cbbd1cf911aa7) + ## [v4.0.4](https://github.com/form-data/form-data/compare/v4.0.3...v4.0.4) - 2025-07-16 ### Commits @@ -158,7 +166,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: add setBoundary method [`55d90ce`](https://github.com/form-data/form-data/commit/55d90ce4a4c22b0ea0647991d85cb946dfb7395b) -## [v3.0.0](https://github.com/form-data/form-data/compare/v2.5.5...v3.0.0) - 2019-11-05 +## [v3.0.0](https://github.com/form-data/form-data/compare/v2.5.6...v3.0.0) - 2019-11-05 ### Merged @@ -182,6 +190,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Pass options to constructor if not used with new [`4bde68e`](https://github.com/form-data/form-data/commit/4bde68e12de1ba90fefad2e7e643f6375b902763) - Make userHeaders optional [`2b4e478`](https://github.com/form-data/form-data/commit/2b4e4787031490942f2d1ee55c56b85a250875a7) +## [v2.5.6](https://github.com/form-data/form-data/compare/v2.5.5...v2.5.6) - 2026-06-12 + +### Commits + +- [Fix] escape CR, LF, and `"` in field names and filenames [`b620316`](https://github.com/form-data/form-data/commit/b62031603c2d7c329b2a369b49466790f0ba6314) +- [Dev Deps] update `@ljharb/eslint-config`, `auto-changelog`, `eslint`, `tape` [`12be578`](https://github.com/form-data/form-data/commit/12be578e936fd77eee75e2e656955f5343c4b80f) +- [Dev Deps] update `js-randomness-predictor` [`46cfd23`](https://github.com/form-data/form-data/commit/46cfd23bd40be14cfa0391e1c5357c4d74098f23) +- [Tests] use `safe-buffer` so the header-injection test runs on node < 4 [`633044a`](https://github.com/form-data/form-data/commit/633044a57a7b19f41cec2271ffd24afa2f6280af) +- [Deps] update `hasown` [`e3b96ee`](https://github.com/form-data/form-data/commit/e3b96eef1661bca8ea4297de057b78bf2734e900) + ## [v2.5.5](https://github.com/form-data/form-data/compare/v2.5.4...v2.5.5) - 2025-07-18 ### Commits
package.json+1 −1 modified@@ -2,7 +2,7 @@ "author": "Felix Geisendörfer <felix@debuggable.com> (http://debuggable.com/)", "name": "form-data", "description": "A library to create readable \"multipart/form-data\" streams. Can be used to submit forms and file uploads to other web applications.", - "version": "2.5.5", + "version": "2.5.6", "repository": { "type": "git", "url": "git://github.com/form-data/form-data.git"
46cfd23bd40b[Dev Deps] update `js-randomness-predictor`
1 file changed · +1 −1
package.json+1 −1 modified@@ -60,7 +60,7 @@ "formidable": "^1.2.6", "in-publish": "^2.0.1", "istanbul": "^0.4.5", - "js-randomness-predictor": "^1.5.5", + "js-randomness-predictor": "^3.6.0", "obake": "^0.1.2", "phantomjs-prebuilt": "^2.1.16", "pkgfiles": "^2.3.2",
633044a57a7b[Tests] use `safe-buffer` so the header-injection test runs on node < 4
1 file changed · +2 −0
test/integration/test-header-injection.js+2 −0 modified@@ -1,5 +1,7 @@ 'use strict'; +var Buffer = require('safe-buffer').Buffer; // eslint-disable-line no-shadow + var common = require('../common'); var assert = common.assert;
e3b96eef1661[Deps] update `hasown`
1 file changed · +1 −1
package.json+1 −1 modified@@ -42,7 +42,7 @@ "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", + "hasown": "^2.0.4", "mime-types": "^2.1.35", "safe-buffer": "^5.2.1" },
12be578e936f[Dev Deps] update `@ljharb/eslint-config`, `auto-changelog`, `eslint`, `tape`
1 file changed · +4 −4
package.json+4 −4 modified@@ -47,14 +47,14 @@ "safe-buffer": "^5.2.1" }, "devDependencies": { - "@ljharb/eslint-config": "^21.2.0", - "auto-changelog": "^2.5.0", + "@ljharb/eslint-config": "^22.2.3", + "auto-changelog": "^2.6.0", "browserify": "^13.3.0", "browserify-istanbul": "^2.0.0", "coveralls": "^3.1.1", "cross-spawn": "^4.0.2", "encoding": "^0.1.13", - "eslint": "=8.8.0", + "eslint": "^8.57.1", "fake": "^0.2.2", "far": "^0.0.7", "formidable": "^1.2.6", @@ -69,7 +69,7 @@ "request": "~2.87.0", "rimraf": "^2.7.1", "semver": "^6.3.1", - "tape": "^5.9.0" + "tape": "^5.10.1" }, "license": "MIT", "auto-changelog": {
6a8a1c6d04da[Deps] update `hasown`
1 file changed · +1 −1
package.json+1 −1 modified@@ -43,7 +43,7 @@ "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", + "hasown": "^2.0.4", "mime-types": "^2.1.35" }, "devDependencies": {
27c61a5deed8[Dev Deps] update `@ljharb/eslint-config`, `auto-changelog`, `eslint`, `tape`
1 file changed · +4 −4
package.json+4 −4 modified@@ -47,13 +47,13 @@ "mime-types": "^2.1.35" }, "devDependencies": { - "@ljharb/eslint-config": "^21.2.0", - "auto-changelog": "^2.5.0", + "@ljharb/eslint-config": "^22.2.3", + "auto-changelog": "^2.6.0", "browserify": "^13.3.0", "browserify-istanbul": "^2.0.0", "coveralls": "^3.1.1", "cross-spawn": "^6.0.6", - "eslint": "=8.8.0", + "eslint": "^8.57.1", "fake": "^0.2.2", "far": "^0.0.7", "formidable": "^1.2.6", @@ -68,7 +68,7 @@ "request": "~2.87.0", "rimraf": "^2.7.1", "semver": "^6.3.1", - "tape": "^5.9.0" + "tape": "^5.10.1" }, "license": "MIT", "auto-changelog": {
92ae0eb5da94[Deps] update `hasown`, `mime-types`
1 file changed · +2 −2
package.json+2 −2 modified@@ -43,8 +43,8 @@ "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "devDependencies": { "@ljharb/eslint-config": "^22.2.3",
f31d21ef10bf[Dev Deps] update `@ljharb/eslint-config`, `auto-changelog`, `tape`
1 file changed · +3 −3
package.json+3 −3 modified@@ -47,8 +47,8 @@ "mime-types": "^2.1.12" }, "devDependencies": { - "@ljharb/eslint-config": "^21.4.0", - "auto-changelog": "^2.5.0", + "@ljharb/eslint-config": "^22.2.3", + "auto-changelog": "^2.6.0", "browserify": "^13.3.0", "browserify-istanbul": "^2.0.0", "coveralls": "^3.1.1", @@ -68,7 +68,7 @@ "request": "~2.87.0", "rimraf": "^2.7.1", "semver": "^6.3.1", - "tape": "^5.9.0" + "tape": "^5.10.1" }, "license": "MIT", "auto-changelog": {
b62031603c2d[Fix] escape CR, LF, and `"` in field names and filenames
2 files changed · +58 −2
lib/form_data.js+7 −2 modified@@ -15,6 +15,11 @@ var setToStringTag = require('es-set-tostringtag'); var populate = require('./populate.js'); var Buffer = require('safe-buffer').Buffer; +// escape CR/LF/`"` so a name/filename can't inject headers or smuggle parts; matches the WHATWG HTML multipart/form-data encoding +function escapeHeaderParam(str) { + return String(str).replace(/\r/g, '%0D').replace(/\n/g, '%0A').replace(/"/g, '%22'); +} + /** * Create readable "multipart/form-data" streams. * Can be used to submit forms @@ -190,7 +195,7 @@ FormData.prototype._multiPartHeader = function (field, value, options) { var contents = ''; var headers = { // add custom disposition as third element or keep it two elements if not - 'Content-Disposition': ['form-data', 'name="' + field + '"'].concat(contentDisposition || []), + 'Content-Disposition': ['form-data', 'name="' + escapeHeaderParam(field) + '"'].concat(contentDisposition || []), // if no content type. allow it to be empty array 'Content-Type': [].concat(contentType || []), }; @@ -245,7 +250,7 @@ FormData.prototype._getContentDisposition = function (value, options) { } if (filename) { - contentDisposition = 'filename="' + filename + '"'; + contentDisposition = 'filename="' + escapeHeaderParam(filename) + '"'; } return contentDisposition;
test/integration/test-header-injection.js+51 −0 added@@ -0,0 +1,51 @@ +'use strict'; + +var common = require('../common'); +var assert = common.assert; + +var FormData = require(common.dir.lib + '/form_data'); + +(function testFieldNameCRLFInjection() { + var form = new FormData(); + form.append('email"\r\nX-Injected: true\r\nfake="', 'user@example.com'); + + var output = form.getBuffer().toString(); + + assert.equal(output.indexOf('X-Injected: true\r\n'), -1, 'CRLF in a field name must not produce an injected header line'); + assert.notEqual(output.indexOf('%0D%0A'), -1, 'CR/LF in a field name must be escaped'); + assert.notEqual(output.indexOf('%22'), -1, 'a `"` in a field name must be escaped'); +}()); + +(function testFilenameCRLFInjection() { + var form = new FormData(); + form.append('file', Buffer.from('x'), { filename: 'a"\r\nX-Injected: yes\r\nb"' }); + + var output = form.getBuffer().toString(); + + assert.equal(output.indexOf('X-Injected: yes\r\n'), -1, 'CRLF in a filename must not produce an injected header line'); + assert.notEqual(output.indexOf('filename="a%22%0D%0A'), -1, 'CR/LF/`"` in a filename must be escaped'); +}()); + +(function testFieldNameBoundarySmuggling() { + var form = new FormData(); + var boundary = form.getBoundary(); + + form.append('username"\r\n\r\nlegit_user\r\n--' + boundary + '\r\nContent-Disposition: form-data; name="is_admin"\r\n\r\ntrue\r\n--' + boundary + '\r\nContent-Disposition: form-data; name="fake', 'ignored_value'); + + var output = form.getBuffer().toString(); + var headerLines = output.split('\r\n').filter(function (line) { + return line.indexOf('Content-Disposition') === 0; + }); + + assert.equal(output.indexOf('name="is_admin"'), -1, 'a CRLF-laden field name must not smuggle an extra part'); + assert.equal(headerLines.length, 1, 'the whole field name must stay on one header line, smuggling no extra parts'); +}()); + +(function testOrdinaryFieldNamePreserved() { + var form = new FormData(); + form.append('items[0]', 'value'); + + var output = form.getBuffer().toString(); + + assert.notEqual(output.indexOf('name="items[0]"'), -1, 'a field name without control characters must be unchanged'); +}());
8777e67fbd02[Fix] escape CR, LF, and `"` in field names and filenames
2 files changed · +58 −2
lib/form_data.js+7 −2 modified@@ -14,6 +14,11 @@ var setToStringTag = require('es-set-tostringtag'); var hasOwn = require('hasown'); var populate = require('./populate.js'); +// escape CR/LF/`"` so a name/filename can't inject headers or smuggle parts; matches the WHATWG HTML multipart/form-data encoding +function escapeHeaderParam(str) { + return String(str).replace(/\r/g, '%0D').replace(/\n/g, '%0A').replace(/"/g, '%22'); +} + /** * Create readable "multipart/form-data" streams. * Can be used to submit forms @@ -189,7 +194,7 @@ FormData.prototype._multiPartHeader = function (field, value, options) { var contents = ''; var headers = { // add custom disposition as third element or keep it two elements if not - 'Content-Disposition': ['form-data', 'name="' + field + '"'].concat(contentDisposition || []), + 'Content-Disposition': ['form-data', 'name="' + escapeHeaderParam(field) + '"'].concat(contentDisposition || []), // if no content type. allow it to be empty array 'Content-Type': [].concat(contentType || []), }; @@ -244,7 +249,7 @@ FormData.prototype._getContentDisposition = function (value, options) { } if (filename) { - contentDisposition = 'filename="' + filename + '"'; + contentDisposition = 'filename="' + escapeHeaderParam(filename) + '"'; } return contentDisposition;
test/integration/test-header-injection.js+51 −0 added@@ -0,0 +1,51 @@ +'use strict'; + +var common = require('../common'); +var assert = common.assert; + +var FormData = require(common.dir.lib + '/form_data'); + +(function testFieldNameCRLFInjection() { + var form = new FormData(); + form.append('email"\r\nX-Injected: true\r\nfake="', 'user@example.com'); + + var output = form.getBuffer().toString(); + + assert.equal(output.indexOf('X-Injected: true\r\n'), -1, 'CRLF in a field name must not produce an injected header line'); + assert.notEqual(output.indexOf('%0D%0A'), -1, 'CR/LF in a field name must be escaped'); + assert.notEqual(output.indexOf('%22'), -1, 'a `"` in a field name must be escaped'); +}()); + +(function testFilenameCRLFInjection() { + var form = new FormData(); + form.append('file', Buffer.from('x'), { filename: 'a"\r\nX-Injected: yes\r\nb"' }); + + var output = form.getBuffer().toString(); + + assert.equal(output.indexOf('X-Injected: yes\r\n'), -1, 'CRLF in a filename must not produce an injected header line'); + assert.notEqual(output.indexOf('filename="a%22%0D%0A'), -1, 'CR/LF/`"` in a filename must be escaped'); +}()); + +(function testFieldNameBoundarySmuggling() { + var form = new FormData(); + var boundary = form.getBoundary(); + + form.append('username"\r\n\r\nlegit_user\r\n--' + boundary + '\r\nContent-Disposition: form-data; name="is_admin"\r\n\r\ntrue\r\n--' + boundary + '\r\nContent-Disposition: form-data; name="fake', 'ignored_value'); + + var output = form.getBuffer().toString(); + var headerLines = output.split('\r\n').filter(function (line) { + return line.indexOf('Content-Disposition') === 0; + }); + + assert.equal(output.indexOf('name="is_admin"'), -1, 'a CRLF-laden field name must not smuggle an extra part'); + assert.equal(headerLines.length, 1, 'the whole field name must stay on one header line, smuggling no extra parts'); +}()); + +(function testOrdinaryFieldNamePreserved() { + var form = new FormData(); + form.append('items[0]', 'value'); + + var output = form.getBuffer().toString(); + + assert.notEqual(output.indexOf('name="items[0]"'), -1, 'a field name without control characters must be unchanged'); +}());
67b0f65c2e0b[Dev Deps] update `js-randomness-predictor`
1 file changed · +1 −1
package.json+1 −1 modified@@ -60,7 +60,7 @@ "in-publish": "^2.0.1", "is-node-modern": "^1.0.0", "istanbul": "^0.4.5", - "js-randomness-predictor": "^1.5.5", + "js-randomness-predictor": "^3.6.0", "obake": "^0.1.2", "pkgfiles": "^2.3.2", "pre-commit": "^1.2.2",
8dff42c6da65[Fix] escape CR, LF, and `"` in field names and filenames
2 files changed · +65 −2
lib/form_data.js+14 −2 modified@@ -15,6 +15,18 @@ var setToStringTag = require('es-set-tostringtag'); var hasOwn = require('hasown'); var populate = require('./populate.js'); +/** + * Escape CR, LF, and `"` in a multipart `name`/`filename` parameter, so a field + * name or filename can not break out of its header line to inject headers or + * smuggle additional parts. Matches the WHATWG HTML multipart/form-data encoding. + * + * @param {string} str - the parameter value to escape + * @returns {string} the escaped value + */ +function escapeHeaderParam(str) { + return String(str).replace(/\r/g, '%0D').replace(/\n/g, '%0A').replace(/"/g, '%22'); +} + /** * Create readable "multipart/form-data" streams. * Can be used to submit forms @@ -180,7 +192,7 @@ FormData.prototype._multiPartHeader = function (field, value, options) { var contents = ''; var headers = { // add custom disposition as third element or keep it two elements if not - 'Content-Disposition': ['form-data', 'name="' + field + '"'].concat(contentDisposition || []), + 'Content-Disposition': ['form-data', 'name="' + escapeHeaderParam(field) + '"'].concat(contentDisposition || []), // if no content type. allow it to be empty array 'Content-Type': [].concat(contentType || []) }; @@ -234,7 +246,7 @@ FormData.prototype._getContentDisposition = function (value, options) { // eslin } if (filename) { - return 'filename="' + filename + '"'; + return 'filename="' + escapeHeaderParam(filename) + '"'; } };
test/integration/test-header-injection.js+51 −0 added@@ -0,0 +1,51 @@ +'use strict'; + +var common = require('../common'); +var assert = common.assert; + +var FormData = require(common.dir.lib + '/form_data'); + +(function testFieldNameCRLFInjection() { + var form = new FormData(); + form.append('email"\r\nX-Injected: true\r\nfake="', 'user@example.com'); + + var output = form.getBuffer().toString(); + + assert.equal(output.indexOf('X-Injected: true\r\n'), -1, 'CRLF in a field name must not produce an injected header line'); + assert.notEqual(output.indexOf('%0D%0A'), -1, 'CR/LF in a field name must be escaped'); + assert.notEqual(output.indexOf('%22'), -1, 'a `"` in a field name must be escaped'); +}()); + +(function testFilenameCRLFInjection() { + var form = new FormData(); + form.append('file', Buffer.from('x'), { filename: 'a"\r\nX-Injected: yes\r\nb"' }); + + var output = form.getBuffer().toString(); + + assert.equal(output.indexOf('X-Injected: yes\r\n'), -1, 'CRLF in a filename must not produce an injected header line'); + assert.notEqual(output.indexOf('filename="a%22%0D%0A'), -1, 'CR/LF/`"` in a filename must be escaped'); +}()); + +(function testFieldNameBoundarySmuggling() { + var form = new FormData(); + var boundary = form.getBoundary(); + + form.append('username"\r\n\r\nlegit_user\r\n--' + boundary + '\r\nContent-Disposition: form-data; name="is_admin"\r\n\r\ntrue\r\n--' + boundary + '\r\nContent-Disposition: form-data; name="fake', 'ignored_value'); + + var output = form.getBuffer().toString(); + var headerLines = output.split('\r\n').filter(function (line) { + return line.indexOf('Content-Disposition') === 0; + }); + + assert.equal(output.indexOf('name="is_admin"'), -1, 'a CRLF-laden field name must not smuggle an extra part'); + assert.equal(headerLines.length, 1, 'the whole field name must stay on one header line, smuggling no extra parts'); +}()); + +(function testOrdinaryFieldNamePreserved() { + var form = new FormData(); + form.append('items[0]', 'value'); + + var output = form.getBuffer().toString(); + + assert.notEqual(output.indexOf('name="items[0]"'), -1, 'a field name without control characters must be unchanged'); +}());
5822467f0ec2[Dev Deps] update `@ljharb/eslint-config`, `eslint`
5 files changed · +5 −20
.eslintrc+0 −15 modified@@ -5,7 +5,6 @@ "rules": { "indent": [2, 2, {"SwitchCase": 2}], - // "curly": [2, "multi-line"], "handle-callback-err": [2, "^err"], "valid-jsdoc": [2, { "requireReturn": false, @@ -19,16 +18,8 @@ "FunctionDeclaration": true } }], - "no-redeclare": [2, { "builtinGlobals": true }], - "no-shadow": [2, { "builtinGlobals": false, "hoist": "all" }], - "no-use-before-define": [2, "nofunc"], - "no-shadow-restricted-names": 2, - "no-extra-semi": 2, - "no-unused-vars": 2, - "no-undef": 2, "strict": 0, "dot-notation": 0, - "no-new": 0, "eqeqeq": 0, "no-underscore-dangle": 0, @@ -45,10 +36,4 @@ "env": { "node": true }, - - "ignorePatterns": [ - "node_modules/*", - "index.d.ts", - "coverage", - ], }
lib/form_data.js+1 −1 modified@@ -464,7 +464,7 @@ FormData.prototype.submit = function (params, cb) { request.removeListener('error', callback); request.removeListener('response', onResponse); - return cb.call(this, error, responce); // eslint-disable-line no-invalid-this + return cb.call(this, error, responce); }; onResponse = callback.bind(this, null);
package.json+2 −2 modified@@ -47,13 +47,13 @@ "mime-types": "^2.1.12" }, "devDependencies": { - "@ljharb/eslint-config": "^21.2.0", + "@ljharb/eslint-config": "^21.4.0", "auto-changelog": "^2.5.0", "browserify": "^13.3.0", "browserify-istanbul": "^2.0.0", "coveralls": "^3.1.1", "cross-spawn": "^6.0.6", - "eslint": "=8.8.0", + "eslint": "^8.57.1", "fake": "^0.2.2", "far": "^0.0.7", "formidable": "^1.2.6",
test/integration/test-ranged-filestream.js+1 −1 modified@@ -42,7 +42,7 @@ var testSubjects = { * @param {string} data - chunk of read data */ function readSizeAccumulator(data) { - this.readSize += data.length; // eslint-disable-line no-invalid-this + this.readSize += data.length; } var server = http.createServer(function (req, res) {
test/run.js+1 −1 modified@@ -38,7 +38,7 @@ if (process.env.running_under_istanbul) { * @param {string} chunk - partial output */ function onOutput(chunk) { - if (this._verbose > 1) { // eslint-disable-line no-invalid-this + if (this._verbose > 1) { process.stderr.write(chunk); } else { output += chunk;
76d0dee43933[Fix] set Symbol.toStringTag in the proper place
2 files changed · +2 −1
lib/form_data.js+1 −1 modified@@ -488,7 +488,7 @@ FormData.prototype._error = function (err) { FormData.prototype.toString = function () { return '[object FormData]'; }; -setToStringTag(FormData, 'FormData'); +setToStringTag(FormData.prototype, 'FormData'); // Public API module.exports = FormData;
test/integration/test-to-string.js+1 −0 modified@@ -5,3 +5,4 @@ var assert = common.assert; var FormData = require(common.dir.lib + '/form_data'); assert(String(new FormData()) === '[object FormData]'); +assert(Object.prototype.toString.call(new FormData()) === '[object FormData]');
026abe5c5c04[Fix] use proper dependency
1 file changed · +1 −1
package.json+1 −1 modified@@ -42,7 +42,7 @@ "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "has-own": "^1.0.1", + "hasown": "^2.0.2", "mime-types": "^2.1.35", "safe-buffer": "^5.2.1" },
02ff026fda71[Dev Deps] add missing peer dep
1 file changed · +1 −0
package.json+1 −0 modified@@ -50,6 +50,7 @@ "browserify-istanbul": "^2.0.0", "coveralls": "^3.1.1", "cross-spawn": "^4.0.2", + "encoding": "^0.1.13", "eslint": "^6.8.0", "fake": "^0.2.2", "far": "^0.0.7",
8bf2492e0555[eslint] update linting config
33 files changed · +574 −539
.eslintrc+13 −43 modified@@ -1,48 +1,18 @@ { + "root": true, + + "extends": "@ljharb/eslint-config/node/0.4", + "rules": { + "callback-return": "warn", + "camelcase": "off", + "eqeqeq": ["error", "always", {"null": "ignore"}], + "func-style": "off", + "id-length": "off", "indent": [2, 2, {"SwitchCase": 1}], - "quotes": [2, "single"], - "linebreak-style": [2, "unix"], - "semi": [2, "always"], - "curly": [2, "multi-line"], - "handle-callback-err": [2, "^err"], - "valid-jsdoc": [2, { - "requireReturn": false, - "requireReturnDescription": false, - "prefer": { - "return": "returns" - } - }], - "require-jsdoc": [2, { - "require": { - "FunctionDeclaration": true - } - }], - "no-redeclare": [2, { "builtinGlobals": true }], - "no-shadow": [2, { "builtinGlobals": true, "hoist": "all" }], - "no-use-before-define": [2, "nofunc"], - "no-shadow-restricted-names": 2, - "no-extra-semi": 2, - "no-unused-vars": 2, - "no-undef": 2, - "no-irregular-whitespace": 2, - "no-console": 2, - "key-spacing": 0, - "strict": 0, - "dot-notation": 0, - "eol-last": 0, - "no-new": 0, - "semi-spacing": 0, - "no-multi-spaces": 0, - "eqeqeq": 0, - "no-mixed-requires": 0 + "max-lines": "off", + "no-param-reassign": "warn", + "no-underscore-dangle": "off", + "sort-keys": "off", }, - "env": { - "node": true - }, - "ignorePatterns": [ - "node_modules/*", - "index.d.ts", - "coverage", - ] }
lib/browser.js+3 −1 modified@@ -1,2 +1,4 @@ +'use strict'; + /* eslint-env browser */ -module.exports = typeof self == 'object' ? self.FormData : window.FormData; +module.exports = typeof self === 'object' ? self.FormData : window.FormData;
lib/form_data.js+131 −111 modified@@ -1,3 +1,5 @@ +'use strict'; + var CombinedStream = require('combined-stream'); var util = require('util'); var path = require('path'); @@ -10,13 +12,7 @@ var asynckit = require('asynckit'); var hasOwn = require('hasown'); var setToStringTag = require('es-set-tostringtag'); var populate = require('./populate.js'); -var Buffer = require('safe-buffer').Buffer; // eslint-disable-line no-shadow - -// Public API -module.exports = FormData; - -// make it a Stream -util.inherits(FormData, CombinedStream); +var Buffer = require('safe-buffer').Buffer; /** * Create readable "multipart/form-data" streams. @@ -38,34 +34,39 @@ function FormData(options) { CombinedStream.call(this); options = options || {}; - for (var option in options) { + for (var option in options) { // eslint-disable-line no-restricted-syntax this[option] = options[option]; } } +// make it a Stream +util.inherits(FormData, CombinedStream); + FormData.LINE_BREAK = '\r\n'; FormData.DEFAULT_CONTENT_TYPE = 'application/octet-stream'; -FormData.prototype.append = function(field, value, options) { +FormData.prototype.append = function (field, value, options) { options = options || {}; // allow filename as single option - if (typeof options == 'string') { - options = {filename: options}; + if (typeof options === 'string') { + options = { filename: options }; } var append = CombinedStream.prototype.append.bind(this); // all that streamy business can't handle numbers - if (typeof value == 'number') { - value = '' + value; + if (typeof value === 'number') { + value = String(value); } // https://github.com/felixge/node-form-data/issues/38 if (Array.isArray(value)) { - // Please convert your array into string - // the way web server expects it + /* + * Please convert your array into string + * the way web server expects it + */ this._error(new Error('Arrays are not supported.')); return; } @@ -81,15 +82,17 @@ FormData.prototype.append = function(field, value, options) { this._trackLength(header, value, options); }; -FormData.prototype._trackLength = function(header, value, options) { +FormData.prototype._trackLength = function (header, value, options) { var valueLength = 0; - // used w/ getLengthSync(), when length is known. - // e.g. for streaming directly from a remote server, - // w/ a known file a size, and not wanting to wait for - // incoming file to finish to get its size. + /* + * used w/ getLengthSync(), when length is known. + * e.g. for streaming directly from a remote server, + * w/ a known file a size, and not wanting to wait for + * incoming file to finish to get its size. + */ if (options.knownLength != null) { - valueLength += +options.knownLength; + valueLength += Number(options.knownLength); } else if (Buffer.isBuffer(value)) { valueLength = value.length; } else if (typeof value === 'string') { @@ -99,12 +102,10 @@ FormData.prototype._trackLength = function(header, value, options) { this._valueLength += valueLength; // @check why add CRLF? does this account for custom/multiple CRLFs? - this._overheadLength += - Buffer.byteLength(header) + - FormData.LINE_BREAK.length; + this._overheadLength += Buffer.byteLength(header) + FormData.LINE_BREAK.length; // empty or either doesn't have path or not an http response - if (!value || ( !value.path && !(value.readable && hasOwn(value, 'httpVersion')) )) { + if (!value || (!value.path && !(value.readable && hasOwn(value, 'httpVersion')))) { return; } @@ -114,27 +115,31 @@ FormData.prototype._trackLength = function(header, value, options) { } }; -FormData.prototype._lengthRetriever = function(value, callback) { +FormData.prototype._lengthRetriever = function (value, callback) { if (hasOwn(value, 'fd')) { - // take read range into a account - // `end` = Infinity –> read file till the end - // - // TODO: Looks like there is bug in Node fs.createReadStream - // it doesn't respect `end` options without `start` options - // Fix it when node fixes it. - // https://github.com/joyent/node/issues/7819 - if (value.end != undefined && value.end != Infinity && value.start != undefined) { - - // when end specified - // no need to calculate range - // inclusive, starts with 0 + /* + * take read range into a account + * `end` = Infinity –> read file till the end + * + * TODO: Looks like there is bug in Node fs.createReadStream + * it doesn't respect `end` options without `start` options + * Fix it when node fixes it. + * https://github.com/joyent/node/issues/7819 + */ + if (value.end != null && value.end !== Infinity && value.start != null) { + + /* + * when end specified + * no need to calculate range + * inclusive, starts with 0 + */ callback(null, value.end + 1 - (value.start ? value.start : 0)); // not that fast snoopy } else { // still need to fetch file size from fs - fs.stat(value.path, function(err, stat) { + fs.stat(value.path, function (err, stat) { var fileSize; @@ -151,14 +156,14 @@ FormData.prototype._lengthRetriever = function(value, callback) { // or http response } else if (hasOwn(value, 'httpVersion')) { - callback(null, +value.headers['content-length']); + callback(null, Number(value.headers['content-length'])); // or request stream http://github.com/mikeal/request } else if (hasOwn(value, 'httpModule')) { // wait till response come back - value.on('response', function(response) { + value.on('response', function (response) { value.pause(); - callback(null, +response.headers['content-length']); + callback(null, Number(response.headers['content-length'])); }); value.resume(); @@ -168,38 +173,40 @@ FormData.prototype._lengthRetriever = function(value, callback) { } }; -FormData.prototype._multiPartHeader = function(field, value, options) { - // custom header specified (as string)? - // it becomes responsible for boundary - // (e.g. to handle extra CRLFs on .NET servers) - if (typeof options.header == 'string') { +FormData.prototype._multiPartHeader = function (field, value, options) { + /* + * custom header specified (as string)? + * it becomes responsible for boundary + * (e.g. to handle extra CRLFs on .NET servers) + */ + if (typeof options.header === 'string') { return options.header; } var contentDisposition = this._getContentDisposition(value, options); var contentType = this._getContentType(value, options); var contents = ''; - var headers = { + var headers = { // add custom disposition as third element or keep it two elements if not 'Content-Disposition': ['form-data', 'name="' + field + '"'].concat(contentDisposition || []), // if no content type. allow it to be empty array - 'Content-Type': [].concat(contentType || []) + 'Content-Type': [].concat(contentType || []), }; // allow custom headers. - if (typeof options.header == 'object') { + if (typeof options.header === 'object') { populate(headers, options.header); } var header; - for (var prop in headers) { + for (var prop in headers) { // eslint-disable-line no-restricted-syntax if (hasOwn(headers, prop)) { header = headers[prop]; // skip nullish headers. if (header == null) { - continue; + continue; // eslint-disable-line no-continue, no-restricted-syntax } // convert all headers to arrays. @@ -217,19 +224,19 @@ FormData.prototype._multiPartHeader = function(field, value, options) { return '--' + this.getBoundary() + FormData.LINE_BREAK + contents + FormData.LINE_BREAK; }; -FormData.prototype._getContentDisposition = function(value, options) { - - var filename - , contentDisposition - ; +FormData.prototype._getContentDisposition = function (value, options) { + var filename, + contentDisposition; if (typeof options.filepath === 'string') { // custom filepath for relative paths filename = path.normalize(options.filepath).replace(/\\/g, '/'); } else if (options.filename || value.name || value.path) { - // custom filename take precedence - // formidable and the browser add a name property - // fs- and request- streams have path property + /* + * custom filename take precedence + * formidable and the browser add a name property + * fs- and request- streams have path property + */ filename = path.basename(options.filename || value.name || value.path); } else if (value.readable && hasOwn(value, 'httpVersion')) { // or try http response @@ -243,7 +250,7 @@ FormData.prototype._getContentDisposition = function(value, options) { return contentDisposition; }; -FormData.prototype._getContentType = function(value, options) { +FormData.prototype._getContentType = function (value, options) { // use custom content-type above all var contentType = options.contentType; @@ -269,18 +276,18 @@ FormData.prototype._getContentType = function(value, options) { } // fallback to the default content type if `value` is not simple value - if (!contentType && typeof value == 'object') { + if (!contentType && typeof value === 'object') { contentType = FormData.DEFAULT_CONTENT_TYPE; } return contentType; }; -FormData.prototype._multiPartFooter = function() { - return function(next) { +FormData.prototype._multiPartFooter = function () { + return function (next) { var footer = FormData.LINE_BREAK; - var lastPart = (this._streams.length === 0); + var lastPart = this._streams.length === 0; if (lastPart) { footer += this._lastBoundary(); } @@ -289,17 +296,17 @@ FormData.prototype._multiPartFooter = function() { }.bind(this); }; -FormData.prototype._lastBoundary = function() { +FormData.prototype._lastBoundary = function () { return '--' + this.getBoundary() + '--' + FormData.LINE_BREAK; }; -FormData.prototype.getHeaders = function(userHeaders) { +FormData.prototype.getHeaders = function (userHeaders) { var header; var formHeaders = { - 'content-type': 'multipart/form-data; boundary=' + this.getBoundary() + 'content-type': 'multipart/form-data; boundary=' + this.getBoundary(), }; - for (header in userHeaders) { + for (header in userHeaders) { // eslint-disable-line no-restricted-syntax if (hasOwn(userHeaders, header)) { formHeaders[header.toLowerCase()] = userHeaders[header]; } @@ -308,43 +315,45 @@ FormData.prototype.getHeaders = function(userHeaders) { return formHeaders; }; -FormData.prototype.getBoundary = function() { +FormData.prototype.getBoundary = function () { if (!this._boundary) { this._generateBoundary(); } return this._boundary; }; -FormData.prototype.getBuffer = function() { - var dataBuffer = new Buffer.alloc(0); +FormData.prototype.getBuffer = function () { + var dataBuffer = Buffer.alloc(0); var boundary = this.getBoundary(); // Create the form content. Add Line breaks to the end of data. for (var i = 0, len = this._streams.length; i < len; i++) { if (typeof this._streams[i] !== 'function') { // Add content to the buffer. - if(Buffer.isBuffer(this._streams[i])) { - dataBuffer = Buffer.concat( [dataBuffer, this._streams[i]]); - }else { - dataBuffer = Buffer.concat( [dataBuffer, Buffer.from(this._streams[i])]); + if (Buffer.isBuffer(this._streams[i])) { + dataBuffer = Buffer.concat([dataBuffer, this._streams[i]]); + } else { + dataBuffer = Buffer.concat([dataBuffer, Buffer.from(this._streams[i])]); } // Add break after content. - if (typeof this._streams[i] !== 'string' || this._streams[i].substring( 2, boundary.length + 2 ) !== boundary) { - dataBuffer = Buffer.concat( [dataBuffer, Buffer.from(FormData.LINE_BREAK)] ); + if (typeof this._streams[i] !== 'string' || this._streams[i].substring(2, boundary.length + 2) !== boundary) { + dataBuffer = Buffer.concat([dataBuffer, Buffer.from(FormData.LINE_BREAK)]); } } } // Add the footer and return the Buffer object. - return Buffer.concat( [dataBuffer, Buffer.from(this._lastBoundary())] ); + return Buffer.concat([dataBuffer, Buffer.from(this._lastBoundary())]); }; -FormData.prototype._generateBoundary = function() { - // This generates a 50 character boundary similar to those used by Firefox. - // They are optimized for boyer-moore parsing. +FormData.prototype._generateBoundary = function () { + /* + * This generates a 50 character boundary similar to those used by Firefox. + * They are optimized for boyer-moore parsing. + */ var boundary = '--------------------------'; for (var i = 0; i < 24; i++) { boundary += Math.floor(Math.random() * 10).toString(16); @@ -353,33 +362,41 @@ FormData.prototype._generateBoundary = function() { this._boundary = boundary; }; -// Note: getLengthSync DOESN'T calculate streams length -// As workaround one can calculate file size manually -// and add it as knownLength option -FormData.prototype.getLengthSync = function() { +/* + * Note: getLengthSync DOESN'T calculate streams length + * As workaround one can calculate file size manually + * and add it as knownLength option + */ +FormData.prototype.getLengthSync = function () { var knownLength = this._overheadLength + this._valueLength; - // Don't get confused, there are 3 "internal" streams for each keyval pair - // so it basically checks if there is any value added to the form + /* + * Don't get confused, there are 3 "internal" streams for each keyval pair + * so it basically checks if there is any value added to the form + */ if (this._streams.length) { knownLength += this._lastBoundary().length; } // https://github.com/form-data/form-data/issues/40 if (!this.hasKnownLength()) { - // Some async length retrievers are present - // therefore synchronous length calculation is false. - // Please use getLength(callback) to get proper length + /* + * Some async length retrievers are present + * therefore synchronous length calculation is false. + * Please use getLength(callback) to get proper length + */ this._error(new Error('Cannot calculate proper length in synchronous way.')); } return knownLength; }; -// Public API to check if length of added values is known -// https://github.com/form-data/form-data/issues/196 -// https://github.com/form-data/form-data/issues/262 -FormData.prototype.hasKnownLength = function() { +/* + * Public API to check if length of added values is known + * https://github.com/form-data/form-data/issues/196 + * https://github.com/form-data/form-data/issues/262 + */ +FormData.prototype.hasKnownLength = function () { var hasKnownLength = true; if (this._valuesToMeasure.length) { @@ -389,7 +406,7 @@ FormData.prototype.hasKnownLength = function() { return hasKnownLength; }; -FormData.prototype.getLength = function(cb) { +FormData.prototype.getLength = function (cb) { var knownLength = this._overheadLength + this._valueLength; if (this._streams.length) { @@ -401,36 +418,37 @@ FormData.prototype.getLength = function(cb) { return; } - asynckit.parallel(this._valuesToMeasure, this._lengthRetriever, function(err, values) { + asynckit.parallel(this._valuesToMeasure, this._lengthRetriever, function (err, values) { if (err) { cb(err); return; } - values.forEach(function(length) { + values.forEach(function (length) { knownLength += length; }); cb(null, knownLength); }); }; -FormData.prototype.submit = function(params, cb) { - var request - , options - , defaults = {method: 'post'} - ; +FormData.prototype.submit = function (params, cb) { + var request; + var options; + var defaults = { method: 'post' }; - // parse provided url if it's string - // or treat it as options object - if (typeof params == 'string') { + /* + * parse provided url if it's string + * or treat it as options object + */ + if (typeof params === 'string') { params = parseUrl(params); options = populate({ port: params.port, path: params.pathname, host: params.hostname, - protocol: params.protocol + protocol: params.protocol, }, defaults); // use custom params @@ -439,22 +457,22 @@ FormData.prototype.submit = function(params, cb) { options = populate(params, defaults); // if no port provided use default one if (!options.port) { - options.port = options.protocol == 'https:' ? 443 : 80; + options.port = options.protocol === 'https:' ? 443 : 80; } } // put that good code in getHeaders to some use options.headers = this.getHeaders(params.headers); // https if specified, fallback to http in any other case - if (options.protocol == 'https:') { + if (options.protocol === 'https:') { request = https.request(options); } else { request = http.request(options); } // get content length and fire away - this.getLength(function(err, length) { + this.getLength(function (err, length) { if (err) { this._error(err); return; @@ -473,7 +491,7 @@ FormData.prototype.submit = function(params, cb) { return request; }; -FormData.prototype._error = function(err) { +FormData.prototype._error = function (err) { if (!this.error) { this.error = err; this.pause(); @@ -485,3 +503,5 @@ FormData.prototype.toString = function () { return '[object FormData]'; }; setToStringTag(FormData, 'FormData'); + +module.exports = FormData;
lib/populate.js+4 −4 modified@@ -1,8 +1,8 @@ -// populates missing values -module.exports = function(dst, src) { +'use strict'; - Object.keys(src).forEach(function(prop) - { +// populates missing values +module.exports = function (dst, src) { + Object.keys(src).forEach(function (prop) { dst[prop] = dst[prop] || src[prop]; });
package.json+2 −1 modified@@ -46,13 +46,14 @@ "safe-buffer": "^5.2.1" }, "devDependencies": { + "@ljharb/eslint-config": "^21.2.0", "auto-changelog": "^2.5.0", "browserify": "^13.3.0", "browserify-istanbul": "^2.0.0", "coveralls": "^3.1.1", "cross-spawn": "^4.0.2", "encoding": "^0.1.13", - "eslint": "^6.8.0", + "eslint": "=8.8.0", "fake": "^0.2.2", "far": "^0.0.7", "formidable": "^1.2.6",
test/common.js+19 −19 modified@@ -1,3 +1,5 @@ +'use strict'; + var fs = require('fs'); var path = require('path'); var assert = require('assert'); @@ -13,7 +15,7 @@ var rootDir = path.join(__dirname, '..'); common.dir = { lib: path.join(rootDir, '/lib'), fixture: path.join(rootDir, '/test/fixture'), - tmp: path.join(rootDir, '/test/tmp') + tmp: path.join(rootDir, '/test/tmp'), }; common.defaultTypeValue = function () { @@ -38,7 +40,7 @@ common.testFields = function (FIELDS, callback) { return http.createServer(function (req, res) { - var incomingForm = new IncomingForm({uploadDir: common.dir.tmp}); + var incomingForm = new IncomingForm({ uploadDir: common.dir.tmp }); incomingForm.parse(req); @@ -58,11 +60,11 @@ common.actions = {}; // generic form field population common.actions.populateFields = function (form, fields) { var field; - for (var name in fields) { + for (var name in fields) { // eslint-disable-line no-restricted-syntax if (hasOwn(fields, name)) { field = fields[name]; // important to append ReadStreams within the same tick - if ((typeof field.value == 'function')) { + if (typeof field.value === 'function') { field.value = field.value(); } form.append(name, field.value); @@ -71,9 +73,8 @@ common.actions.populateFields = function (form, fields) { }; // generic form submit -common.actions.submit = function(form, server) -{ - return form.submit('http://localhost:' + common.port + '/', function(err, res) { +common.actions.submit = function (form, server) { + return form.submit('http://localhost:' + common.port + '/', function (err, res) { if (err) { throw err; @@ -88,44 +89,43 @@ common.actions.submit = function(form, server) }); }; -common.actions.checkForm = function(form, fields, callback) -{ +common.actions.checkForm = function (form, fields, callback) { var fieldChecked = 0; form - .on('field', function(name, value) { - fieldChecked++; + .on('field', function (name, value) { + fieldChecked += 1; common.actions.formOnField(fields, name, value); }) - .on('file', function(name, file) { - fieldChecked++; + .on('file', function (name, file) { + fieldChecked += 1; common.actions.formOnFile(fields, name, file); }) - .on('end', function() { + .on('end', function () { callback(fieldChecked); }); }; -common.actions.basicFormOnField = function(name, value) { +common.actions.basicFormOnField = function (name, value) { assert.strictEqual(name, 'my_field'); assert.strictEqual(value, 'my_value'); }; -common.actions.formOnField = function(FIELDS, name, value) { +common.actions.formOnField = function (FIELDS, name, value) { assert.ok(name in FIELDS); var field = FIELDS[name]; - assert.strictEqual(value, field.value + ''); + assert.strictEqual(value, String(field.value)); }; -common.actions.formOnFile = function(FIELDS, name, file) { +common.actions.formOnFile = function (FIELDS, name, file) { assert.ok(name in FIELDS); var field = FIELDS[name]; assert.strictEqual(file.name, path.basename(field.value.path || field.name)); assert.strictEqual(file.type, field.type ? field.type : mime.lookup(file.name)); }; // after form has finished parsing -common.actions.formOnEnd = function(res) { +common.actions.formOnEnd = function (res) { res.writeHead(200); res.end('done'); };
test/integration/test-custom-content-type.js+27 −23 modified@@ -1,3 +1,5 @@ +'use strict'; + var common = require('../common'); var assert = common.assert; var http = require('http'); @@ -7,38 +9,40 @@ var hasOwn = require('hasown'); var FormData = require(common.dir.lib + '/form_data'); -// wrap non simple values into function -// just to deal with ReadStream "autostart" +/* + * wrap non simple values into function + * just to deal with ReadStream "autostart" + */ var FIELDS = { - 'no_type': { - value: 'my_value' + no_type: { + value: 'my_value', }, - 'custom_type': { + custom_type: { value: 'my_value', expectedType: 'image/png', options: { - contentType: 'image/png' - } + contentType: 'image/png', + }, }, - 'default_type': { + default_type: { expectedType: FormData.DEFAULT_CONTENT_TYPE, - value: common.defaultTypeValue + value: common.defaultTypeValue, }, - 'implicit_type': { + implicit_type: { expectedType: mime.lookup(common.dir.fixture + '/unicycle.jpg'), - value: function() { return fs.createReadStream(common.dir.fixture + '/unicycle.jpg'); } + value: function () { return fs.createReadStream(common.dir.fixture + '/unicycle.jpg'); }, }, - 'overridden_type': { + overridden_type: { expectedType: 'image/png', options: { - contentType: 'image/png' + contentType: 'image/png', }, - value: function() { return fs.createReadStream(common.dir.fixture + '/unicycle.jpg'); } - } + value: function () { return fs.createReadStream(common.dir.fixture + '/unicycle.jpg'); }, + }, }; var fieldsPassed = false; -var server = http.createServer(function(req, res) { +var server = http.createServer(function (req, res) { var body = ''; var boundry = req.headers['content-type'].split('boundary=').pop(); @@ -53,10 +57,10 @@ var server = http.createServer(function(req, res) { for (var i = 0; i < fieldNames.length; i++) { assert.ok(fields[i].indexOf('name="' + fieldNames[i] + '"') > -1); - if (!FIELDS[fieldNames[i]].expectedType) { - assert.equal(fields[i].indexOf('Content-Type'), -1, 'Expecting ' + fieldNames[i] + ' not to have Content-Type'); - } else { + if (FIELDS[fieldNames[i]].expectedType) { assert.ok(fields[i].indexOf('Content-Type: ' + FIELDS[fieldNames[i]].expectedType) > -1, 'Expecting ' + fieldNames[i] + ' to have Content-Type ' + FIELDS[fieldNames[i]].expectedType); + } else { + assert.equal(fields[i].indexOf('Content-Type'), -1, 'Expecting ' + fieldNames[i] + ' not to have Content-Type'); } } @@ -65,15 +69,15 @@ var server = http.createServer(function(req, res) { }); }); -server.listen(common.port, function() { +server.listen(common.port, function () { var form = new FormData(); - for (var name in FIELDS) { + for (var name in FIELDS) { // eslint-disable-line no-restricted-syntax if (hasOwn(FIELDS, name)) { var field = FIELDS[name]; // important to append ReadStreams within the same tick - if ((typeof field.value == 'function')) { + if (typeof field.value === 'function') { field.value = field.value(); } form.append(name, field.value, field.options); @@ -84,6 +88,6 @@ server.listen(common.port, function() { common.actions.submit(form, server); }); -process.on('exit', function() { +process.on('exit', function () { assert.ok(fieldsPassed); });
test/integration/test-custom-filename.js+33 −32 modified@@ -1,16 +1,18 @@ +'use strict'; + /* -test custom filename and content-type: -re: https://github.com/felixge/node-form-data/issues/29 -*/ - -var common = require('../common'); -var assert = common.assert; -var mime = require('mime-types'); -var http = require('http'); -var fs = require('fs'); -var path = require('path'); - -var FormData = require(common.dir.lib + '/form_data'); + * test custom filename and content-type: + * re: https://github.com/felixge/node-form-data/issues/29 + */ + +var common = require('../common'); +var assert = common.assert; +var mime = require('mime-types'); +var http = require('http'); +var fs = require('fs'); +var path = require('path'); + +var FormData = require(common.dir.lib + '/form_data'); var IncomingForm = require('formidable').IncomingForm; var knownFile = path.join(common.dir.fixture, 'unicycle.jpg'); @@ -19,50 +21,49 @@ var relativeFile = path.relative(path.join(knownFile, '..', '..'), knownFile); var options = { filename: 'test.png', - contentType: 'image/gif' + contentType: 'image/gif', }; -var server = http.createServer(function(req, res) { +var server = http.createServer(function (req, res) { - var form = new IncomingForm({uploadDir: common.dir.tmp}); + var form = new IncomingForm({ uploadDir: common.dir.tmp }); form.parse(req, function (err, fields, files) { assert(!err); assert('custom_everything' in files); - assert.strictEqual(files['custom_everything'].name, options.filename, 'Expects custom filename'); - assert.strictEqual(files['custom_everything'].type, options.contentType, 'Expects custom content-type'); + assert.strictEqual(files.custom_everything.name, options.filename, 'Expects custom filename'); + assert.strictEqual(files.custom_everything.type, options.contentType, 'Expects custom content-type'); assert('custom_filename' in files); - assert.strictEqual(files['custom_filename'].name, options.filename, 'Expects custom filename'); - assert.strictEqual(files['custom_filename'].type, mime.lookup(knownFile), 'Expects original content-type'); + assert.strictEqual(files.custom_filename.name, options.filename, 'Expects custom filename'); + assert.strictEqual(files.custom_filename.type, mime.lookup(knownFile), 'Expects original content-type'); assert('custom_filepath' in files); - assert.strictEqual(files['custom_filepath'].name, relativeFile.replace(/\\/g, '/'), 'Expects custom filepath'); - assert.strictEqual(files['custom_filepath'].type, mime.lookup(knownFile), 'Expects original content-type'); + assert.strictEqual(files.custom_filepath.name, relativeFile.replace(/\\/g, '/'), 'Expects custom filepath'); + assert.strictEqual(files.custom_filepath.type, mime.lookup(knownFile), 'Expects original content-type'); assert('unknown_with_filename' in files); - assert.strictEqual(files['unknown_with_filename'].name, options.filename, 'Expects custom filename'); - assert.strictEqual(files['unknown_with_filename'].type, mime.lookup(options.filename), 'Expects filename-derived content-type'); + assert.strictEqual(files.unknown_with_filename.name, options.filename, 'Expects custom filename'); + assert.strictEqual(files.unknown_with_filename.type, mime.lookup(options.filename), 'Expects filename-derived content-type'); assert('unknown_with_filename_as_object' in files); - assert.strictEqual(files['unknown_with_filename_as_object'].name, options.filename, 'Expects custom filename'); - assert.strictEqual(files['unknown_with_filename_as_object'].type, mime.lookup(options.filename), 'Expects filename-derived content-type'); + assert.strictEqual(files.unknown_with_filename_as_object.name, options.filename, 'Expects custom filename'); + assert.strictEqual(files.unknown_with_filename_as_object.type, mime.lookup(options.filename), 'Expects filename-derived content-type'); assert('unknown_with_name_prop' in files); - assert.strictEqual(files['unknown_with_name_prop'].name, options.filename, 'Expects custom filename'); - assert.strictEqual(files['unknown_with_name_prop'].type, mime.lookup(options.filename), 'Expects filename-derived content-type'); + assert.strictEqual(files.unknown_with_name_prop.name, options.filename, 'Expects custom filename'); + assert.strictEqual(files.unknown_with_name_prop.type, mime.lookup(options.filename), 'Expects filename-derived content-type'); assert('unknown_everything' in files); - assert.strictEqual(files['unknown_everything'].type, FormData.DEFAULT_CONTENT_TYPE, 'Expects default content-type'); + assert.strictEqual(files.unknown_everything.type, FormData.DEFAULT_CONTENT_TYPE, 'Expects default content-type'); res.writeHead(200); res.end('done'); }); }); - -server.listen(common.port, function() { +server.listen(common.port, function () { var form = new FormData(); // Explicit contentType and filename. @@ -72,9 +73,9 @@ server.listen(common.port, function() { // Filename only with unknown file form.append('unknown_with_filename', fs.createReadStream(unknownFile), options.filename); // Filename only with unknown file - form.append('unknown_with_filename_as_object', fs.createReadStream(unknownFile), {filename: options.filename}); + form.append('unknown_with_filename_as_object', fs.createReadStream(unknownFile), { filename: options.filename }); // Filename with relative path - form.append('custom_filepath', fs.createReadStream(knownFile), {filepath: relativeFile}); + form.append('custom_filepath', fs.createReadStream(knownFile), { filepath: relativeFile }); // No options or implicit file type from extension on name property. var customNameStream = fs.createReadStream(unknownFile); customNameStream.name = options.filename;
test/integration/test-custom-headers-object.js+13 −11 modified@@ -1,7 +1,9 @@ +'use strict'; + /* -test custom headers object. -https://github.com/form-data/form-data/issues/133 -*/ + * test custom headers object. + * https://github.com/form-data/form-data/issues/133 + */ var common = require('../common'); var assert = common.assert; @@ -13,9 +15,8 @@ var testHeader = { 'X-Test-Fake': 123 }; var expectedLength; - -var server = http.createServer(function(req, res) { - assert.ok( typeof req.headers['content-length'] !== 'undefined' ); +var server = http.createServer(function (req, res) { + assert.ok(typeof req.headers['content-length'] !== 'undefined'); assert.equal(req.headers['content-length'], expectedLength); req.on('data', function (data) { @@ -29,16 +30,17 @@ var server = http.createServer(function(req, res) { res.end('done'); }); - -server.listen(common.port, function() { +server.listen(common.port, function () { var form = new FormData(); var options = { header: testHeader, - // override content-length, - // much lower than actual buffer size (1000) - knownLength: 1 + /* + * override content-length, + * much lower than actual buffer size (1000) + */ + knownLength: 1, }; var bufferData = [];
test/integration/test-custom-headers-string.js+14 −14 modified@@ -1,7 +1,9 @@ +'use strict'; + /* -test custom headers, added in pull request: -https://github.com/felixge/node-form-data/pull/17 -*/ + * test custom headers, added in pull request: + * https://github.com/felixge/node-form-data/pull/17 + */ var common = require('../common'); var assert = common.assert; @@ -15,9 +17,8 @@ var testHeader = 'X-Test-Fake: 123'; var expectedLength; - -var server = http.createServer(function(req, res) { - assert.ok( typeof req.headers['content-length'] !== 'undefined' ); +var server = http.createServer(function (req, res) { + assert.ok(typeof req.headers['content-length'] !== 'undefined'); assert.equal(req.headers['content-length'], expectedLength); req.on('data', function (data) { @@ -31,19 +32,18 @@ var server = http.createServer(function(req, res) { res.end('done'); }); - -server.listen(common.port, function() { +server.listen(common.port, function () { var form = new FormData(); var options = { header: - CRLF + '--' + form.getBoundary() + CRLF + - testHeader + - CRLF + CRLF, + CRLF + '--' + form.getBoundary() + CRLF + testHeader + CRLF + CRLF, - // override content-length, - // much lower than actual buffer size (1000) - knownLength: 1 + /* + * override content-length, + * much lower than actual buffer size (1000) + */ + knownLength: 1, }; var bufferData = [];
test/integration/test-errors.js+14 −14 modified@@ -1,3 +1,5 @@ +'use strict'; + var common = require('../common'); var assert = common.assert; var FormData = require(common.dir.lib + '/form_data'); @@ -14,31 +16,30 @@ var http = require('http'); var callback = fake.callback('testAppendArray-onError-append'); fake.expectAnytime(callback, ['Arrays are not supported.']); - form.on('error', function(err) { + form.on('error', function (err) { // workaround for expectAnytime handling objects callback(err.message); }); form.append('my_array', ['bird', 'cute']); -})(); - +}()); (function testGetLengthSync() { var fields = [ { name: 'my_string', - value: 'Test 123' + value: 'Test 123', }, { name: 'my_image', - value: fs.createReadStream(common.dir.fixture + '/unicycle.jpg') - } + value: fs.createReadStream(common.dir.fixture + '/unicycle.jpg'), + }, ]; var form = new FormData(); var expectedLength = 0; - fields.forEach(function(field) { + fields.forEach(function (field) { form.append(field.name, field.value); if (field.value.path) { var stat = fs.statSync(field.value.path); @@ -49,11 +50,10 @@ var http = require('http'); }); expectedLength += form._overheadLength + form._lastBoundary().length; - var callback = fake.callback('testGetLengthSync-onError-getLengthSync'); fake.expectAnytime(callback, ['Cannot calculate proper length in synchronous way.']); - form.on('error', function(err) { + form.on('error', function (err) { // workaroud for expectAnytime handling objects callback(err.message); }); @@ -62,7 +62,7 @@ var http = require('http'); // getLengthSync DOESN'T calculate streams length assert.ok(expectedLength > calculatedLength); -})(); +}()); (function testStreamError() { var req; @@ -75,14 +75,14 @@ var http = require('http'); form.append('fake-stream', src); - form.on('error', function(err) { + form.on('error', function (err) { assert.equal(err.code, 'ENOENT'); assert.equal(err.path, fakePath); - req.on('error', function() {}); + req.on('error', function () {}); server.close(); }); - server.listen(common.port, function() { + server.listen(common.port, function () { req = form.submit(addr); }); -})(); +}());
test/integration/test-form-get-length.js+14 −19 modified@@ -1,3 +1,5 @@ +'use strict'; + var common = require('../common'); var assert = common.assert; var FormData = require(common.dir.lib + '/form_data'); @@ -13,7 +15,7 @@ var fs = require('fs'); // Make sure our response is async assert.strictEqual(calls.length, 0); -})(); +}()); (function testUtf8String() { var FIELD = 'my_field'; @@ -23,14 +25,11 @@ var fs = require('fs'); form.append(FIELD, VALUE); var callback = fake.callback('testUtf8String-getLength'); - var expectedLength = - form._overheadLength + - Buffer.byteLength(VALUE) + - form._lastBoundary().length; + var expectedLength = form._overheadLength + Buffer.byteLength(VALUE) + form._lastBoundary().length; fake.expectAnytime(callback, [null, expectedLength]); form.getLength(callback); -})(); +}()); (function testBuffer() { var FIELD = 'my_field'; @@ -40,40 +39,36 @@ var fs = require('fs'); form.append(FIELD, VALUE); var callback = fake.callback('testBuffer-getLength'); - var expectedLength = - form._overheadLength + - VALUE.length + - form._lastBoundary().length; + var expectedLength = form._overheadLength + VALUE.length + form._lastBoundary().length; fake.expectAnytime(callback, [null, expectedLength]); form.getLength(callback); -})(); - +}()); (function testStringFileBufferFile() { var fields = [ { name: 'my_field', - value: 'Test 123' + value: 'Test 123', }, { name: 'my_image', - value: fs.createReadStream(common.dir.fixture + '/unicycle.jpg') + value: fs.createReadStream(common.dir.fixture + '/unicycle.jpg'), }, { name: 'my_buffer', - value: new Buffer('123') + value: new Buffer('123'), }, { name: 'my_txt', - value: fs.createReadStream(common.dir.fixture + '/veggies.txt') - } + value: fs.createReadStream(common.dir.fixture + '/veggies.txt'), + }, ]; var form = new FormData(); var expectedLength = 0; - fields.forEach(function(field) { + fields.forEach(function (field) { form.append(field.name, field.value); if (field.value.path) { var stat = fs.statSync(field.value.path); @@ -88,4 +83,4 @@ var fs = require('fs'); var callback = fake.callback('testStringFileBufferFile-getLength'); fake.expectAnytime(callback, [null, expectedLength]); form.getLength(callback); -})(); +}());
test/integration/test-form-get-length-sync.js+17 −16 modified@@ -1,3 +1,5 @@ +'use strict'; + var common = require('../common'); var assert = common.assert; var FormData = require(common.dir.lib + '/form_data'); @@ -7,69 +9,68 @@ var fs = require('fs'); var fields = [ { name: 'my_number', - value: 123 + value: 123, }, { name: 'my_string', - value: 'Test 123' + value: 'Test 123', }, { name: 'my_buffer', - value: new Buffer('123') - } + value: new Buffer('123'), + }, ]; var form = new FormData(); var expectedLength = 0; - fields.forEach(function(field) { + fields.forEach(function (field) { form.append(field.name, field.value); - expectedLength += ('' + field.value).length; + expectedLength += String(field.value).length; }); expectedLength += form._overheadLength + form._lastBoundary().length; var calculatedLength = form.getLengthSync(); assert.equal(expectedLength, calculatedLength); -})(); - +}()); (function testGetLengthSyncWithKnownLength() { var fields = [ { name: 'my_number', - value: 123 + value: 123, }, { name: 'my_string', - value: 'Test 123' + value: 'Test 123', }, { name: 'my_buffer', - value: new Buffer('123') + value: new Buffer('123'), }, { name: 'my_image', value: fs.createReadStream(common.dir.fixture + '/unicycle.jpg'), - options: { knownLength: fs.statSync(common.dir.fixture + '/unicycle.jpg').size } - } + options: { knownLength: fs.statSync(common.dir.fixture + '/unicycle.jpg').size }, + }, ]; var form = new FormData(); var expectedLength = 0; - fields.forEach(function(field) { + fields.forEach(function (field) { form.append(field.name, field.value, field.options); if (field.value.path) { var stat = fs.statSync(field.value.path); expectedLength += stat.size; } else { - expectedLength += ('' + field.value).length; + expectedLength += String(field.value).length; } }); expectedLength += form._overheadLength + form._lastBoundary().length; var calculatedLength = form.getLengthSync(); assert.equal(expectedLength, calculatedLength); -})(); +}());
test/integration/test-get-boundary.js+4 −2 modified@@ -1,3 +1,5 @@ +'use strict'; + var common = require('../common'); var assert = common.assert; @@ -9,10 +11,10 @@ var FormData = require(common.dir.lib + '/form_data'); assert.equal(boundary, form.getBoundary()); assert.equal(boundary.length, 50); -})(); +}()); (function testUniqueBoundaryPerForm() { var formA = new FormData(); var formB = new FormData(); assert.notEqual(formA.getBoundary(), formB.getBoundary()); -})(); +}());
test/integration/test-get-buffer.js+11 −22 modified@@ -9,11 +9,11 @@ var FormData = require(common.dir.lib + '/form_data'); (function testTypeIsBuffer() { var form = new FormData(); - form.append( 'String', 'Some random string' ); + form.append('String', 'Some random string'); var buffer = form.getBuffer(); assert.equal(typeof buffer === 'object' && Buffer.isBuffer(buffer), true); -})(); +}()); (function testBufferIsValid() { var form = new FormData(); @@ -23,36 +23,25 @@ var FormData = require(common.dir.lib + '/form_data'); var intName = 'Int'; var intValue = 1549873167987; var bufferName = 'Buffer'; - var bufferValue = Buffer.from([0x00,0x4a,0x45,0x46,0x46,0x52,0x45,0x59,0x255]); + var bufferValue = Buffer.from([0x00, 0x4a, 0x45, 0x46, 0x46, 0x52, 0x45, 0x59, 0x255]); // Fill the formData object - form.append( stringName, stringValue ); - form.append( intName, intValue ); - form.append( bufferName, bufferValue ); + form.append(stringName, stringValue); + form.append(intName, intValue); + form.append(bufferName, bufferValue); // Get the resulting Buffer var buffer = form.getBuffer(); // Generate expected code. var boundary = form.getBoundary(); - var expected = Buffer.concat( [ - Buffer.from( '--' + boundary + FormData.LINE_BREAK + - 'Content-Disposition: form-data; name="' + stringName + '"' + FormData.LINE_BREAK + - FormData.LINE_BREAK + - stringValue + FormData.LINE_BREAK + - '--' + boundary + FormData.LINE_BREAK + - 'Content-Disposition: form-data; name="' + intName + '"' + FormData.LINE_BREAK + - FormData.LINE_BREAK + - intValue + FormData.LINE_BREAK + - '--' + boundary + FormData.LINE_BREAK + - 'Content-Disposition: form-data; name="' + bufferName + '"' + FormData.LINE_BREAK + - 'Content-Type: application/octet-stream' + FormData.LINE_BREAK + - FormData.LINE_BREAK), + var expected = Buffer.concat([ + Buffer.from('--' + boundary + FormData.LINE_BREAK + 'Content-Disposition: form-data; name="' + stringName + '"' + FormData.LINE_BREAK + FormData.LINE_BREAK + stringValue + FormData.LINE_BREAK + '--' + boundary + FormData.LINE_BREAK + 'Content-Disposition: form-data; name="' + intName + '"' + FormData.LINE_BREAK + FormData.LINE_BREAK + intValue + FormData.LINE_BREAK + '--' + boundary + FormData.LINE_BREAK + 'Content-Disposition: form-data; name="' + bufferName + '"' + FormData.LINE_BREAK + 'Content-Type: application/octet-stream' + FormData.LINE_BREAK + FormData.LINE_BREAK), bufferValue, - Buffer.from( FormData.LINE_BREAK + '--' + boundary + '--' + FormData.LINE_BREAK ) - ] ); + Buffer.from(FormData.LINE_BREAK + '--' + boundary + '--' + FormData.LINE_BREAK), + ]); // Test if the buffer content, equals the expected buffer. assert.equal(buffer.length, expected.length); assert.equal(buffer.toString('hex'), expected.toString('hex')); -})(); +}());
test/integration/test-http-response.js+15 −14 modified@@ -1,3 +1,5 @@ +'use strict'; + var common = require('../common'); var assert = common.assert; var http = require('http'); @@ -14,34 +16,34 @@ var options = { method: 'get', port: parsedUrl.port || 80, path: parsedUrl.pathname, - host: parsedUrl.hostname + host: parsedUrl.hostname, }; var FIELDS = { - 'my_field': { - value: 'my_value' + my_field: { + value: 'my_value', }, - 'my_buffer': { + my_buffer: { type: FormData.DEFAULT_CONTENT_TYPE, - value: common.defaultTypeValue + value: common.defaultTypeValue, }, - 'remote_file': { + remote_file: { value: 'TBD', - name: remoteFile - } + name: remoteFile, + }, }; // request static file -http.request(options, function(response) { +http.request(options, function (response) { var form = new FormData(); // add http response to the form fields - FIELDS['remote_file'].value = response; + FIELDS.remote_file.value = response; common.actions.populateFields(form, FIELDS); - server.listen(common.port, function() { + server.listen(common.port, function () { common.actions.submit(form, server); }); @@ -51,11 +53,10 @@ http.request(options, function(response) { var fieldsPassed = Object.keys(FIELDS).length; // prepare form-receiving http server -server = common.testFields(FIELDS, function(fields){ +server = common.testFields(FIELDS, function (fields) { fieldsPassed = fields; }); - -process.on('exit', function() { +process.on('exit', function () { assert.strictEqual(fieldsPassed, 0); });
test/integration/test-last_boundary-line_break.js+5 −3 modified@@ -1,3 +1,5 @@ +'use strict'; + var http = require('http'); var common = require('../common'); var assert = common.assert; @@ -17,18 +19,18 @@ function submitForm() { } // create https server -server = http.createServer(function(req, res) { +server = http.createServer(function (req, res) { var body = ''; req.setEncoding('utf8'); // old and simple - req.on('data', function(data) { + req.on('data', function (data) { body += data; }); - req.on('end', function() { + req.on('end', function () { // last character(s) sequence equals predefined line break assert.strictEqual(body.substr(-1 * FormData.LINE_BREAK.length), FormData.LINE_BREAK);
test/integration/test-options-override.js+3 −1 modified@@ -1,6 +1,8 @@ +'use strict'; + var common = require('../common'); var assert = common.assert; var FormData = require(common.dir.lib + '/form_data'); -var form = new FormData({maxDataSize: 20 * 1024 * 1024}); +var form = new FormData({ maxDataSize: 20 * 1024 * 1024 }); assert.strictEqual(form.maxDataSize, 20 * 1024 * 1024);
test/integration/test-pipe.js+24 −21 modified@@ -1,3 +1,5 @@ +'use strict'; + var common = require('../common'); var assert = common.assert; var http = require('http'); @@ -10,51 +12,52 @@ var IncomingForm = require('formidable').IncomingForm; var remoteFile = 'http://localhost:' + common.staticPort + '/unicycle.jpg'; -// wrap non simple values into function -// just to deal with ReadStream "autostart" +/* + * wrap non simple values into function + * just to deal with ReadStream "autostart" + */ var FIELDS = { - 'my_field': { - value: 'my_value' + my_field: { + value: 'my_value', }, - 'my_buffer': { + my_buffer: { type: FormData.DEFAULT_CONTENT_TYPE, - value: common.defaultTypeValue + value: common.defaultTypeValue, }, - 'my_file': { + my_file: { type: mime.lookup(common.dir.fixture + '/unicycle.jpg'), - value: function() { return fs.createReadStream(common.dir.fixture + '/unicycle.jpg'); } + value: function () { return fs.createReadStream(common.dir.fixture + '/unicycle.jpg'); }, }, - 'remote_file': { + remote_file: { type: mime.lookup(common.dir.fixture + '/unicycle.jpg'), - value: function() { return request(remoteFile); } - } + value: function () { return request(remoteFile); }, + }, }; var fieldsPassed = Object.keys(FIELDS).length; -var server = http.createServer(function(req, res) { +var server = http.createServer(function (req, res) { - var form = new IncomingForm({uploadDir: common.dir.tmp}); + var form = new IncomingForm({ uploadDir: common.dir.tmp }); form.parse(req); - common.actions.checkForm(form, FIELDS, function(fieldsChecked) - { + common.actions.checkForm(form, FIELDS, function (fieldsChecked) { // keep track of number of the processed fields fieldsPassed = fieldsPassed - fieldsChecked; // finish it common.actions.formOnEnd(res); }); }); -server.listen(common.port, function() { +server.listen(common.port, function () { var form = new FormData(); - for (var name in FIELDS) { + for (var name in FIELDS) { // eslint-disable-line no-restricted-syntax if (hasOwn(FIELDS, name)) { var field = FIELDS[name]; // important to append ReadStreams within the same tick - if ((typeof field.value == 'function')) { + if (typeof field.value === 'function') { field.value = field.value(); } form.append(name, field.value); @@ -65,12 +68,12 @@ server.listen(common.port, function() { method: 'post', port: common.port, path: '/upload', - headers: form.getHeaders() + headers: form.getHeaders(), }); form.pipe(req); - req.on('response', function(res) { + req.on('response', function (res) { // unstuck new streams res.resume(); @@ -79,6 +82,6 @@ server.listen(common.port, function() { }); }); -process.on('exit', function() { +process.on('exit', function () { assert.strictEqual(fieldsPassed, 0); });
test/integration/test-ranged-filestream.js+35 −34 modified@@ -1,88 +1,89 @@ +'use strict'; + /* -test ranged fs.createReadStream -re: https://github.com/felixge/node-form-data/issues/71 -*/ + * test ranged fs.createReadStream + * re: https://github.com/felixge/node-form-data/issues/71 + */ -var common = require('../common'); -var assert = common.assert; -var http = require('http'); -var fs = require('fs'); -var hasOwn = require('hasown'); +var common = require('../common'); +var assert = common.assert; +var http = require('http'); +var fs = require('fs'); +var hasOwn = require('hasown'); -var FormData = require(common.dir.lib + '/form_data'); +var FormData = require(common.dir.lib + '/form_data'); var IncomingForm = require('formidable').IncomingForm; var testSubjects = { - 'a_file': { + a_file: { file: 'veggies.txt', start: 8, - end: 18 - }, 'b_file': { + end: 18, + }, b_file: { file: 'veggies.txt', - start: 6 - }, 'c_file': { + start: 6, + }, c_file: { file: 'veggies.txt', - end: 16 - }, 'd_file': { + end: 16, + }, d_file: { file: 'veggies.txt', start: 0, - end: 16 - }, 'e_file': { + end: 16, + }, e_file: { file: 'veggies.txt', start: 0, - end: 0 - } + end: 0, + }, }; /** * Accumulates read data size * - * @param {string} data - chunk of read data + * @param {string} data - chunk of read data */ function readSizeAccumulator(data) { - this.readSize += data.length; + this.readSize += data.length; // eslint-disable-line no-invalid-this } -var server = http.createServer(function(req, res) { +var server = http.createServer(function (req, res) { var requestBodyLength = 0; // calculate actual length of the request body - req.on('data', function(data) { + req.on('data', function (data) { requestBodyLength += data.length; }); - req.on('end', function() { + req.on('end', function () { // make sure total Content-Length is properly calculated assert.equal(req.headers['content-length'], requestBodyLength); // successfully accepted request and it's good res.writeHead(200); }); - var form = new IncomingForm({uploadDir: common.dir.tmp}); + var form = new IncomingForm({ uploadDir: common.dir.tmp }); form.parse(req); form - .on('file', function(name, file) { + .on('file', function (name, file) { // make sure chunks are the same size assert.equal(file.size, testSubjects[name].readSize); // clean up tested subject delete testSubjects[name]; }) - .on('end', function() { + .on('end', function () { // done here res.end(); }); }); - -server.listen(common.port, function() { +server.listen(common.port, function () { var form = new FormData(); var name, options; // add test subjects to the form - for (name in testSubjects) { + for (name in testSubjects) { // eslint-disable-line no-restricted-syntax if (hasOwn(testSubjects, name)) { - options = {encoding: 'utf8'}; + options = { encoding: 'utf8' }; if (testSubjects[name].start) { options.start = testSubjects[name].start; } if (testSubjects[name].end) { options.end = testSubjects[name].end; } @@ -95,15 +96,15 @@ server.listen(common.port, function() { } } - form.submit('http://localhost:' + common.port + '/', function(err, res) { + form.submit('http://localhost:' + common.port + '/', function (err, res) { if (err) { throw err; } assert.strictEqual(res.statusCode, 200); // wait for server to finish - res.on('end', function() { + res.on('end', function () { // check that all subjects were tested assert.strictEqual(Object.keys(testSubjects).length, 0); server.close();
test/integration/test-request.js+16 −14 modified@@ -1,3 +1,5 @@ +'use strict'; + /** * Show & Test for `mikeal/request` library * as bonus shows progress monitor implementation @@ -13,52 +15,52 @@ var fs = require('fs'); var IncomingForm = require('formidable').IncomingForm; var fileName = common.dir.fixture + '/unicycle.jpg'; -var myFile = function() { return fs.createReadStream(fileName); }; +var myFile = function () { return fs.createReadStream(fileName); }; var numItems = 5; // Make request to use our FormData request.prototype.form = function (form) { var self = this; if (form) { - if (!/^application\/x-www-form-urlencoded\b/.test(self.getHeader('content-type'))) { + if (!(/^application\/x-www-form-urlencoded\b/).test(self.getHeader('content-type'))) { self.setHeader('content-type', 'application/x-www-form-urlencoded'); } - self.body = (typeof form === 'string') + self.body = typeof form === 'string' ? self._qs.rfc3986(form.toString('utf8')) : self._qs.stringify(form).toString('utf8'); return self; } // create form-data object self._form = new FormData(); - self._form.on('error', function(err) { + self._form.on('error', function (err) { err.message = 'form-data: ' + err.message; self.emit('error', err); self.abort(); }); return self._form; }; -var server = http.createServer(function(req, res) { +var server = http.createServer(function (req, res) { - var form = new IncomingForm({uploadDir: common.dir.tmp}); + var form = new IncomingForm({ uploadDir: common.dir.tmp }); form.parse(req); form - .on('file', function(name, file) { - numItems--; + .on('file', function (name, file) { + numItems -= 1; assert.strictEqual(file.name, path.basename(fileName)); assert.strictEqual(file.type, mime.lookup(file.name)); }) .on('end', common.actions.formOnEnd.bind(null, res)); }); -server.listen(common.port, function() { +server.listen(common.port, function () { var uploadSize = 0; var uploaded = 0; - var r = request.post('http://localhost:' + common.port + '/', function(err, res) { + var r = request.post('http://localhost:' + common.port + '/', function (err, res) { assert.ifError(err); assert.strictEqual(res.statusCode, 200); server.close(); @@ -71,23 +73,23 @@ server.listen(common.port, function() { } // get upload size - form.getLength(function(err, size) { + form.getLength(function (err, size) { assert.equal(err, null); uploadSize = size; assert.ok(uploadSize > 0); }); // calculate uploaded size chunk by chunk - form.on('data', function(data) { + form.on('data', function (data) { uploaded += data.length; }); // done uploading compare sizes - form.on('end', function() { + form.on('end', function () { assert.equal(uploaded, uploadSize); }); }); -process.on('exit', function() { +process.on('exit', function () { assert.strictEqual(numItems, 0); });
test/integration/test-return-http-request.js+19 −16 modified@@ -1,9 +1,11 @@ -/* -test return http request, added for issue #47: -https://github.com/felixge/node-form-data/issues/47 +'use strict'; -Checking correct length header and request object -*/ +/* + * test return http request, added for issue #47: + * https://github.com/felixge/node-form-data/issues/47 + * + * Checking correct length header and request object + */ var common = require('../common'); var assert = common.assert; @@ -15,17 +17,17 @@ var expectedLength; var dataSize = 1000000; -var server = http.createServer(function(req, res) { +var server = http.createServer(function (req, res) { var uploaded = 0; - assert.ok( typeof req.headers['content-length'] !== 'undefined' ); + assert.ok(typeof req.headers['content-length'] !== 'undefined'); assert.equal(req.headers['content-length'], expectedLength); // check for uploaded body - req.on('data', function(data) { + req.on('data', function (data) { uploaded += data.length; }); - req.on('end', function() { + req.on('end', function () { // compare uploaded total to the expected length assert.equal(uploaded, expectedLength); @@ -35,9 +37,10 @@ var server = http.createServer(function(req, res) { }); - -server.listen(common.port, function() { - var R, oWrite, progress = 0, form = new FormData(); +server.listen(common.port, function () { + var R, oWrite, + progress = 0, + form = new FormData(); var bufferData = []; for (var z = 0; z < dataSize; z++) { @@ -50,7 +53,7 @@ server.listen(common.port, function() { // (available to req handler) expectedLength = form._lastBoundary().length + form._overheadLength + dataSize; - R = form.submit('http://localhost:' + common.port + '/', function(err, res) { + R = form.submit('http://localhost:' + common.port + '/', function (err, res) { if (err) { throw err; } @@ -68,14 +71,14 @@ server.listen(common.port, function() { // augment into request oWrite = R.write; - R.write = function(chunk) { - return oWrite.call(this, chunk, function() { + R.write = function (chunk) { + return oWrite.call(this, chunk, function () { form.emit('progress', chunk); }); }; // track progress - form.on('progress', function(chunk) { + form.on('progress', function (chunk) { progress += chunk.length; assert.ok(progress <= expectedLength); });
test/integration/test-submit-custom-header.js+23 −20 modified@@ -1,3 +1,5 @@ +'use strict'; + var common = require('../common'); var assert = common.assert; var http = require('http'); @@ -9,44 +11,45 @@ var IncomingForm = require('formidable').IncomingForm; var remoteFile = 'http://localhost:' + common.staticPort + '/unicycle.jpg'; -// wrap non simple values into function -// just to deal with ReadStream "autostart" +/* + * wrap non simple values into function + * just to deal with ReadStream "autostart" + */ var FIELDS = { - 'my_field': { - value: 'my_value' + my_field: { + value: 'my_value', }, - 'my_buffer': { + my_buffer: { type: FormData.DEFAULT_CONTENT_TYPE, - value: common.defaultTypeValue + value: common.defaultTypeValue, }, - 'my_file': { + my_file: { type: mime.lookup(common.dir.fixture + '/unicycle.jpg'), - value: function() { return fs.createReadStream(common.dir.fixture + '/unicycle.jpg'); } + value: function () { return fs.createReadStream(common.dir.fixture + '/unicycle.jpg'); }, }, - 'remote_file': { + remote_file: { type: mime.lookup(common.dir.fixture + '/unicycle.jpg'), - value: function() { return request(remoteFile); } - } + value: function () { return request(remoteFile); }, + }, }; var fieldsPassed = Object.keys(FIELDS).length; -var server = http.createServer(function(req, res) { +var server = http.createServer(function (req, res) { assert.ok(req.headers['x-test-header'], 'test-header-value'); - var form = new IncomingForm({uploadDir: common.dir.tmp}); + var form = new IncomingForm({ uploadDir: common.dir.tmp }); form.parse(req); - common.actions.checkForm(form, FIELDS, function(fieldsChecked) - { + common.actions.checkForm(form, FIELDS, function (fieldsChecked) { // keep track of number of the processed fields fieldsPassed = fieldsPassed - fieldsChecked; // finish it common.actions.formOnEnd(res); }); }); -server.listen(common.port, function() { +server.listen(common.port, function () { var form = new FormData(); @@ -57,9 +60,9 @@ server.listen(common.port, function() { port: common.port, path: '/', headers: { - 'x-test-header': 'test-header-value' - } - }, function(error, result) { + 'x-test-header': 'test-header-value', + }, + }, function (error, result) { if (error) { throw error; } @@ -73,6 +76,6 @@ server.listen(common.port, function() { }); -process.on('exit', function() { +process.on('exit', function () { assert.strictEqual(fieldsPassed, 0); });
test/integration/test-submit-custom.js+20 −16 modified@@ -1,3 +1,5 @@ +'use strict'; + var common = require('../common'); var assert = common.assert; var mime = require('mime-types'); @@ -7,35 +9,37 @@ var FormData = require(common.dir.lib + '/form_data'); var remoteFile = 'http://localhost:' + common.staticPort + '/unicycle.jpg'; -// wrap non simple values into function -// just to deal with ReadStream "autostart" +/* + * wrap non simple values into function + * just to deal with ReadStream "autostart" + */ var FIELDS = { - 'my_field': { - value: 'my_value' + my_field: { + value: 'my_value', }, - 'my_buffer': { + my_buffer: { type: FormData.DEFAULT_CONTENT_TYPE, - value: common.defaultTypeValue + value: common.defaultTypeValue, }, - 'my_file': { + my_file: { type: mime.lookup(common.dir.fixture + '/unicycle.jpg'), - value: function() { return fs.createReadStream(common.dir.fixture + '/unicycle.jpg'); } + value: function () { return fs.createReadStream(common.dir.fixture + '/unicycle.jpg'); }, }, - 'remote_file': { + remote_file: { type: mime.lookup(common.dir.fixture + '/unicycle.jpg'), - value: function() { return request(remoteFile); } - } + value: function () { return request(remoteFile); }, + }, }; // count total var fieldsPassed = Object.keys(FIELDS).length; // prepare form-receiving http server -var server = common.testFields(FIELDS, function(fields){ +var server = common.testFields(FIELDS, function (fields) { fieldsPassed = fields; }); -server.listen(common.port, function() { +server.listen(common.port, function () { var form = new FormData(); @@ -44,8 +48,8 @@ server.listen(common.port, function() { // custom params object passed to submit form.submit({ port: common.port, - path: '/' - }, function(err, res) { + path: '/', + }, function (err, res) { if (err) { throw err; @@ -59,6 +63,6 @@ server.listen(common.port, function() { }); -process.on('exit', function() { +process.on('exit', function () { assert.strictEqual(fieldsPassed, 0); });
test/integration/test-submit-https.js+11 −8 modified@@ -1,4 +1,7 @@ +'use strict'; + var https = require('https'); +var constants = require('constants'); var common = require('../common'); var assert = common.assert; var FormData = require(common.dir.lib + '/form_data'); @@ -19,9 +22,9 @@ function submitForm() { port: common.httpsPort, pathname: '/', // for self-signed certs on localhost - secureOptions: require('constants').SSL_OP_NO_TLSv1_2, - ca: common.httpsServerCert - }, function(err, res) { + secureOptions: constants.SSL_OP_NO_TLSv1_2, + ca: common.httpsServerCert, + }, function (err, res) { if (err) { throw err; @@ -40,14 +43,14 @@ function submitForm() { // create https server server = https.createServer({ key: common.httpsServerKey, - cert: common.httpsServerCert -}, function(req, res) { + cert: common.httpsServerCert, +}, function (req, res) { // old and simple - req.on('data', function() {}); + req.on('data', function () {}); - req.on('end', function() { - res.writeHead(200, {'x-success': 'OK'}); + req.on('end', function () { + res.writeHead(200, { 'x-success': 'OK' }); res.end('Great Success'); }); });
test/integration/test-submit.js+18 −15 modified@@ -1,3 +1,5 @@ +'use strict'; + var common = require('../common'); var assert = common.assert; var mime = require('mime-types'); @@ -7,36 +9,37 @@ var FormData = require(common.dir.lib + '/form_data'); var remoteFile = 'http://localhost:' + common.staticPort + '/unicycle.jpg'; -// wrap non simple values into function -// just to deal with ReadStream "autostart" +/* + * wrap non simple values into function + * just to deal with ReadStream "autostart" + */ var FIELDS = { - 'my_field': { - value: 'my_value' + my_field: { + value: 'my_value', }, - 'my_buffer': { + my_buffer: { type: FormData.DEFAULT_CONTENT_TYPE, - value: common.defaultTypeValue + value: common.defaultTypeValue, }, - 'my_file': { + my_file: { type: mime.lookup(common.dir.fixture + '/unicycle.jpg'), - value: function() { return fs.createReadStream(common.dir.fixture + '/unicycle.jpg'); } + value: function () { return fs.createReadStream(common.dir.fixture + '/unicycle.jpg'); }, }, - 'remote_file': { + remote_file: { type: mime.lookup(common.dir.fixture + '/unicycle.jpg'), - value: function() { return request(remoteFile); } - } + value: function () { return request(remoteFile); }, + }, }; // count total var fieldsPassed = Object.keys(FIELDS).length; // prepare form-receiving http server -var server = common.testFields( FIELDS, function(fields){ +var server = common.testFields(FIELDS, function (fields) { fieldsPassed = fields; }); - -server.listen(common.port, function() { +server.listen(common.port, function () { var form = new FormData(); @@ -45,6 +48,6 @@ server.listen(common.port, function() { common.actions.submit(form, server); }); -process.on('exit', function() { +process.on('exit', function () { assert.strictEqual(fieldsPassed, 0); });
test/integration/test-submit-multi.js+17 −11 modified@@ -1,3 +1,5 @@ +'use strict'; + var common = require('../common'); var assert = common.assert; var http = require('http'); @@ -14,29 +16,33 @@ function submitForm() { form.append('my_field', 'my_value'); - form.submit('http://localhost:' + common.port + '/', function(err, res) { + form.submit('http://localhost:' + common.port + '/', function (err, res) { if (err) { throw err; } assert.strictEqual(res.statusCode, 200); - // Needed for node-0.10.x because Streams2 - // more info: http://nodejs.org/api/stream.html#stream_compatibility_with_older_node_versions + /* + * Needed for node-0.10.x because Streams2 + * more info: http://nodejs.org/api/stream.html#stream_compatibility_with_older_node_versions + */ res.resume(); - times--; - if (times == 0) { + times -= 1; + if (times === 0) { server.close(); } }); } -server = http.createServer(function(req, res) { +server = http.createServer(function (req, res) { - // no need to have tmp dir here, since no files being uploaded - // but formidable would fail in 0.6 otherwise - var form = new IncomingForm({uploadDir: common.dir.tmp}); + /* + * no need to have tmp dir here, since no files being uploaded + * but formidable would fail in 0.6 otherwise + */ + var form = new IncomingForm({ uploadDir: common.dir.tmp }); form.parse(req); @@ -45,14 +51,14 @@ server = http.createServer(function(req, res) { .on('end', common.actions.formOnEnd.bind(null, res)); }); -server.listen(common.port, function() { +server.listen(common.port, function () { var i; for (i = 0; i < times; i++) { submitForm(); } }); -process.on('exit', function() { +process.on('exit', function () { assert.strictEqual(times, 0); });
test/integration/test-submit-multi-nocallback.js+13 −9 modified@@ -1,3 +1,5 @@ +'use strict'; + var common = require('../common'); var assert = common.assert; var http = require('http'); @@ -6,29 +8,31 @@ var IncomingForm = require('formidable').IncomingForm; var times = 10; -var server = http.createServer(function(req, res) { +var server = http.createServer(function (req, res) { - // no need to have tmp dir here, since no files being uploaded - // but formidable would fail in 0.6 otherwise - var form = new IncomingForm({uploadDir: common.dir.tmp}); + /* + * no need to have tmp dir here, since no files being uploaded + * but formidable would fail in 0.6 otherwise + */ + var form = new IncomingForm({ uploadDir: common.dir.tmp }); form.parse(req); form .on('field', common.actions.basicFormOnField) - .on('end', function() { + .on('end', function () { res.writeHead(200); res.end('done'); - times--; + times -= 1; - if (times == 0) { + if (times === 0) { server.close(); } }); }); -server.listen(common.port, function() { +server.listen(common.port, function () { var i; for (i = 0; i < times; i++) { @@ -41,6 +45,6 @@ server.listen(common.port, function() { }); -process.on('exit', function() { +process.on('exit', function () { assert.strictEqual(times, 0); });
test/integration/test-submit-url-parsing.js+5 −5 modified@@ -1,3 +1,5 @@ +'use strict'; + var http = require('http'); var https = require('https'); var common = require('../common'); @@ -13,21 +15,19 @@ var form = new FormData(); form.append('field', 'value'); // Basic parsing -req = form.submit('http://localhost/path', function() {}); +req = form.submit('http://localhost/path', function () {}); assert.strictEqual(req.path, '/path'); assert.ok(req.agent instanceof http.Agent, 'req.agent instanceof http.Agent'); assert.strictEqual(req.getHeader('Host'), 'localhost'); req.abort(); // Non-default port handling -req = form.submit('http://localhost:' + common.port, function() {}); +req = form.submit('http://localhost:' + common.port, function () {}); assert.strictEqual(req.getHeader('Host'), 'localhost:' + common.port); req.abort(); // HTTPS protocol handling -req = form.submit('https://localhost/path', function() {}); +req = form.submit('https://localhost/path', function () {}); assert.ok(req.agent instanceof https.Agent, 'req.agent instanceof https.Agent'); assert.strictEqual(req.getHeader('Host'), 'localhost'); req.abort(); - -
test/integration/test-to-string.js+4 −2 modified@@ -1,5 +1,7 @@ -var common = require('../common'); -var assert = common.assert; +'use strict'; + +var common = require('../common'); +var assert = common.assert; var FormData = require(common.dir.lib + '/form_data'); assert(new FormData().toString() === '[object FormData]');
test/run-browser.js+3 −2 modified@@ -1,9 +1,10 @@ +'use strict'; + var test = require('tape'); var FormData = require('../lib/browser.js'); var form = new FormData(); -test('being nice to browser-like environments', function(t) -{ +test('being nice to browser-like environments', function (t) { t.plan(3); t.notEqual(typeof FormData, 'undefined', 'FormData should be defined'); t.equal(typeof form, 'object', 'FormData instance should be object');
test/run.js+19 −13 modified@@ -1,6 +1,9 @@ #!/usr/bin/env node + +'use strict'; + var path = require('path'); -var static = require('./static'); +var staticS = require('./static'); var far = require('far').create(); var farPaths = require('far/lib/paths'); var spawn = require('cross-spawn'); @@ -9,17 +12,20 @@ var istanbul = path.join(basePath, './node_modules/.bin/istanbul'); // augment Far to support istanbul if (process.env.running_under_istanbul) { - far.constructor.prototype._execute = function(file) { + far.constructor.prototype._execute = function (file) { this._printStatus(file); var node = spawn(istanbul, [ 'cover', - '--report', 'none', - '--print', 'none', + '--report', + 'none', + '--print', + 'none', '--include-all-sources', '--include-pid', - '--root', basePath, - file + '--root', + basePath, + file, ]); var output = ''; @@ -32,7 +38,7 @@ if (process.env.running_under_istanbul) { * @param {string} chunk - partial output */ function onOutput(chunk) { - if (this._verbose > 1) { + if (this._verbose > 1) { // eslint-disable-line no-invalid-this process.stderr.write(chunk); } else { output += chunk; @@ -42,8 +48,8 @@ if (process.env.running_under_istanbul) { node.stdout.on('data', onOutput.bind(this)); node.stderr.on('data', onOutput.bind(this)); - node.on('exit', function(code) { - this._index++; + node.on('exit', function (code) { + this._index += 1; this._printTestResult(file, code, output); this._executeNext(); }.bind(this)); @@ -52,10 +58,10 @@ if (process.env.running_under_istanbul) { // augment far to work on windows -farPaths.expandSync = function(pathList) { +farPaths.expandSync = function (pathList) { var expanded = {}; - pathList.forEach(function(p) { + pathList.forEach(function (p) { p = path.resolve(process.cwd(), p); if (!farPaths.isDirectory(p)) { @@ -65,7 +71,7 @@ farPaths.expandSync = function(pathList) { farPaths .findRecursiveSync(p) - .forEach(function(pp) { + .forEach(function (pp) { expanded[pp] = true; }); }); @@ -83,6 +89,6 @@ far.add(__dirname); far.include(/test-.*\.js$/); // start static server for all tests -static(function() { +staticS(function () { far.execute(); });
test/static.js+5 −3 modified@@ -1,3 +1,5 @@ +'use strict'; + // serves static files var http = require('http'); var fs = require('fs'); @@ -10,17 +12,17 @@ if (!fs.existsSync(common.dir.tmp)) { fs.mkdirSync(common.dir.tmp); } -module.exports = function(callback) { +module.exports = function (callback) { // create http server - var httpServer = http.createServer(function(req, res) { + var httpServer = http.createServer(function (req, res) { var target = path.join(common.dir.fixture, req.url); var stat = fs.statSync(target); res.writeHead(200, { 'Content-Type': mime.lookup(target), - 'Content-Length': stat.size + 'Content-Length': stat.size, }); fs.createReadStream(target).pipe(res);
97ac9c208be0[Refactor] use `hasown`
6 files changed · +19 −12
lib/form_data.js+9 −8 modified@@ -7,6 +7,7 @@ var parseUrl = require('url').parse; var fs = require('fs'); var mime = require('mime-types'); var asynckit = require('asynckit'); +var hasOwn = require('hasown'); var setToStringTag = require('es-set-tostringtag'); var populate = require('./populate.js'); var Buffer = require('safe-buffer').Buffer; // eslint-disable-line no-shadow @@ -103,7 +104,7 @@ FormData.prototype._trackLength = function(header, value, options) { FormData.LINE_BREAK.length; // empty or either doesn't have path or not an http response - if (!value || ( !value.path && !(value.readable && Object.prototype.hasOwnProperty.call(value, 'httpVersion')) )) { + if (!value || ( !value.path && !(value.readable && hasOwn(value, 'httpVersion')) )) { return; } @@ -114,7 +115,7 @@ FormData.prototype._trackLength = function(header, value, options) { }; FormData.prototype._lengthRetriever = function(value, callback) { - if (Object.prototype.hasOwnProperty.call(value, 'fd')) { + if (hasOwn(value, 'fd')) { // take read range into a account // `end` = Infinity –> read file till the end @@ -149,11 +150,11 @@ FormData.prototype._lengthRetriever = function(value, callback) { } // or http response - } else if (Object.prototype.hasOwnProperty.call(value, 'httpVersion')) { + } else if (hasOwn(value, 'httpVersion')) { callback(null, +value.headers['content-length']); // or request stream http://github.com/mikeal/request - } else if (Object.prototype.hasOwnProperty.call(value, 'httpModule')) { + } else if (hasOwn(value, 'httpModule')) { // wait till response come back value.on('response', function(response) { value.pause(); @@ -193,7 +194,7 @@ FormData.prototype._multiPartHeader = function(field, value, options) { var header; for (var prop in headers) { - if (Object.prototype.hasOwnProperty.call(headers, prop)) { + if (hasOwn(headers, prop)) { header = headers[prop]; // skip nullish headers. @@ -230,7 +231,7 @@ FormData.prototype._getContentDisposition = function(value, options) { // formidable and the browser add a name property // fs- and request- streams have path property filename = path.basename(options.filename || value.name || value.path); - } else if (value.readable && Object.prototype.hasOwnProperty.call(value, 'httpVersion')) { + } else if (value.readable && hasOwn(value, 'httpVersion')) { // or try http response filename = path.basename(value.client._httpMessage.path || ''); } @@ -258,7 +259,7 @@ FormData.prototype._getContentType = function(value, options) { } // or if it's http-reponse - if (!contentType && value.readable && Object.prototype.hasOwnProperty.call(value, 'httpVersion')) { + if (!contentType && value.readable && hasOwn(value, 'httpVersion')) { contentType = value.headers['content-type']; } @@ -299,7 +300,7 @@ FormData.prototype.getHeaders = function(userHeaders) { }; for (header in userHeaders) { - if (Object.prototype.hasOwnProperty.call(userHeaders, header)) { + if (hasOwn(userHeaders, header)) { formHeaders[header.toLowerCase()] = userHeaders[header]; } }
package.json+1 −0 modified@@ -41,6 +41,7 @@ "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "has-own": "^1.0.1", "mime-types": "^2.1.35", "safe-buffer": "^5.2.1" },
test/common.js+2 −1 modified@@ -4,6 +4,7 @@ var assert = require('assert'); var fake = require('fake'); var mime = require('mime-types'); var http = require('http'); +var hasOwn = require('hasown'); var IncomingForm = require('formidable').IncomingForm; var common = module.exports; @@ -58,7 +59,7 @@ common.actions = {}; common.actions.populateFields = function (form, fields) { var field; for (var name in fields) { - if (Object.prototype.hasOwnProperty.call(fields, name)) { + if (hasOwn(fields, name)) { field = fields[name]; // important to append ReadStreams within the same tick if ((typeof field.value == 'function')) {
test/integration/test-custom-content-type.js+3 −1 modified@@ -3,6 +3,8 @@ var assert = common.assert; var http = require('http'); var mime = require('mime-types'); var fs = require('fs'); +var hasOwn = require('hasown'); + var FormData = require(common.dir.lib + '/form_data'); // wrap non simple values into function @@ -68,7 +70,7 @@ server.listen(common.port, function() { var form = new FormData(); for (var name in FIELDS) { - if (Object.prototype.hasOwnProperty.call(FIELDS, name)) { + if (hasOwn(FIELDS, name)) { var field = FIELDS[name]; // important to append ReadStreams within the same tick if ((typeof field.value == 'function')) {
test/integration/test-pipe.js+2 −1 modified@@ -4,6 +4,7 @@ var http = require('http'); var mime = require('mime-types'); var request = require('request'); var fs = require('fs'); +var hasOwn = require('hasown'); var FormData = require(common.dir.lib + '/form_data'); var IncomingForm = require('formidable').IncomingForm; @@ -50,7 +51,7 @@ server.listen(common.port, function() { var form = new FormData(); for (var name in FIELDS) { - if (Object.prototype.hasOwnProperty.call(FIELDS, name)) { + if (hasOwn(FIELDS, name)) { var field = FIELDS[name]; // important to append ReadStreams within the same tick if ((typeof field.value == 'function')) {
test/integration/test-ranged-filestream.js+2 −1 modified@@ -7,6 +7,7 @@ var common = require('../common'); var assert = common.assert; var http = require('http'); var fs = require('fs'); +var hasOwn = require('hasown'); var FormData = require(common.dir.lib + '/form_data'); var IncomingForm = require('formidable').IncomingForm; @@ -80,7 +81,7 @@ server.listen(common.port, function() { // add test subjects to the form for (name in testSubjects) { - if (Object.prototype.hasOwnProperty.call(testSubjects, name)) { + if (hasOwn(testSubjects, name)) { options = {encoding: 'utf8'}; if (testSubjects[name].start) { options.start = testSubjects[name].start; }
8f233664842d[Dev Deps] remove unused script
1 file changed · +0 −2
package.json+0 −2 modified@@ -19,7 +19,6 @@ "posttest": "npx npm@'>=10.2' audit --production", "lint": "eslint --ext=js,mjs .", "report": "istanbul report lcov text", - "ci-lint": "is-node-modern 8 && npm run lint || is-node-not-modern 8", "ci-test": "npm run tests-only && npm run browser && npm run report", "predebug": "rimraf coverage test/tmp", "debug": "verbose=1 ./test/run.js", @@ -57,7 +56,6 @@ "far": "^0.0.7", "formidable": "^1.2.6", "in-publish": "^2.0.1", - "is-node-modern": "^1.0.0", "istanbul": "^0.4.5", "obake": "^0.1.2", "phantomjs-prebuilt": "^2.1.16",
10626c0a9b78[meta] actually ensure the readme backup isn’t published
2 files changed · +5 −4
.npmignore+1 −1 modified@@ -9,4 +9,4 @@ node_modules/ test/ yarn.lock -README.md.bak +*.bak
package.json+4 −3 modified@@ -27,10 +27,11 @@ "files": "pkgfiles --sort=name", "get-version": "node -e \"console.log(require('./package.json').version)\"", "update-readme": "sed -i.bak 's/\\/master\\.svg/\\/v'$(npm --silent run get-version)'.svg/g' README.md", - "restore-readme": "mv README.md.bak README.md", + "postupdate-readme": "mv README.md.bak READ.ME.md.bak", + "restore-readme": "mv READ.ME.md.bak README.md", "prepublish": "not-in-publish || npm run prepublishOnly", - "prepublishOnly": "npm run update-readme", - "postpublish": "npm run restore-readme", + "prepack": "npm run update-readme", + "postpack": "npm run restore-readme", "version": "auto-changelog && git add CHANGELOG.md", "postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\"" },
2fd5f61ebfb5[meta] fix readme capitalization
1 file changed · +0 −0
README.md+0 −0 renamed
b5101ad3d5f7[meta] add `auto-changelog`
3 files changed · +604 −2
CHANGELOG.md+589 −0 added@@ -0,0 +1,589 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [v4.0.3](https://github.com/form-data/form-data/compare/v4.0.2...v4.0.3) - 2025-06-05 + +### Fixed + +- [Fix] `append`: avoid a crash on nullish values [`#577`](https://github.com/form-data/form-data/issues/577) + +### Commits + +- [eslint] use a shared config [`426ba9a`](https://github.com/form-data/form-data/commit/426ba9ac440f95d1998dac9a5cd8d738043b048f) +- [eslint] fix some spacing issues [`2094191`](https://github.com/form-data/form-data/commit/20941917f0e9487e68c564ebc3157e23609e2939) +- [Refactor] use `hasown` [`81ab41b`](https://github.com/form-data/form-data/commit/81ab41b46fdf34f5d89d7ff30b513b0925febfaa) +- [Fix] validate boundary type in `setBoundary()` method [`8d8e469`](https://github.com/form-data/form-data/commit/8d8e4693093519f7f18e3c597d1e8df8c493de9e) +- [Tests] add tests to check the behavior of `getBoundary` with non-strings [`837b8a1`](https://github.com/form-data/form-data/commit/837b8a1f7562bfb8bda74f3fc538adb7a5858995) +- [Dev Deps] remove unused deps [`870e4e6`](https://github.com/form-data/form-data/commit/870e4e665935e701bf983a051244ab928e62d58e) +- [meta] remove local commit hooks [`e6e83cc`](https://github.com/form-data/form-data/commit/e6e83ccb545a5619ed6cd04f31d5c2f655eb633e) +- [Dev Deps] update `eslint` [`4066fd6`](https://github.com/form-data/form-data/commit/4066fd6f65992b62fa324a6474a9292a4f88c916) +- [meta] fix scripts to use prepublishOnly [`c4bbb13`](https://github.com/form-data/form-data/commit/c4bbb13c0ef669916657bc129341301b1d331d75) + +## [v4.0.2](https://github.com/form-data/form-data/compare/v4.0.1...v4.0.2) - 2025-02-14 + +### Merged + +- [Fix] set `Symbol.toStringTag` when available [`#573`](https://github.com/form-data/form-data/pull/573) +- [Fix] set `Symbol.toStringTag` when available [`#573`](https://github.com/form-data/form-data/pull/573) +- fix (npmignore): ignore temporary build files [`#532`](https://github.com/form-data/form-data/pull/532) +- fix (npmignore): ignore temporary build files [`#532`](https://github.com/form-data/form-data/pull/532) + +### Fixed + +- [Fix] set `Symbol.toStringTag` when available (#573) [`#396`](https://github.com/form-data/form-data/issues/396) +- [Fix] set `Symbol.toStringTag` when available (#573) [`#396`](https://github.com/form-data/form-data/issues/396) +- [Fix] set `Symbol.toStringTag` when available [`#396`](https://github.com/form-data/form-data/issues/396) + +### Commits + +- Merge tags v2.5.3 and v3.0.3 [`92613b9`](https://github.com/form-data/form-data/commit/92613b9208556eb4ebc482fdf599fae111626fb6) +- [Tests] migrate from travis to GHA [`806eda7`](https://github.com/form-data/form-data/commit/806eda77740e6e3c67c7815afb216f2e1f187ba5) +- [Tests] migrate from travis to GHA [`8fdb3bc`](https://github.com/form-data/form-data/commit/8fdb3bc6b5d001f8909a9fca391d1d1d97ef1d79) +- [Refactor] use `Object.prototype.hasOwnProperty.call` [`7fecefe`](https://github.com/form-data/form-data/commit/7fecefe4ba8f775634aff86a698776ad95ecffb5) +- [Refactor] use `Object.prototype.hasOwnProperty.call` [`6e682d4`](https://github.com/form-data/form-data/commit/6e682d4bd41de7e80de41e3c4ee10f23fcc3dd00) +- [Refactor] use `Object.prototype.hasOwnProperty.call` [`df3c1e6`](https://github.com/form-data/form-data/commit/df3c1e6f0937f47a782dc4573756a54987f31dde) +- [Dev Deps] update `@types/node`, `browserify`, `coveralls`, `cross-spawn`, `eslint`, `formidable`, `in-publish`, `pkgfiles`, `pre-commit`, `puppeteer`, `request`, `tape`, `typescript` [`8261fcb`](https://github.com/form-data/form-data/commit/8261fcb8bf5944d30ae3bd04b91b71d6a9932ef4) +- [Dev Deps] update `@types/node`, `browserify`, `coveralls`, `cross-spawn`, `eslint`, `formidable`, `in-publish`, `pkgfiles`, `pre-commit`, `puppeteer`, `request`, `tape`, `typescript` [`fb66cb7`](https://github.com/form-data/form-data/commit/fb66cb740e29fb170eee947d4be6fdf82d6659af) +- [Dev Deps] update `@types/node`, `browserify`, `coveralls`, `eslint`, `formidable`, `in-publish`, `phantomjs-prebuilt`, `pkgfiles`, `pre-commit`, `request`, `tape`, `typescript` [`819f6b7`](https://github.com/form-data/form-data/commit/819f6b7a543306a891fca37c3a06d0ff4a734422) +- [eslint] clean up ignores [`3217b3d`](https://github.com/form-data/form-data/commit/3217b3ded8e382e51171d5c74c6038a21cc54440) +- [eslint] clean up ignores [`3a9d480`](https://github.com/form-data/form-data/commit/3a9d480232dbcbc07260ad84c3da4975d9a3ae9e) +- [Fix] `Buffer.from` and `Buffer.alloc` require node 4+ [`c499f76`](https://github.com/form-data/form-data/commit/c499f76f1faac1ddbf210c45217038e4c1e02337) +- Only apps should have lockfiles [`b82f590`](https://github.com/form-data/form-data/commit/b82f59093cdbadb4b7ec0922d33ae7ab048b82ff) +- Only apps should have lockfiles [`b170ee2`](https://github.com/form-data/form-data/commit/b170ee2b22b4c695c363b811c0c553d2fb1bbd79) +- [Deps] update `combined-stream`, `mime-types` [`6b1ca1d`](https://github.com/form-data/form-data/commit/6b1ca1dc7362a1b1c3a99a885516cca4b7eb817f) +- [Dev Deps] pin `request` which via `tough-cookie` ^2.4 depends on `psl` [`e5df7f2`](https://github.com/form-data/form-data/commit/e5df7f24383342264bd73dee3274818a40d04065) +- [Deps] update `mime-types` [`5a5bafe`](https://github.com/form-data/form-data/commit/5a5bafee894fead10da49e1fa2b084e17f2e1034) +- Bumped version 2.5.3 [`9457283`](https://github.com/form-data/form-data/commit/9457283e1dce6122adc908fdd7442cfc54cabe7a) +- [Dev Deps] pin `request` which via `tough-cookie` ^2.4 depends on `psl` [`9dbe192`](https://github.com/form-data/form-data/commit/9dbe192be3db215eac4d9c0b980470a5c2c030c6) +- Merge tags v2.5.2 and v3.0.2 [`d53265d`](https://github.com/form-data/form-data/commit/d53265d86c5153f535ec68eb107548b1b2883576) +- Bumped version 2.5.2 [`7020dd4`](https://github.com/form-data/form-data/commit/7020dd4c1260370abc40e86e3dfe49c5d576fbda) +- [Dev Deps] downgrade `cross-spawn` [`3fc1a9b`](https://github.com/form-data/form-data/commit/3fc1a9b62ddf1fe77a2bd6bd3476e4c0a9e01a88) +- fix: move util.isArray to Array.isArray (#564) [`edb555a`](https://github.com/form-data/form-data/commit/edb555a811f6f7e4668db4831551cf41c1de1cac) +- fix: move util.isArray to Array.isArray (#564) [`10418d1`](https://github.com/form-data/form-data/commit/10418d1fe4b0d65fe020eafe3911feb5ad5e2bd6) + +## [v4.0.1](https://github.com/form-data/form-data/compare/v4.0.0...v4.0.1) - 2024-10-10 + +### Commits + +- [Tests] migrate from travis to GHA [`757b4e3`](https://github.com/form-data/form-data/commit/757b4e32e95726aec9bdcc771fb5a3b564d88034) +- [eslint] clean up ignores [`e8f0d80`](https://github.com/form-data/form-data/commit/e8f0d80cd7cd424d1488532621ec40a33218b30b) +- fix (npmignore): ignore temporary build files [`335ad19`](https://github.com/form-data/form-data/commit/335ad19c6e17dc2d7298ffe0e9b37ba63600e94b) +- fix: move util.isArray to Array.isArray [`440d3be`](https://github.com/form-data/form-data/commit/440d3bed752ac2f9213b4c2229dbccefe140e5fa) + +## [v4.0.0](https://github.com/form-data/form-data/compare/v3.0.3...v4.0.0) - 2021-02-15 + +### Merged + +- Handle custom stream [`#382`](https://github.com/form-data/form-data/pull/382) + +### Commits + +- Fix typo [`e705c0a`](https://github.com/form-data/form-data/commit/e705c0a1fdaf90d21501f56460b93e43a18bd435) +- Update README for custom stream behavior [`6dd8624`](https://github.com/form-data/form-data/commit/6dd8624b2999e32768d62752c9aae5845a803b0d) + +## [v3.0.3](https://github.com/form-data/form-data/compare/v3.0.2...v3.0.3) - 2025-02-14 + +### Merged + +- [Fix] set `Symbol.toStringTag` when available [`#573`](https://github.com/form-data/form-data/pull/573) + +### Fixed + +- [Fix] set `Symbol.toStringTag` when available (#573) [`#396`](https://github.com/form-data/form-data/issues/396) + +### Commits + +- [Refactor] use `Object.prototype.hasOwnProperty.call` [`7fecefe`](https://github.com/form-data/form-data/commit/7fecefe4ba8f775634aff86a698776ad95ecffb5) +- [Dev Deps] update `@types/node`, `browserify`, `coveralls`, `cross-spawn`, `eslint`, `formidable`, `in-publish`, `pkgfiles`, `pre-commit`, `puppeteer`, `request`, `tape`, `typescript` [`8261fcb`](https://github.com/form-data/form-data/commit/8261fcb8bf5944d30ae3bd04b91b71d6a9932ef4) +- Only apps should have lockfiles [`b82f590`](https://github.com/form-data/form-data/commit/b82f59093cdbadb4b7ec0922d33ae7ab048b82ff) +- [Dev Deps] pin `request` which via `tough-cookie` ^2.4 depends on `psl` [`e5df7f2`](https://github.com/form-data/form-data/commit/e5df7f24383342264bd73dee3274818a40d04065) +- [Deps] update `mime-types` [`5a5bafe`](https://github.com/form-data/form-data/commit/5a5bafee894fead10da49e1fa2b084e17f2e1034) + +## [v3.0.2](https://github.com/form-data/form-data/compare/v3.0.1...v3.0.2) - 2024-10-10 + +### Merged + +- fix (npmignore): ignore temporary build files [`#532`](https://github.com/form-data/form-data/pull/532) + +### Commits + +- [Tests] migrate from travis to GHA [`8fdb3bc`](https://github.com/form-data/form-data/commit/8fdb3bc6b5d001f8909a9fca391d1d1d97ef1d79) +- [eslint] clean up ignores [`3217b3d`](https://github.com/form-data/form-data/commit/3217b3ded8e382e51171d5c74c6038a21cc54440) +- fix: move util.isArray to Array.isArray (#564) [`edb555a`](https://github.com/form-data/form-data/commit/edb555a811f6f7e4668db4831551cf41c1de1cac) + +## [v3.0.1](https://github.com/form-data/form-data/compare/v3.0.0...v3.0.1) - 2021-02-15 + +### Merged + +- Fix typo: ads -> adds [`#451`](https://github.com/form-data/form-data/pull/451) + +### Commits + +- feat: add setBoundary method [`55d90ce`](https://github.com/form-data/form-data/commit/55d90ce4a4c22b0ea0647991d85cb946dfb7395b) + +## [v3.0.0](https://github.com/form-data/form-data/compare/v2.5.3...v3.0.0) - 2019-11-05 + +### Merged + +- Update Readme.md [`#449`](https://github.com/form-data/form-data/pull/449) +- Update package.json [`#448`](https://github.com/form-data/form-data/pull/448) +- fix memory leak [`#447`](https://github.com/form-data/form-data/pull/447) +- form-data: Replaced PhantomJS Dependency [`#442`](https://github.com/form-data/form-data/pull/442) +- Fix constructor options in Typescript definitions [`#446`](https://github.com/form-data/form-data/pull/446) +- Fix the getHeaders method signatures [`#434`](https://github.com/form-data/form-data/pull/434) +- Update combined-stream (fixes #422) [`#424`](https://github.com/form-data/form-data/pull/424) + +### Fixed + +- Merge pull request #424 from botgram/update-combined-stream [`#422`](https://github.com/form-data/form-data/issues/422) +- Update combined-stream (fixes #422) [`#422`](https://github.com/form-data/form-data/issues/422) + +### Commits + +- Add readable stream options to constructor type [`80c8f74`](https://github.com/form-data/form-data/commit/80c8f746bcf4c0418ae35fbedde12fb8c01e2748) +- Fixed: getHeaders method signatures [`f4ca7f8`](https://github.com/form-data/form-data/commit/f4ca7f8e31f7e07df22c1aeb8e0a32a7055a64ca) +- Pass options to constructor if not used with new [`4bde68e`](https://github.com/form-data/form-data/commit/4bde68e12de1ba90fefad2e7e643f6375b902763) +- Make userHeaders optional [`2b4e478`](https://github.com/form-data/form-data/commit/2b4e4787031490942f2d1ee55c56b85a250875a7) + +## [v2.5.3](https://github.com/form-data/form-data/compare/v2.5.2...v2.5.3) - 2025-02-14 + +### Merged + +- [Fix] set `Symbol.toStringTag` when available [`#573`](https://github.com/form-data/form-data/pull/573) + +### Fixed + +- [Fix] set `Symbol.toStringTag` when available (#573) [`#396`](https://github.com/form-data/form-data/issues/396) + +### Commits + +- [Refactor] use `Object.prototype.hasOwnProperty.call` [`6e682d4`](https://github.com/form-data/form-data/commit/6e682d4bd41de7e80de41e3c4ee10f23fcc3dd00) +- [Dev Deps] update `@types/node`, `browserify`, `coveralls`, `eslint`, `formidable`, `in-publish`, `phantomjs-prebuilt`, `pkgfiles`, `pre-commit`, `request`, `tape`, `typescript` [`819f6b7`](https://github.com/form-data/form-data/commit/819f6b7a543306a891fca37c3a06d0ff4a734422) +- Only apps should have lockfiles [`b170ee2`](https://github.com/form-data/form-data/commit/b170ee2b22b4c695c363b811c0c553d2fb1bbd79) +- [Deps] update `combined-stream`, `mime-types` [`6b1ca1d`](https://github.com/form-data/form-data/commit/6b1ca1dc7362a1b1c3a99a885516cca4b7eb817f) +- Bumped version 2.5.3 [`9457283`](https://github.com/form-data/form-data/commit/9457283e1dce6122adc908fdd7442cfc54cabe7a) +- [Dev Deps] pin `request` which via `tough-cookie` ^2.4 depends on `psl` [`9dbe192`](https://github.com/form-data/form-data/commit/9dbe192be3db215eac4d9c0b980470a5c2c030c6) + +## [v2.5.2](https://github.com/form-data/form-data/compare/v2.5.1...v2.5.2) - 2024-10-10 + +### Merged + +- fix (npmignore): ignore temporary build files [`#532`](https://github.com/form-data/form-data/pull/532) + +### Commits + +- [Tests] migrate from travis to GHA [`806eda7`](https://github.com/form-data/form-data/commit/806eda77740e6e3c67c7815afb216f2e1f187ba5) +- [eslint] clean up ignores [`3a9d480`](https://github.com/form-data/form-data/commit/3a9d480232dbcbc07260ad84c3da4975d9a3ae9e) +- [Fix] `Buffer.from` and `Buffer.alloc` require node 4+ [`c499f76`](https://github.com/form-data/form-data/commit/c499f76f1faac1ddbf210c45217038e4c1e02337) +- Bumped version 2.5.2 [`7020dd4`](https://github.com/form-data/form-data/commit/7020dd4c1260370abc40e86e3dfe49c5d576fbda) +- [Dev Deps] downgrade `cross-spawn` [`3fc1a9b`](https://github.com/form-data/form-data/commit/3fc1a9b62ddf1fe77a2bd6bd3476e4c0a9e01a88) +- fix: move util.isArray to Array.isArray (#564) [`10418d1`](https://github.com/form-data/form-data/commit/10418d1fe4b0d65fe020eafe3911feb5ad5e2bd6) + +## [v2.5.1](https://github.com/form-data/form-data/compare/v2.5.0...v2.5.1) - 2019-08-28 + +### Merged + +- Fix error in callback signatures [`#435`](https://github.com/form-data/form-data/pull/435) +- -Fixed: Eerror in the documentations as indicated in #439 [`#440`](https://github.com/form-data/form-data/pull/440) +- Add constructor options to TypeScript defs [`#437`](https://github.com/form-data/form-data/pull/437) + +### Commits + +- Add remaining combined-stream options to typedef [`4d41a32`](https://github.com/form-data/form-data/commit/4d41a32c0b3f85f8bbc9cf17df43befd2d5fc305) +- Bumped version 2.5.1 [`8ce81f5`](https://github.com/form-data/form-data/commit/8ce81f56cccf5466363a5eff135ad394a929f59b) +- Bump rimraf to 2.7.1 [`a6bc2d4`](https://github.com/form-data/form-data/commit/a6bc2d4296dbdee5d84cbab7c69bcd0eea7a12e2) + +## [v2.5.0](https://github.com/form-data/form-data/compare/v2.4.0...v2.5.0) - 2019-07-03 + +### Merged + +- - Added: public methods with information and examples to readme [`#429`](https://github.com/form-data/form-data/pull/429) +- chore: move @types/node to devDep [`#431`](https://github.com/form-data/form-data/pull/431) +- Switched windows tests from AppVeyor to Travis [`#430`](https://github.com/form-data/form-data/pull/430) +- feat(typings): migrate TS typings #427 [`#428`](https://github.com/form-data/form-data/pull/428) +- enhance the method of path.basename, handle undefined case [`#421`](https://github.com/form-data/form-data/pull/421) + +### Commits + +- - Added: public methods with information and examples to the readme file. [`21323f3`](https://github.com/form-data/form-data/commit/21323f3b4043a167046a4a2554c5f2825356c423) +- feat(typings): migrate TS typings [`a3c0142`](https://github.com/form-data/form-data/commit/a3c0142ed91b0c7dcaf89c4f618776708f1f70a9) +- - Fixed: Typos [`37350fa`](https://github.com/form-data/form-data/commit/37350fa250782f156a998ec1fa9671866d40ac49) +- Switched to Travis Windows from Appveyor [`fc61c73`](https://github.com/form-data/form-data/commit/fc61c7381fad12662df16dbc3e7621c91b886f03) +- - Fixed: rendering of subheaders [`e93ed8d`](https://github.com/form-data/form-data/commit/e93ed8df9d7f22078bc3a2c24889e9dfa11e192d) +- Updated deps and readme [`e3d8628`](https://github.com/form-data/form-data/commit/e3d8628728f6e4817ab97deeed92f0c822661b89) +- Updated dependencies [`19add50`](https://github.com/form-data/form-data/commit/19add50afb7de66c70d189f422d16f1b886616e2) +- Bumped version to 2.5.0 [`905f173`](https://github.com/form-data/form-data/commit/905f173a3f785e8d312998e765634ee451ca5f42) +- - Fixed: filesize is not a valid option? knownLength should be used for streams [`d88f912`](https://github.com/form-data/form-data/commit/d88f912b75b666b47f8674467516eade69d2d5be) +- Bump notion of modern node to node8 [`508b626`](https://github.com/form-data/form-data/commit/508b626bf1b460d3733d3420dc1cfd001617f6ac) +- enhance the method of path.basename [`faaa68a`](https://github.com/form-data/form-data/commit/faaa68a297be7d4fca0ac4709d5b93afc1f78b5c) + +## [v2.4.0](https://github.com/form-data/form-data/compare/v2.3.2...v2.4.0) - 2019-06-19 + +### Merged + +- Added "getBuffer" method and updated certificates [`#419`](https://github.com/form-data/form-data/pull/419) +- docs(readme): add axios integration document [`#425`](https://github.com/form-data/form-data/pull/425) +- Allow newer versions of combined-stream [`#402`](https://github.com/form-data/form-data/pull/402) + +### Commits + +- Updated: Certificate [`e90a76a`](https://github.com/form-data/form-data/commit/e90a76ab3dcaa63a6f3045f8255bfbb9c25a3e4e) +- Updated build/test/badges [`8512eef`](https://github.com/form-data/form-data/commit/8512eef436e28372f5bc88de3ca76a9cb46e6847) +- Bumped version 2.4.0 [`0f8da06`](https://github.com/form-data/form-data/commit/0f8da06c0b4c997bd2f6b09d78290d339616a950) +- docs(readme): remove unnecessary bracket [`4e3954d`](https://github.com/form-data/form-data/commit/4e3954dde304d27e3b95371d8c78002f3af5d5b2) +- Bumped version to 2.3.3 [`b16916a`](https://github.com/form-data/form-data/commit/b16916a568a0d06f3f8a16c31f9a8b89b7844094) + +## [v2.3.2](https://github.com/form-data/form-data/compare/v2.3.1...v2.3.2) - 2018-02-13 + +### Merged + +- Pulling in fixed combined-stream [`#379`](https://github.com/form-data/form-data/pull/379) + +### Commits + +- All the dev dependencies are breaking in old versions of node :'( [`c7dba6a`](https://github.com/form-data/form-data/commit/c7dba6a139d872d173454845e25e1850ed6b72b4) +- Updated badges [`19b6c7a`](https://github.com/form-data/form-data/commit/19b6c7a8a5c40f47f91c8a8da3e5e4dc3c449fa3) +- Try tests in node@4 [`872a326`](https://github.com/form-data/form-data/commit/872a326ab13e2740b660ff589b75232c3a85fcc9) +- Pull in final version [`9d44871`](https://github.com/form-data/form-data/commit/9d44871073d647995270b19dbc26f65671ce15c7) + +## [v2.3.1](https://github.com/form-data/form-data/compare/v2.3.0...v2.3.1) - 2017-08-24 + +### Commits + +- Updated readme with custom options example [`8e0a569`](https://github.com/form-data/form-data/commit/8e0a5697026016fe171e93bec43c2205279e23ca) +- Added support (tests) for node 8 [`d1d6f4a`](https://github.com/form-data/form-data/commit/d1d6f4ad4670d8ba84cc85b28e522ca0e93eb362) + +## [v2.3.0](https://github.com/form-data/form-data/compare/v2.2.0...v2.3.0) - 2017-08-24 + +### Merged + +- Added custom `options` support [`#368`](https://github.com/form-data/form-data/pull/368) +- Allow form.submit with url string param to use https [`#249`](https://github.com/form-data/form-data/pull/249) +- Proper header production [`#357`](https://github.com/form-data/form-data/pull/357) +- Fix wrong MIME type in example [`#285`](https://github.com/form-data/form-data/pull/285) + +### Commits + +- allow form.submit with url string param to use https [`c0390dc`](https://github.com/form-data/form-data/commit/c0390dcc623e15215308fa2bb0225aa431d9381e) +- update tests for url parsing [`eec0e80`](https://github.com/form-data/form-data/commit/eec0e807889d46697abd39a89ad9bf39996ba787) +- Uses for in to assign properties instead of Object.assign [`f6854ed`](https://github.com/form-data/form-data/commit/f6854edd85c708191bb9c89615a09fd0a9afe518) +- Adds test to check for option override [`61762f2`](https://github.com/form-data/form-data/commit/61762f2c5262e576d6a7f778b4ebab6546ef8582) +- Removes the 2mb maxDataSize limitation [`dc171c3`](https://github.com/form-data/form-data/commit/dc171c3ba49ac9b8813636fd4159d139b812315b) +- Ignore .DS_Store [`e8a05d3`](https://github.com/form-data/form-data/commit/e8a05d33361f7dca8927fe1d96433d049843de24) + +## [v2.2.0](https://github.com/form-data/form-data/compare/v2.1.4...v2.2.0) - 2017-06-11 + +### Merged + +- Filename can be a nested path [`#355`](https://github.com/form-data/form-data/pull/355) + +### Commits + +- Bumped version number. [`d7398c3`](https://github.com/form-data/form-data/commit/d7398c3e7cd81ed12ecc0b84363721bae467db02) + +## [v2.1.4](https://github.com/form-data/form-data/compare/2.1.3...v2.1.4) - 2017-04-08 + +## [2.1.3](https://github.com/form-data/form-data/compare/v2.1.3...2.1.3) - 2017-04-08 + +## [v2.1.3](https://github.com/form-data/form-data/compare/v2.1.2...v2.1.3) - 2017-04-08 + +### Merged + +- toString should output '[object FormData]' [`#346`](https://github.com/form-data/form-data/pull/346) + +## [v2.1.2](https://github.com/form-data/form-data/compare/v2.1.1...v2.1.2) - 2016-11-07 + +### Merged + +- #271 Added check for self and window objects + tests [`#282`](https://github.com/form-data/form-data/pull/282) + +### Commits + +- Added check for self and window objects + tests [`c99e4ec`](https://github.com/form-data/form-data/commit/c99e4ec32cd14d83776f2bdcc5a4e7384131c1b1) + +## [v2.1.1](https://github.com/form-data/form-data/compare/v2.1.0...v2.1.1) - 2016-10-03 + +### Merged + +- Bumped dependencies. [`#270`](https://github.com/form-data/form-data/pull/270) +- Update browser.js shim to use self instead of window [`#267`](https://github.com/form-data/form-data/pull/267) +- Boilerplate code rediction [`#265`](https://github.com/form-data/form-data/pull/265) +- eslint@3.7.0 [`#266`](https://github.com/form-data/form-data/pull/266) + +### Commits + +- code duplicates removed [`e9239fb`](https://github.com/form-data/form-data/commit/e9239fbe7d3c897b29fe3bde857d772469541c01) +- Changed according to requests [`aa99246`](https://github.com/form-data/form-data/commit/aa9924626bd9168334d73fea568c0ad9d8fbaa96) +- chore(package): update eslint to version 3.7.0 [`090a859`](https://github.com/form-data/form-data/commit/090a859835016cab0de49629140499e418db9c3a) + +## [v2.1.0](https://github.com/form-data/form-data/compare/v2.0.0...v2.1.0) - 2016-09-25 + +### Merged + +- Added `hasKnownLength` public method [`#263`](https://github.com/form-data/form-data/pull/263) + +### Commits + +- Added hasKnownLength public method [`655b959`](https://github.com/form-data/form-data/commit/655b95988ef2ed3399f8796b29b2a8673c1df11c) + +## [v2.0.0](https://github.com/form-data/form-data/compare/v1.0.0...v2.0.0) - 2016-09-16 + +### Merged + +- Replaced async with asynckit [`#258`](https://github.com/form-data/form-data/pull/258) +- Pre-release house cleaning [`#247`](https://github.com/form-data/form-data/pull/247) + +### Commits + +- Replaced async with asynckit. Modernized [`1749b78`](https://github.com/form-data/form-data/commit/1749b78d50580fbd080e65c1eb9702ad4f4fc0c0) +- Ignore .bak files [`c08190a`](https://github.com/form-data/form-data/commit/c08190a87d3e22a528b6e32b622193742a4c2672) +- Trying to be more chatty. :) [`c79eabb`](https://github.com/form-data/form-data/commit/c79eabb24eaf761069255a44abf4f540cfd47d40) + +## [v1.0.0](https://github.com/form-data/form-data/compare/v1.0.0-rc4...v1.0.0) - 2016-08-26 + +### Merged + +- Allow custom header fields to be set as an object. [`#190`](https://github.com/form-data/form-data/pull/190) +- v1.0.0-rc4 [`#182`](https://github.com/form-data/form-data/pull/182) +- Avoid undefined variable reference in older browsers [`#176`](https://github.com/form-data/form-data/pull/176) +- More housecleaning [`#164`](https://github.com/form-data/form-data/pull/164) +- More cleanup [`#159`](https://github.com/form-data/form-data/pull/159) +- Added windows testing. Some cleanup. [`#158`](https://github.com/form-data/form-data/pull/158) +- Housecleaning. Added test coverage. [`#156`](https://github.com/form-data/form-data/pull/156) +- Second iteration of cleanup. [`#145`](https://github.com/form-data/form-data/pull/145) + +### Commits + +- Pre-release house cleaning [`440d72b`](https://github.com/form-data/form-data/commit/440d72b5fd44dd132f42598c3183d46e5f35ce71) +- Updated deps, updated docs [`54b6114`](https://github.com/form-data/form-data/commit/54b61143e9ce66a656dd537a1e7b31319a4991be) +- make docs up-to-date [`5e383d7`](https://github.com/form-data/form-data/commit/5e383d7f1466713f7fcef58a6817e0cb466c8ba7) +- Added missing deps [`fe04862`](https://github.com/form-data/form-data/commit/fe04862000b2762245e2db69d5207696a08c1174) + +## [v1.0.0-rc4](https://github.com/form-data/form-data/compare/v1.0.0-rc3...v1.0.0-rc4) - 2016-03-15 + +### Merged + +- Housecleaning, preparing for the release [`#144`](https://github.com/form-data/form-data/pull/144) +- lib: emit error when failing to get length [`#127`](https://github.com/form-data/form-data/pull/127) +- Cleaning up for Codacity 2. [`#143`](https://github.com/form-data/form-data/pull/143) +- Cleaned up codacity concerns. [`#142`](https://github.com/form-data/form-data/pull/142) +- Should throw type error without new operator. [`#129`](https://github.com/form-data/form-data/pull/129) + +### Commits + +- More cleanup [`94b6565`](https://github.com/form-data/form-data/commit/94b6565bb98a387335c72feff5ed5c10da0a7f6f) +- Shuffling things around [`3c2f172`](https://github.com/form-data/form-data/commit/3c2f172eaddf0979b3eef5c73985d1a6fd3eee4a) +- Second iteration of cleanup. [`347c88e`](https://github.com/form-data/form-data/commit/347c88ef9a99a66b9bcf4278497425db2f0182b2) +- Housecleaning [`c335610`](https://github.com/form-data/form-data/commit/c3356100c054a4695e4dec8ed7072775cd745616) +- More housecleaning [`f573321`](https://github.com/form-data/form-data/commit/f573321824aae37ba2052a92cc889d533d9f8fb8) +- Trying to make far run on windows. + cleanup [`e426dfc`](https://github.com/form-data/form-data/commit/e426dfcefb07ee307d8a15dec04044cce62413e6) +- Playing with appveyor [`c9458a7`](https://github.com/form-data/form-data/commit/c9458a7c328782b19859bc1745e7d6b2005ede86) +- Updated dev dependencies. [`ceebe88`](https://github.com/form-data/form-data/commit/ceebe88872bb22da0a5a98daf384e3cc232928d3) +- Replaced win-spawn with cross-spawn [`405a69e`](https://github.com/form-data/form-data/commit/405a69ee34e235ee6561b5ff0140b561be40d1cc) +- Updated readme badges. [`12f282a`](https://github.com/form-data/form-data/commit/12f282a1310fcc2f70cc5669782283929c32a63d) +- Making paths windows friendly. [`f4bddc5`](https://github.com/form-data/form-data/commit/f4bddc5955e2472f8e23c892c9b4d7a08fcb85a3) +- [WIP] trying things for greater sanity [`8ad1f02`](https://github.com/form-data/form-data/commit/8ad1f02b0b3db4a0b00c5d6145ed69bcb7558213) +- Bending under Codacy [`bfff3bb`](https://github.com/form-data/form-data/commit/bfff3bb36052dc83f429949b4e6f9b146a49d996) +- Another attempt to make windows friendly [`f3eb628`](https://github.com/form-data/form-data/commit/f3eb628974ccb91ba0020f41df490207eeed77f6) +- Updated dependencies. [`f73996e`](https://github.com/form-data/form-data/commit/f73996e0508ee2d4b2b376276adfac1de4188ac2) +- Missed travis changes. [`67ee79f`](https://github.com/form-data/form-data/commit/67ee79f964fdabaf300bd41b0af0c1cfaca07687) +- Restructured badges. [`48444a1`](https://github.com/form-data/form-data/commit/48444a1ff156ba2c2c3cfd11047c2f2fd92d4474) +- Add similar type error as the browser for attempting to use form-data without new. [`5711320`](https://github.com/form-data/form-data/commit/5711320fb7c8cc620cfc79b24c7721526e23e539) +- Took out codeclimate-test-reporter [`a7e0c65`](https://github.com/form-data/form-data/commit/a7e0c6522afe85ca9974b0b4e1fca9c77c3e52b1) +- One more [`8e84cff`](https://github.com/form-data/form-data/commit/8e84cff3370526ecd3e175fd98e966242d81993c) + +## [v1.0.0-rc3](https://github.com/form-data/form-data/compare/v1.0.0-rc2...v1.0.0-rc3) - 2015-07-29 + +### Merged + +- House cleaning. Added `pre-commit`. [`#140`](https://github.com/form-data/form-data/pull/140) +- Allow custom content-type without setting a filename. [`#138`](https://github.com/form-data/form-data/pull/138) +- Add node-fetch to alternative submission methods. [`#132`](https://github.com/form-data/form-data/pull/132) +- Update dependencies [`#130`](https://github.com/form-data/form-data/pull/130) +- Switching to container based TravisCI [`#136`](https://github.com/form-data/form-data/pull/136) +- Default content-type to 'application/octect-stream' [`#128`](https://github.com/form-data/form-data/pull/128) +- Allow filename as third option of .append [`#125`](https://github.com/form-data/form-data/pull/125) + +### Commits + +- Allow custom content-type without setting a filename [`c8a77cc`](https://github.com/form-data/form-data/commit/c8a77cc0cf16d15f1ebf25272beaab639ce89f76) +- Fixed ranged test. [`a5ac58c`](https://github.com/form-data/form-data/commit/a5ac58cbafd0909f32fe8301998f689314fd4859) +- Allow filename as third option of #append [`d081005`](https://github.com/form-data/form-data/commit/d0810058c84764b3c463a18b15ebb37864de9260) +- Allow custom content-type without setting a filename [`8cb9709`](https://github.com/form-data/form-data/commit/8cb9709e5f1809cfde0cd707dbabf277138cd771) + +## [v1.0.0-rc2](https://github.com/form-data/form-data/compare/v1.0.0-rc1...v1.0.0-rc2) - 2015-07-21 + +### Merged + +- #109 Append proper line break [`#123`](https://github.com/form-data/form-data/pull/123) +- Add shim for browser (browserify/webpack). [`#122`](https://github.com/form-data/form-data/pull/122) +- Update license field [`#115`](https://github.com/form-data/form-data/pull/115) + +### Commits + +- Add shim for browser. [`87c33f4`](https://github.com/form-data/form-data/commit/87c33f4269a2211938f80ab3e53835362b1afee8) +- Bump version [`a3f5d88`](https://github.com/form-data/form-data/commit/a3f5d8872c810ce240c7d3838c69c3c9fcecc111) + +## [v1.0.0-rc1](https://github.com/form-data/form-data/compare/0.2...v1.0.0-rc1) - 2015-06-13 + +### Merged + +- v1.0.0-rc1 [`#114`](https://github.com/form-data/form-data/pull/114) +- Updated test targets [`#102`](https://github.com/form-data/form-data/pull/102) +- Remove duplicate plus sign [`#94`](https://github.com/form-data/form-data/pull/94) + +### Commits + +- Made https test local. Updated deps. [`afe1959`](https://github.com/form-data/form-data/commit/afe1959ec711f23e57038ab5cb20fedd86271f29) +- Proper self-signed ssl [`4d5ec50`](https://github.com/form-data/form-data/commit/4d5ec50e81109ad2addf3dbb56dc7c134df5ff87) +- Update HTTPS handling for modern days [`2c11b01`](https://github.com/form-data/form-data/commit/2c11b01ce2c06e205c84d7154fa2f27b66c94f3b) +- Made tests more local [`09633fa`](https://github.com/form-data/form-data/commit/09633fa249e7ce3ac581543aafe16ee9039a823b) +- Auto create tmp folder for Formidable [`28714b7`](https://github.com/form-data/form-data/commit/28714b7f71ad556064cdff88fabe6b92bd407ddd) +- remove duplicate plus sign [`36e09c6`](https://github.com/form-data/form-data/commit/36e09c695b0514d91a23f5cd64e6805404776fc7) + +## [0.2](https://github.com/form-data/form-data/compare/0.1.4...0.2) - 2014-12-06 + +### Merged + +- Bumped version [`#96`](https://github.com/form-data/form-data/pull/96) +- Replace mime library. [`#95`](https://github.com/form-data/form-data/pull/95) +- #71 Respect bytes range in a read stream. [`#73`](https://github.com/form-data/form-data/pull/73) + +## [0.1.4](https://github.com/form-data/form-data/compare/0.1.3...0.1.4) - 2014-06-23 + +### Merged + +- Updated version. [`#76`](https://github.com/form-data/form-data/pull/76) +- #71 Respect bytes range in a read stream. [`#75`](https://github.com/form-data/form-data/pull/75) + +## [0.1.3](https://github.com/form-data/form-data/compare/0.1.2...0.1.3) - 2014-06-17 + +### Merged + +- Updated versions. [`#69`](https://github.com/form-data/form-data/pull/69) +- Added custom headers support [`#60`](https://github.com/form-data/form-data/pull/60) +- Added test for Request. Small fixes. [`#56`](https://github.com/form-data/form-data/pull/56) + +### Commits + +- Added test for the custom header functionality [`bd50685`](https://github.com/form-data/form-data/commit/bd506855af62daf728ef1718cae88ed23bb732f3) +- Documented custom headers option [`77a024a`](https://github.com/form-data/form-data/commit/77a024a9375f93c246c35513d80f37d5e11d35ff) +- Removed 0.6 support. [`aee8dce`](https://github.com/form-data/form-data/commit/aee8dce604c595cfaacfc6efb12453d1691ac0d6) + +## [0.1.2](https://github.com/form-data/form-data/compare/0.1.1...0.1.2) - 2013-10-02 + +### Merged + +- Fixed default https port assignment, added tests. [`#52`](https://github.com/form-data/form-data/pull/52) +- #45 Added tests for multi-submit. Updated readme. [`#49`](https://github.com/form-data/form-data/pull/49) +- #47 return request from .submit() [`#48`](https://github.com/form-data/form-data/pull/48) + +### Commits + +- Bumped version. [`2b761b2`](https://github.com/form-data/form-data/commit/2b761b256ae607fc2121621f12c2e1042be26baf) + +## [0.1.1](https://github.com/form-data/form-data/compare/0.1.0...0.1.1) - 2013-08-21 + +### Merged + +- Added license type and reference to package.json [`#46`](https://github.com/form-data/form-data/pull/46) + +### Commits + +- #47 return request from .submit() [`1d61c2d`](https://github.com/form-data/form-data/commit/1d61c2da518bd5e136550faa3b5235bb540f1e06) +- #47 Updated readme. [`e3dae15`](https://github.com/form-data/form-data/commit/e3dae1526bd3c3b9d7aff6075abdaac12c3cc60f) + +## [0.1.0](https://github.com/form-data/form-data/compare/0.0.10...0.1.0) - 2013-07-08 + +### Merged + +- Update master to 0.1.0 [`#44`](https://github.com/form-data/form-data/pull/44) +- 0.1.0 - Added error handling. Streamlined edge cases behavior. [`#43`](https://github.com/form-data/form-data/pull/43) +- Pointed badges back to mothership. [`#39`](https://github.com/form-data/form-data/pull/39) +- Updated node-fake to support 0.11 tests. [`#37`](https://github.com/form-data/form-data/pull/37) +- Updated tests to play nice with 0.10 [`#36`](https://github.com/form-data/form-data/pull/36) +- #32 Added .npmignore [`#34`](https://github.com/form-data/form-data/pull/34) +- Spring cleaning [`#30`](https://github.com/form-data/form-data/pull/30) + +### Commits + +- Added error handling. Streamlined edge cases behavior. [`4da496e`](https://github.com/form-data/form-data/commit/4da496e577cb9bc0fd6c94cbf9333a0082ce353a) +- Made tests more deterministic. [`7fc009b`](https://github.com/form-data/form-data/commit/7fc009b8a2cc9232514a44b2808b9f89ce68f7d2) +- Fixed styling. [`d373b41`](https://github.com/form-data/form-data/commit/d373b417e779024bc3326073e176383cd08c0b18) +- #40 Updated Readme.md regarding getLengthSync() [`efb373f`](https://github.com/form-data/form-data/commit/efb373fd63814d977960e0299d23c92cd876cfef) +- Updated readme. [`527e3a6`](https://github.com/form-data/form-data/commit/527e3a63b032cb6f576f597ad7ff2ebcf8a0b9b4) + +## [0.0.10](https://github.com/form-data/form-data/compare/0.0.9...0.0.10) - 2013-05-08 + +### Commits + +- Updated tests to play nice with 0.10. [`932b39b`](https://github.com/form-data/form-data/commit/932b39b773e49edcb2c5d2e58fe389ab6c42f47c) +- Added dependency tracking. [`3131d7f`](https://github.com/form-data/form-data/commit/3131d7f6996cd519d50547e4de1587fd80d0fa07) + +## 0.0.9 - 2013-04-29 + +### Merged + +- Custom params for form.submit() should cover most edge cases. [`#22`](https://github.com/form-data/form-data/pull/22) +- Updated Readme and version number. [`#20`](https://github.com/form-data/form-data/pull/20) +- Allow custom headers and pre-known length in parts [`#17`](https://github.com/form-data/form-data/pull/17) +- Bumped version number. [`#12`](https://github.com/form-data/form-data/pull/12) +- Fix for #10 [`#11`](https://github.com/form-data/form-data/pull/11) +- Bumped version number. [`#8`](https://github.com/form-data/form-data/pull/8) +- Added support for https destination, http-response and mikeal's request streams. [`#7`](https://github.com/form-data/form-data/pull/7) +- Updated git url. [`#6`](https://github.com/form-data/form-data/pull/6) +- Version bump. [`#5`](https://github.com/form-data/form-data/pull/5) +- Changes to support custom content-type and getLengthSync. [`#4`](https://github.com/form-data/form-data/pull/4) +- make .submit(url) use host from url, not 'localhost' [`#2`](https://github.com/form-data/form-data/pull/2) +- Make package.json JSON [`#1`](https://github.com/form-data/form-data/pull/1) + +### Fixed + +- Add MIT license [`#14`](https://github.com/form-data/form-data/issues/14) + +### Commits + +- Spring cleaning. [`850ba1b`](https://github.com/form-data/form-data/commit/850ba1b649b6856b0fa87bbcb04bc70ece0137a6) +- Added custom request params to form.submit(). Made tests more stable. [`de3502f`](https://github.com/form-data/form-data/commit/de3502f6c4a509f6ed12a7dd9dc2ce9c2e0a8d23) +- Basic form (no files) working [`6ffdc34`](https://github.com/form-data/form-data/commit/6ffdc343e8594cfc2efe1e27653ea39d8980a14e) +- Got initial test to pass [`9a59d08`](https://github.com/form-data/form-data/commit/9a59d08c024479fd3c9d99ba2f0893a47b3980f0) +- Implement initial getLength [`9060c91`](https://github.com/form-data/form-data/commit/9060c91b861a6573b73beddd11e866db422b5830) +- Make getLength work with file streams [`6f6b1e9`](https://github.com/form-data/form-data/commit/6f6b1e9b65951e6314167db33b446351702f5558) +- Implemented a simplistic submit() function [`41e9cc1`](https://github.com/form-data/form-data/commit/41e9cc124124721e53bc1d1459d45db1410c44e6) +- added test for custom headers and content-length in parts (felixge/node-form-data/17) [`b16d14e`](https://github.com/form-data/form-data/commit/b16d14e693670f5d52babec32cdedd1aa07c1aa4) +- Fixed code styling. [`5847424`](https://github.com/form-data/form-data/commit/5847424c666970fc2060acd619e8a78678888a82) +- #29 Added custom filename and content-type options to support identity-less streams. [`adf8b4a`](https://github.com/form-data/form-data/commit/adf8b4a41530795682cd3e35ffaf26b30288ccda) +- Initial Readme and package.json [`8c744e5`](https://github.com/form-data/form-data/commit/8c744e58be4014bdf432e11b718ed87f03e217af) +- allow append() to completely override header and boundary [`3fb2ad4`](https://github.com/form-data/form-data/commit/3fb2ad491f66e4b4ff16130be25b462820b8c972) +- Syntax highlighting [`ab3a6a5`](https://github.com/form-data/form-data/commit/ab3a6a5ed1ab77a2943ce3befcb2bb3cd9ff0330) +- Updated Readme.md [`de8f441`](https://github.com/form-data/form-data/commit/de8f44122ca754cbfedc0d2748e84add5ff0b669) +- Added examples to Readme file. [`c406ac9`](https://github.com/form-data/form-data/commit/c406ac921d299cbc130464ed19338a9ef97cb650) +- pass options.knownLength to set length at beginning, w/o waiting for async size calculation [`e2ac039`](https://github.com/form-data/form-data/commit/e2ac0397ff7c37c3dca74fa9925b55f832e4fa0b) +- Updated dependencies and added test command. [`09bd7cd`](https://github.com/form-data/form-data/commit/09bd7cd86f1ad7a58df1b135eb6eef0d290894b4) +- Bumped version. Updated readme. [`4581140`](https://github.com/form-data/form-data/commit/4581140f322758c6fc92019d342c7d7d6c94af5c) +- Test runner [`1707ebb`](https://github.com/form-data/form-data/commit/1707ebbd180856e6ed44e80c46b02557e2425762) +- Added .npmignore, bumped version. [`2e033e0`](https://github.com/form-data/form-data/commit/2e033e0e4be7c1457be090cd9b2996f19d8fb665) +- FormData.prototype.append takes and passes along options (for header) [`b519203`](https://github.com/form-data/form-data/commit/b51920387ed4da7b4e106fc07b9459f26b5ae2f0) +- Make package.json JSON [`bf1b58d`](https://github.com/form-data/form-data/commit/bf1b58df794b10fda86ed013eb9237b1e5032085) +- Add dependencies to package.json [`7413d0b`](https://github.com/form-data/form-data/commit/7413d0b4cf5546312d47ea426db8180619083974) +- Add convenient submit() interface [`55855e4`](https://github.com/form-data/form-data/commit/55855e4bea14585d4a3faf9e7318a56696adbc7d) +- Fix content type [`08b6ae3`](https://github.com/form-data/form-data/commit/08b6ae337b23ef1ba457ead72c9b133047df213c) +- Combatting travis rvm calls. [`409adfd`](https://github.com/form-data/form-data/commit/409adfd100a3cf4968a632c05ba58d92d262d144) +- Fixed Issue #2 [`b3a5d66`](https://github.com/form-data/form-data/commit/b3a5d661739dcd6921b444b81d5cb3c32fab655d) +- Fix for #10. [`bab70b9`](https://github.com/form-data/form-data/commit/bab70b9e803e17287632762073d227d6c59989e0) +- Trying workarounds for formidable - 0.6 "love". [`25782a3`](https://github.com/form-data/form-data/commit/25782a3f183d9c30668ec2bca6247ed83f10611c) +- change whitespace to conform with felixge's style guide [`9fa34f4`](https://github.com/form-data/form-data/commit/9fa34f433bece85ef73086a874c6f0164ab7f1f6) +- Add async to deps [`b7d1a6b`](https://github.com/form-data/form-data/commit/b7d1a6b10ee74be831de24ed76843e5a6935f155) +- typo [`7860a9c`](https://github.com/form-data/form-data/commit/7860a9c8a582f0745ce0e4a0549f4bffc29c0b50) +- Bumped version. [`fa36c1b`](https://github.com/form-data/form-data/commit/fa36c1b4229c34b85d7efd41908429b6d1da3bfc) +- Updated .gitignore [`de567bd`](https://github.com/form-data/form-data/commit/de567bde620e53b8e9b0ed3506e79491525ec558) +- Don't rely on resume() being called by pipe [`1deae47`](https://github.com/form-data/form-data/commit/1deae47e042bcd170bd5dbe2b4a4fa5356bb8aa2) +- One more wrong content type [`28f166d`](https://github.com/form-data/form-data/commit/28f166d443e2eb77f2559324014670674b97e46e) +- Another typo [`b959b6a`](https://github.com/form-data/form-data/commit/b959b6a2be061cac17f8d329b89cea109f0f32be) +- Typo [`698fa0a`](https://github.com/form-data/form-data/commit/698fa0aa5dbf4eeb77377415acc202a6fbe3f4a2) +- Being simply dumb. [`b614db8`](https://github.com/form-data/form-data/commit/b614db85702061149fbd98418605106975e72ade) +- Fixed typo in the filename. [`30af6be`](https://github.com/form-data/form-data/commit/30af6be13fb0c9e92b32e935317680b9d7599928)
.npmrc+2 −0 modified@@ -1 +1,3 @@ package-lock=false +allow-same-version=true +message=v%s
package.json+13 −2 modified@@ -30,7 +30,9 @@ "restore-readme": "mv README.md.bak README.md", "prepublish": "not-in-publish || npm run prepublishOnly", "prepublishOnly": "npm run update-readme", - "postpublish": "npm run restore-readme" + "postpublish": "npm run restore-readme", + "version": "auto-changelog && git add CHANGELOG.md", + "postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\"" }, "engines": { "node": ">= 0.12" @@ -43,6 +45,7 @@ "safe-buffer": "^5.2.1" }, "devDependencies": { + "auto-changelog": "^2.5.0", "browserify": "^13.3.0", "browserify-istanbul": "^2.0.0", "coveralls": "^3.1.1", @@ -60,5 +63,13 @@ "rimraf": "^2.7.1", "tape": "^5.9.0" }, - "license": "MIT" + "license": "MIT", + "auto-changelog": { + "output": "CHANGELOG.md", + "template": "keepachangelog", + "unreleased": false, + "commitLimit": false, + "backfillLimit": false, + "hideCredit": true + } }
c97cfbed9eb6[Tests] Switch to newer v8 prediction library; enable node 24 testing
2 files changed · +32 −10
package.json+1 −1 modified@@ -59,11 +59,11 @@ "formidable": "^1.2.6", "in-publish": "^2.0.1", "istanbul": "^0.4.5", + "js-randomness-predictor": "^1.5.5", "obake": "^0.1.2", "phantomjs-prebuilt": "^2.1.16", "pkgfiles": "^2.3.2", "pre-commit": "^1.2.2", - "predict-v8-randomness": "^1.0.35", "puppeteer": "^1.20.0", "request": "~2.87.0", "rimraf": "^2.7.1",
test/integration/test-boundary-prediction.js+31 −9 modified@@ -4,18 +4,40 @@ var common = require('../common'); var assert = common.assert; var FormData = require(common.dir.lib + '/form_data'); var satisfies = require('semver').satisfies; -var predictV8Randomness = satisfies(process.version, '^17 - ^23') && require('predict-v8-randomness'); // eslint-disable-line global-require +var jsrp = satisfies(process.version, '>= 17') && require('js-randomness-predictor'); // eslint-disable-line global-require -var initialSequence = [ - Math.random(), - Math.random(), - Math.random(), - Math.random(), -]; -var predictor = predictV8Randomness && new predictV8Randomness.Predictor(initialSequence); +var predictor = jsrp && jsrp.node(); + +/** + * Predicts the next random outputs from the V8 engine's random number generator. + * + * @param {JsRandomnessPredictor} predictorInstance - The instance of the randomness predictor. + * @param {number} count - The number of random outputs to predict. + * @returns {Promise<number[]>} A promise that resolves with the predicted random outputs or rejects with an error. + */ +function predictMany(predictorInstance, count) { + return new Promise(function (resolve, reject) { + var outputs = []; + + /** Recursive function to predict the next random output */ + function predictOne() { + predictorInstance.predictNext().then(function (nextOutput) { + outputs.push(nextOutput); + + if (outputs.length < count) { + predictOne(); + } else { + resolve(outputs); + } + }).catch(reject); + } + + predictOne(); + }); +} if (predictor) { - predictor.predictNext(24).then(function (next24RandomOutputs) { + predictMany(predictor, 24).then(function (next24RandomOutputs) { var predictedBoundary = next24RandomOutputs .map(function (v) { return Math.floor(v * 10).toString(16);
16e007653421[Tests] Switch to newer v8 prediction library; enable node 24 testing
2 files changed · +32 −10
package.json+1 −1 modified@@ -60,10 +60,10 @@ "in-publish": "^2.0.1", "is-node-modern": "^1.0.0", "istanbul": "^0.4.5", + "js-randomness-predictor": "^1.5.5", "obake": "^0.1.2", "pkgfiles": "^2.3.2", "pre-commit": "^1.2.2", - "predict-v8-randomness": "^1.0.35", "puppeteer": "^1.20.0", "request": "~2.87.0", "rimraf": "^2.7.1",
test/integration/test-boundary-prediction.js+31 −9 modified@@ -2,18 +2,40 @@ var common = require('../common'); var assert = common.assert; var FormData = require(common.dir.lib + '/form_data'); var satisfies = require('semver').satisfies; -var predictV8Randomness = satisfies(process.version, '^17 - ^23') && require('predict-v8-randomness'); // eslint-disable-line global-require +var jsrp = satisfies(process.version, '>= 17') && require('js-randomness-predictor'); // eslint-disable-line global-require -var initialSequence = [ - Math.random(), - Math.random(), - Math.random(), - Math.random() -]; -var predictor = predictV8Randomness && new predictV8Randomness.Predictor(initialSequence); +var predictor = jsrp && jsrp.node(); + +/** + * Predicts the next random outputs from the V8 engine's random number generator. + * + * @param {JsRandomnessPredictor} predictorInstance - The instance of the randomness predictor. + * @param {number} count - The number of random outputs to predict. + * @returns {Promise<number[]>} A promise that resolves with the predicted random outputs or rejects with an error. + */ +function predictMany(predictorInstance, count) { + return new Promise(function (resolve, reject) { + var outputs = []; + + /** Recursive function to predict the next random output */ + function predictOne() { + predictorInstance.predictNext().then(function (nextOutput) { + outputs.push(nextOutput); + + if (outputs.length < count) { + predictOne(); + } else { + resolve(outputs); + } + }).catch(reject); + } + + predictOne(); + }); +} if (predictor) { - predictor.predictNext(24).then(function (next24RandomOutputs) { + predictMany(predictor, 24).then(function (next24RandomOutputs) { var predictedBoundary = next24RandomOutputs .map(function (v) { return Math.floor(v * 10).toString(16);
0e9312235841[Tests] handle predict-v8-randomness failures in node < 17 and node > 23
2 files changed · +33 −29
package.json+1 −0 modified@@ -67,6 +67,7 @@ "puppeteer": "^1.20.0", "request": "~2.87.0", "rimraf": "^2.7.1", + "semver": "^6.3.1", "tape": "^5.9.0" }, "license": "MIT",
test/integration/test-boundary-prediction.js+32 −29 modified@@ -3,50 +3,53 @@ var common = require('../common'); var assert = common.assert; var FormData = require(common.dir.lib + '/form_data'); -var predictV8Randomness = require('predict-v8-randomness'); +var satisfies = require('semver').satisfies; +var predictV8Randomness = satisfies(process.version, '^17 - ^23') && require('predict-v8-randomness'); // eslint-disable-line global-require var initialSequence = [ Math.random(), Math.random(), Math.random(), Math.random(), ]; -var predictor = new predictV8Randomness.Predictor(initialSequence); +var predictor = predictV8Randomness && new predictV8Randomness.Predictor(initialSequence); -predictor.predictNext(24).then(function (next24RandomOutputs) { - var predictedBoundary = next24RandomOutputs - .map(function (v) { - return Math.floor(v * 10).toString(16); - }) - .join(''); +if (predictor) { + predictor.predictNext(24).then(function (next24RandomOutputs) { + var predictedBoundary = next24RandomOutputs + .map(function (v) { + return Math.floor(v * 10).toString(16); + }) + .join(''); - var boundaryIntro = '----------------------------'; + var boundaryIntro = '----------------------------'; - var payload = 'zzz\r\n' + boundaryIntro + predictedBoundary + '\r\nContent-Disposition: form-data; name="is_admin"\r\n\r\ntrue\r\n' + boundaryIntro + predictedBoundary + '--\r\n'; + var payload = 'zzz\r\n' + boundaryIntro + predictedBoundary + '\r\nContent-Disposition: form-data; name="is_admin"\r\n\r\ntrue\r\n' + boundaryIntro + predictedBoundary + '--\r\n'; - var FIELDS = { - my_field: { - value: payload, - }, - }; + var FIELDS = { + my_field: { + value: payload, + }, + }; - // count total - var fieldsPassed = Object.keys(FIELDS).length; + // count total + var fieldsPassed = Object.keys(FIELDS).length; - // prepare form-receiving http server - var server = common.testFields(FIELDS, function (fields) { - fieldsPassed = fields; - }); + // prepare form-receiving http server + var server = common.testFields(FIELDS, function (fields) { + fieldsPassed = fields; + }); - server.listen(common.port, function () { - var form = new FormData(); + server.listen(common.port, function () { + var form = new FormData(); - common.actions.populateFields(form, FIELDS); + common.actions.populateFields(form, FIELDS); - common.actions.submit(form, server); - }); + common.actions.submit(form, server); + }); - process.on('exit', function () { - assert.strictEqual(fieldsPassed, 0); + process.on('exit', function () { + assert.strictEqual(fieldsPassed, 0); + }); }); -}); +}
be99d4eea5ce[meta] remove local commit hooks
1 file changed · +0 −6
package.json+0 −6 modified@@ -32,11 +32,6 @@ "prepublishOnly": "npm run update-readme", "postpublish": "npm run restore-readme" }, - "pre-commit": [ - "lint", - "ci-test", - "check" - ], "engines": { "node": ">= 0.12" }, @@ -61,7 +56,6 @@ "obake": "^0.1.2", "phantomjs-prebuilt": "^2.1.16", "pkgfiles": "^2.3.2", - "pre-commit": "^1.2.2", "request": "~2.87.0", "rimraf": "^2.7.1", "tape": "^5.9.0"
ddbc89b6d6d6[Dev Deps] remove unused deps
1 file changed · +1 −5
package.json+1 −5 modified@@ -48,9 +48,6 @@ "safe-buffer": "^5.2.1" }, "devDependencies": { - "@types/combined-stream": "^1.0.6", - "@types/mime-types": "^2.1.4", - "@types/node": "^12.20.55", "browserify": "^13.3.0", "browserify-istanbul": "^2.0.0", "coveralls": "^3.1.1", @@ -68,8 +65,7 @@ "pre-commit": "^1.2.2", "request": "~2.87.0", "rimraf": "^2.7.1", - "tape": "^5.9.0", - "typescript": "^3.9.10" + "tape": "^5.9.0" }, "license": "MIT" }
131ae5efa30b[Fix] validate boundary type in `setBoundary()` method
2 files changed · +44 −0
lib/form_data.js+7 −0 modified@@ -315,6 +315,13 @@ FormData.prototype.getHeaders = function (userHeaders) { return formHeaders; }; +FormData.prototype.setBoundary = function (boundary) { + if (typeof boundary !== 'string') { + throw new TypeError('FormData boundary must be a string'); + } + this._boundary = boundary; +}; + FormData.prototype.getBoundary = function () { if (!this._boundary) { this._generateBoundary();
test/integration/test-set-boundary.js+37 −0 added@@ -0,0 +1,37 @@ +'use strict'; + +var common = require('../common'); +var assert = common.assert; + +var FormData = require(common.dir.lib + '/form_data'); + +(function testSetBoundary() { + var userBoundary = '---something'; + var form = new FormData(); + form.setBoundary(userBoundary); + + assert.equal(form.getBoundary(), userBoundary); +}()); + +(function testUniqueBoundaryPerFormAfterSet() { + var userBoundary = '---something'; + var formA = new FormData(); + formA.setBoundary(userBoundary); + + var formB = new FormData(); + + assert.equal(formA.getBoundary(), userBoundary); + assert.notEqual(formA.getBoundary(), formB.getBoundary()); +}()); + +(function testSetBoundaryWithNonString() { + var form = new FormData(); + + var invalidValues = [123, null, undefined, { value: 123 }, ['---something']]; + + invalidValues.forEach(function (value) { + assert.throws(function () { + form.setBoundary(value); + }, TypeError); + }); +}());
b88316c94bb0[Fix] Switch to using `crypto` random for boundary values
3 files changed · +59 −9
lib/form_data.js+4 −9 modified@@ -7,6 +7,7 @@ var http = require('http'); var https = require('https'); var parseUrl = require('url').parse; var fs = require('fs'); +var crypto = require('crypto'); var mime = require('mime-types'); var asynckit = require('asynckit'); var hasOwn = require('hasown'); @@ -357,16 +358,10 @@ FormData.prototype.getBuffer = function () { }; FormData.prototype._generateBoundary = function () { - /* - * This generates a 50 character boundary similar to those used by Firefox. - * They are optimized for boyer-moore parsing. - */ - var boundary = '--------------------------'; - for (var i = 0; i < 24; i++) { - boundary += Math.floor(Math.random() * 10).toString(16); - } + // This generates a 50 character boundary similar to those used by Firefox. - this._boundary = boundary; + // They are optimized for boyer-moore parsing. + this._boundary = '--------------------------' + crypto.randomBytes(12).toString('hex'); }; /*
package.json+3 −0 modified@@ -62,6 +62,9 @@ "obake": "^0.1.2", "phantomjs-prebuilt": "^2.1.16", "pkgfiles": "^2.3.2", + "pre-commit": "^1.2.2", + "predict-v8-randomness": "^1.0.35", + "puppeteer": "^1.20.0", "request": "~2.87.0", "rimraf": "^2.7.1", "tape": "^5.9.0"
test/integration/test-boundary-prediction.js+52 −0 added@@ -0,0 +1,52 @@ +'use strict'; + +var common = require('../common'); +var assert = common.assert; +var FormData = require(common.dir.lib + '/form_data'); +var predictV8Randomness = require('predict-v8-randomness'); + +var initialSequence = [ + Math.random(), + Math.random(), + Math.random(), + Math.random(), +]; +var predictor = new predictV8Randomness.Predictor(initialSequence); + +predictor.predictNext(24).then(function (next24RandomOutputs) { + var predictedBoundary = next24RandomOutputs + .map(function (v) { + return Math.floor(v * 10).toString(16); + }) + .join(''); + + var boundaryIntro = '----------------------------'; + + var payload = 'zzz\r\n' + boundaryIntro + predictedBoundary + '\r\nContent-Disposition: form-data; name="is_admin"\r\n\r\ntrue\r\n' + boundaryIntro + predictedBoundary + '--\r\n'; + + var FIELDS = { + my_field: { + value: payload, + }, + }; + + // count total + var fieldsPassed = Object.keys(FIELDS).length; + + // prepare form-receiving http server + var server = common.testFields(FIELDS, function (fields) { + fieldsPassed = fields; + }); + + server.listen(common.port, function () { + var form = new FormData(); + + common.actions.populateFields(form, FIELDS); + + common.actions.submit(form, server); + }); + + process.on('exit', function () { + assert.strictEqual(fieldsPassed, 0); + }); +});
e351a97e9f6c[meta] fix scripts to use prepublishOnly
1 file changed · +2 −1
package.json+2 −1 modified@@ -28,7 +28,8 @@ "get-version": "node -e \"console.log(require('./package.json').version)\"", "update-readme": "sed -i.bak 's/\\/master\\.svg/\\/v'$(npm --silent run get-version)'.svg/g' README.md", "restore-readme": "mv README.md.bak README.md", - "prepublish": "in-publish && npm run update-readme || not-in-publish", + "prepublish": "not-in-publish || npm run prepublishOnly", + "prepublishOnly": "npm run update-readme", "postpublish": "npm run restore-readme" }, "pre-commit": [
440d3bed752afix: move util.isArray to Array.isArray
1 file changed · +1 −1
lib/form_data.js+1 −1 modified@@ -61,7 +61,7 @@ FormData.prototype.append = function(field, value, options) { } // https://github.com/felixge/node-form-data/issues/38 - if (util.isArray(value)) { + if (Array.isArray(value)) { // Please convert your array into string // the way web server expects it this._error(new Error('Arrays are not supported.'));
335ad19c6e17fix (npmignore): ignore temporary build files
1 file changed · +3 −0
.npmignore+3 −0 modified@@ -7,3 +7,6 @@ sftp-config.json coverage/ node_modules/ test/ + +yarn.lock +README.md.bak
1 file changed · +2 −2
Readme.md+2 −2 modified@@ -218,7 +218,7 @@ form.append( 'my_file', fs.createReadStream('/foo/bar.jpg'), {filename: 'bar.jpg ``` #### _Headers_ getHeaders( [**Headers** _userHeaders_] ) -This method adds the correct `content-type` header to the provided array of `userHeaders`. +This method adds the correct `content-type` header to the provided array of `userHeaders`. #### _String_ getBoundary() Return the boundary of the formData. By default, the boundary consists of 26 `-` followed by 24 numbers @@ -349,7 +349,7 @@ axios.post('http://example.com', form, { - ```getLengthSync()``` method DOESN'T calculate length for streams, use ```knownLength``` options as workaround. - ```getLength(cb)``` will send an error as first parameter of callback if stream length cannot be calculated (e.g. send in custom streams w/o using ```knownLength```). -- ```sbumit``` will not add `content-length` if form length is unknown or not calculable. +- ```submit``` will not add `content-length` if form length is unknown or not calculable. - Starting version `2.x` FormData has dropped support for `node@0.10.x`. - Starting version `3.x` FormData has dropped support for `node@4.x`.
55d90ce4a4c2feat: add setBoundary method
4 files changed · +34 −1
index.d.ts+1 −0 modified@@ -36,6 +36,7 @@ declare class FormData extends stream.Readable { callback?: (error: Error | null, response: http.IncomingMessage) => void ): http.ClientRequest; getBuffer(): Buffer; + setBoundary(boundary: string): void; getBoundary(): string; getLength(callback: (err: Error | null, length: number) => void): void; getLengthSync(): number;
lib/form_data.js+4 −0 modified@@ -305,6 +305,10 @@ FormData.prototype.getHeaders = function(userHeaders) { return formHeaders; }; +FormData.prototype.setBoundary = function(boundary) { + this._boundary = boundary; +}; + FormData.prototype.getBoundary = function() { if (!this._boundary) { this._generateBoundary();
Readme.md+6 −1 modified@@ -189,6 +189,7 @@ form.submit({ - [_Void_ append( **String** _field_, **Mixed** _value_ [, **Mixed** _options_] )](https://github.com/form-data/form-data#void-append-string-field-mixed-value--mixed-options-). - [_Headers_ getHeaders( [**Headers** _userHeaders_] )](https://github.com/form-data/form-data#array-getheaders-array-userheaders-) - [_String_ getBoundary()](https://github.com/form-data/form-data#string-getboundary) +- [_Void_ setBoundary()](https://github.com/form-data/form-data#void-setboundary) - [_Buffer_ getBuffer()](https://github.com/form-data/form-data#buffer-getbuffer) - [_Integer_ getLengthSync()](https://github.com/form-data/form-data#integer-getlengthsync) - [_Integer_ getLength( **function** _callback_ )](https://github.com/form-data/form-data#integer-getlength-function-callback-) @@ -220,11 +221,15 @@ form.append( 'my_file', fs.createReadStream('/foo/bar.jpg'), {filename: 'bar.jpg This method adds the correct `content-type` header to the provided array of `userHeaders`. #### _String_ getBoundary() -Return the boundary of the formData. A boundary consists of 26 `-` followed by 24 numbers +Return the boundary of the formData. By default, the boundary consists of 26 `-` followed by 24 numbers for example: ```javascript --------------------------515890814546601021194782 ``` + +#### _Void_ setBoundary(String _boundary_) +Set the boundary string, overriding the default behavior described above. + _Note: The boundary must be unique and may not appear in the data._ #### _Buffer_ getBuffer()
test/integration/test-set-boundary.js+23 −0 added@@ -0,0 +1,23 @@ +var common = require('../common'); +var assert = common.assert; + +var FormData = require(common.dir.lib + '/form_data'); + +(function testSetBoundary() { + var userBoundary = '---something'; + var form = new FormData(); + form.setBoundary(userBoundary); + + assert.equal(form.getBoundary(), userBoundary); +})(); + +(function testUniqueBoundaryPerFormAfterSet() { + var userBoundary = '---something'; + var formA = new FormData(); + formA.setBoundary(userBoundary); + + var formB = new FormData(); + + assert.equal(formA.getBoundary(), userBoundary); + assert.notEqual(formA.getBoundary(), formB.getBoundary()); +})();
4bde68e12de1Pass options to constructor if not used with new
1 file changed · +1 −1
lib/form_data.js+1 −1 modified@@ -25,7 +25,7 @@ util.inherits(FormData, CombinedStream); */ function FormData(options) { if (!(this instanceof FormData)) { - return new FormData(); + return new FormData(options); } this._overheadLength = 0;
2b4e47870314Make userHeaders optional
1 file changed · +1 −1
index.d.ts+1 −1 modified@@ -20,7 +20,7 @@ interface Options { declare class FormData extends stream.Readable { constructor(options?: Options); append(key: string, value: any, options?: FormData.AppendOptions | string): void; - getHeaders(userHeaders: FormData.Headers): FormData.Headers; + getHeaders(userHeaders?: FormData.Headers): FormData.Headers; submit( params: string | FormData.SubmitOptions, callback?: (error: Error | null, response: http.IncomingMessage) => void
6dd8624b2999Update README for custom stream behavior
1 file changed · +2 −0
Readme.md+2 −0 modified@@ -227,6 +227,8 @@ fetch('http://example.com', { method: 'POST', body: form }) ## Notes - ```getLengthSync()``` method DOESN'T calculate length for streams, use ```knownLength``` options as workaround. +- ```getLength(cb)``` will send an error as first parameter of callback if stream length cannot be calculated (e.g. send in custom streams w/o using ```knownLength```). +- ```sbumit``` will not add `content-length` if form length is unknown or not calculable. - Starting version `2.x` FormData has dropped support for `node@0.10.x`. ## License
Vulnerability mechanics
Root cause
"Missing escaping of CR, LF, and double-quote characters in field names and filenames when building the Content-Disposition header allows CRLF injection."
Attack vector
An attacker supplies a field name or filename containing CR (`\r`), LF (`\n`), or double-quote (`"`) characters to `FormData#append`. Because the library concatenates these values directly into the `Content-Disposition` header without escaping, the attacker can terminate the header line and inject arbitrary headers or smuggle entire additional multipart parts into the request body [CWE-93][ref_id=3]. This allows overriding or adding form fields (e.g. `is_admin=true`) that a downstream parser accepts as legitimate. Exploitation requires the consuming application to pass untrusted input as a field name or filename; applications using only fixed/trusted names are not affected [ref_id=3].
Affected code
The vulnerability is in `lib/form_data.js` in the `_multiPartHeader` and `_getContentDisposition` methods. The `field` argument to `FormData#append` and the `filename` option were concatenated verbatim into the `Content-Disposition` header without escaping CR, LF, or double-quote characters [patch_id=5727020][patch_id=5727014][patch_id=5727040].
What the fix does
The fix introduces an `escapeHeaderParam` function in `lib/form_data.js` that replaces `\r`, `\n`, and `"` with `%0D`, `%0A`, and `%22` respectively [patch_id=5727020][patch_id=5727014][patch_id=5727040]. This function is called when constructing the `name="..."` parameter in `_multiPartHeader` and the `filename="..."` parameter in `_getContentDisposition`. The escaping matches the WHATWG HTML multipart/form-data encoding algorithm that browsers use, neutralizing the injection while leaving ordinary field names unchanged. A new integration test file (`test-header-injection.js`) verifies that CRLF in field names or filenames no longer produces injected headers or smuggled parts.
Preconditions
- inputThe consuming application must pass attacker-controlled (untrusted) data as the field name argument to FormData#append or as the filename option.
- configThe application must forward the generated multipart request body to a backend parser that trusts the injected fields.
Generated on Jun 12, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- cwe.mitre.org/data/definitions/93.htmlnvd
- github.com/form-data/form-data/commit/64190db548c0179e37206858e39f27cf513e9435nvd
- github.com/form-data/form-data/commit/be3f3cf553978bac15a5182f1f3c3d2d38ccf229nvd
- github.com/form-data/form-data/commit/c7133499c2ee1b80c678e411244f4442bf902045nvd
- github.com/form-data/form-data/security/advisories/GHSA-hmw2-7cc7-3qxxnvd
- html.spec.whatwg.org/multipage/form-control-infrastructure.htmlnvd
- www.npmjs.com/package/form-datanvd
News mentions
0No linked articles in our index yet.