Skip to content

Commit 582d1ed

Browse files
authored
test(stringifyFormData): add tests
1 parent 0f2dc5b commit 582d1ed

1 file changed

Lines changed: 158 additions & 0 deletions

File tree

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import { describe, expect, it } from 'vitest';
2+
3+
import { stringifyFormData } from '../../../src/handlers/fix-request-body-utils/stringify-form-data.js';
4+
5+
describe('stringifyFormData', () => {
6+
describe('boundary parsing', () => {
7+
it('should serialize using quoted boundary', () => {
8+
const result = stringifyFormData('multipart/form-data; boundary="BB"', { user: 'alice' });
9+
10+
expect(result).toBe('--BB\r\nContent-Disposition: form-data; name="user"\r\n\r\nalice\r\n');
11+
});
12+
13+
it('should serialize using unquoted boundary', () => {
14+
const result = stringifyFormData('multipart/form-data; boundary=BB', { user: 'alice' });
15+
16+
expect(result).toBe('--BB\r\nContent-Disposition: form-data; name="user"\r\n\r\nalice\r\n');
17+
});
18+
19+
it('should serialize using case-insensitive boundary parameter', () => {
20+
const result = stringifyFormData('multipart/form-data; BOUNDARY=BB', { user: 'alice' });
21+
22+
expect(result).toBe('--BB\r\nContent-Disposition: form-data; name="user"\r\n\r\nalice\r\n');
23+
});
24+
25+
it('should lock legacy fallback when boundary parameter is missing', () => {
26+
const contentType = 'multipart/form-data; charset=utf-8';
27+
28+
const result = stringifyFormData(contentType, { user: 'alice' });
29+
30+
expect(result).toBe(
31+
'--multipart/form-data; charset=utf-8\r\nContent-Disposition: form-data; name="user"\r\n\r\nalice\r\n',
32+
);
33+
});
34+
});
35+
36+
describe('field serialization', () => {
37+
it('should escape field names and coerce values with String()', () => {
38+
// Input key includes a backslash + quote sequence: field\"name
39+
// Output escapes backslash first, then quote in Content-Disposition name.
40+
const result = stringifyFormData('multipart/form-data; boundary=BB', {
41+
'field\\"name': 42,
42+
});
43+
44+
expect(result).toBe(
45+
'--BB\r\nContent-Disposition: form-data; name="field\\\\\\"name"\r\n\r\n42\r\n',
46+
);
47+
});
48+
49+
it('should coerce null and undefined values with String()', () => {
50+
const result = stringifyFormData('multipart/form-data; boundary=BB', {
51+
nullValue: null,
52+
undefinedValue: undefined,
53+
});
54+
55+
expect(result).toContain('name="nullValue"\r\n\r\nnull\r\n');
56+
expect(result).toContain('name="undefinedValue"\r\n\r\nundefined\r\n');
57+
});
58+
59+
it('should serialize multiple fields in insertion order', () => {
60+
const result = stringifyFormData('multipart/form-data; boundary=BB', {
61+
first: '1',
62+
second: true,
63+
});
64+
65+
expect(result).toBe(
66+
'--BB\r\nContent-Disposition: form-data; name="first"\r\n\r\n1\r\n' +
67+
'--BB\r\nContent-Disposition: form-data; name="second"\r\n\r\ntrue\r\n',
68+
);
69+
});
70+
});
71+
72+
describe('security validation', () => {
73+
// RFC 9112 obsolete line folding guidance: reject CR/LF in multipart boundary and fields.
74+
it('should reject unsafe multipart boundary containing CRLF', () => {
75+
expect(() =>
76+
stringifyFormData('multipart/form-data; boundary="BB\r\nX-Injection: 1"', {
77+
user: 'alice',
78+
}),
79+
).toThrow(
80+
'[HPM] unsafe multipart boundary detected. Request rejected per RFC 9112 obsolete line folding guidance.',
81+
);
82+
});
83+
84+
it('should reject unsafe multipart boundary containing CR only', () => {
85+
expect(() =>
86+
stringifyFormData('multipart/form-data; boundary="BB\rX"', { user: 'alice' }),
87+
).toThrow(
88+
'[HPM] unsafe multipart boundary detected. Request rejected per RFC 9112 obsolete line folding guidance.',
89+
);
90+
});
91+
92+
it('should reject unsafe multipart boundary containing LF only', () => {
93+
expect(() =>
94+
stringifyFormData('multipart/form-data; boundary="BB\nX"', { user: 'alice' }),
95+
).toThrow(
96+
'[HPM] unsafe multipart boundary detected. Request rejected per RFC 9112 obsolete line folding guidance.',
97+
);
98+
});
99+
100+
it('should reject unsafe multipart boundary when empty after trimming', () => {
101+
expect(() =>
102+
stringifyFormData('multipart/form-data; boundary= ', { user: 'alice' }),
103+
).toThrow(
104+
'[HPM] unsafe multipart boundary detected. Request rejected per RFC 9112 obsolete line folding guidance.',
105+
);
106+
});
107+
108+
it('should reject unsafe multipart field names containing LF', () => {
109+
expect(() =>
110+
stringifyFormData('multipart/form-data; boundary=BB', {
111+
'bad\nname': 'alice',
112+
}),
113+
).toThrow(
114+
'[HPM] unsafe multipart field name "bad\nname" detected. Request rejected per RFC 9112 obsolete line folding guidance.',
115+
);
116+
});
117+
118+
it('should reject unsafe multipart field names containing CR', () => {
119+
expect(() =>
120+
stringifyFormData('multipart/form-data; boundary=BB', {
121+
'bad\rname': 'alice',
122+
}),
123+
).toThrow(
124+
'[HPM] unsafe multipart field name "bad\rname" detected. Request rejected per RFC 9112 obsolete line folding guidance.',
125+
);
126+
});
127+
128+
it('should reject unsafe multipart field values containing CRLF', () => {
129+
expect(() =>
130+
stringifyFormData('multipart/form-data; boundary=BB', {
131+
user: 'alice\r\nadmin',
132+
}),
133+
).toThrow(
134+
'[HPM] unsafe multipart field value for "user" detected. Request rejected per RFC 9112 obsolete line folding guidance.',
135+
);
136+
});
137+
138+
it('should reject unsafe multipart field values containing CR', () => {
139+
expect(() =>
140+
stringifyFormData('multipart/form-data; boundary=BB', {
141+
user: 'alice\radmin',
142+
}),
143+
).toThrow(
144+
'[HPM] unsafe multipart field value for "user" detected. Request rejected per RFC 9112 obsolete line folding guidance.',
145+
);
146+
});
147+
148+
it('should reject unsafe multipart field values containing boundary delimiter', () => {
149+
expect(() =>
150+
stringifyFormData('multipart/form-data; boundary=BB', {
151+
user: 'prefix --BB suffix',
152+
}),
153+
).toThrow(
154+
'[HPM] unsafe multipart field value for "user" detected. Request rejected per RFC 9112 obsolete line folding guidance.',
155+
);
156+
});
157+
});
158+
});

0 commit comments

Comments
 (0)