Skip to content

Commit 72df034

Browse files
committed
Merge origin/master into fix/content-encoding-crash
2 parents df34e3e + cf9abfe commit 72df034

3 files changed

Lines changed: 110 additions & 3 deletions

File tree

src/Formidable.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,8 @@ class IncomingForm extends EventEmitter {
208208
}
209209
}
210210
const callback = once(dezalgo(cb));
211-
this.fields = {};
212-
const files = {};
211+
this.fields = Object.create(null);
212+
const files = Object.create(null);
213213

214214
this.on('field', (name, value) => {
215215
if (this.type === 'multipart' || this.type === 'urlencoded') {

src/plugins/json.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ function init(_self, _opts) {
2727
const parser = new JSONParser(this.options);
2828

2929
parser.on('data', (fields) => {
30-
this.fields = fields;
30+
this.fields = Object.assign(Object.create(null), fields);
3131
});
3232

3333
parser.once('end', () => {
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { ok, strictEqual } from 'node:assert';
2+
import { createServer } from 'node:http';
3+
import test from 'node:test';
4+
import formidable, { errors } from '../../src/index.js';
5+
6+
7+
8+
let server;
9+
let port = 13000;
10+
11+
test.beforeEach(() => {
12+
// Increment port to avoid conflicts between tests
13+
port += 1;
14+
server = createServer();
15+
});
16+
17+
test('prototype contamination', async (t) => {
18+
server.on('request', async (req, res) => {
19+
const form = formidable();
20+
21+
const [fields, files] = await form.parse(req);
22+
23+
res.writeHead(200);
24+
res.end("ok");
25+
26+
let a;
27+
try {
28+
a = typeof String(fields);
29+
} catch {
30+
;
31+
}
32+
strictEqual(a, undefined, "the toString method should not be used directly");
33+
34+
});
35+
36+
await new Promise(resolve => server.listen(port, resolve));
37+
38+
const body = `{"toString":"x","hasOwnProperty":"x","a":5}`;
39+
40+
const resClient = await fetch(String(new URL(`http:localhost:${port}/`)), {
41+
method: 'POST',
42+
headers: {
43+
'Content-Length': body.length,
44+
Host: `localhost:${port}`,
45+
'Content-Type': 'text/json;',
46+
},
47+
body
48+
});
49+
50+
strictEqual(resClient.status, 200);
51+
52+
const text = await resClient.text();
53+
54+
ok(text);
55+
});
56+
57+
test('should not use unsafe methods on user provided objects', async (t) => {
58+
server.on('request', async (req, res) => {
59+
const form = formidable();
60+
61+
const [fields, files] = await form.parse(req);
62+
63+
res.writeHead(200);
64+
res.end("ok");
65+
66+
let a;
67+
try {
68+
a = typeof String(fields);
69+
} catch {
70+
;
71+
}
72+
strictEqual(a, undefined, "the toString method should not be used directly");
73+
74+
});
75+
76+
await new Promise(resolve => server.listen(port, resolve));
77+
78+
const body = `{"a":"x","b":"x","z":5}`;
79+
80+
const resClient = await fetch(String(new URL(`http:localhost:${port}/`)), {
81+
method: 'POST',
82+
headers: {
83+
'Content-Length': body.length,
84+
Host: `localhost:${port}`,
85+
'Content-Type': 'text/json;',
86+
},
87+
body
88+
});
89+
90+
strictEqual(resClient.status, 200);
91+
92+
const text = await resClient.text();
93+
94+
ok(text);
95+
});
96+
97+
98+
99+
test.afterEach(async () => {
100+
await new Promise((resolve) => {
101+
if (server.listening) {
102+
server.close(() => resolve());
103+
} else {
104+
resolve();
105+
}
106+
});
107+
});

0 commit comments

Comments
 (0)