Imported Upstream version 2.5.11
[libapache-mod-security.git] / apache2 / t / regression / misc / 00-multipart-parser.t
1 ### Multipart parser tests
2
3 # Final CRLF or not, we should still work
4 {
5     type => "misc",
6     comment => "multipart parser (final CRLF)",
7     conf => qq(
8         SecRuleEngine On
9         SecDebugLog $ENV{DEBUG_LOG}
10         SecDebugLogLevel 9
11         SecRequestBodyAccess On
12         SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny"
13         SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny"
14         SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny"
15     ),
16     match_log => {
17         debug => [ qr/Adding request argument \(BODY\): name "a", value "1".*Adding request argument \(BODY\): name "b", value "2"/s, 1 ],
18         -debug => [ qr/Multipart error:/, 1 ],
19
20     },
21     match_response => {
22         status => qr/^200$/,
23     },
24     request => new HTTP::Request(
25         POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
26         [
27             "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646",
28         ],
29         normalize_raw_request_data(
30             q(
31                 -----------------------------69343412719991675451336310646
32                 Content-Disposition: form-data; name="a"
33                 
34                 1
35                 -----------------------------69343412719991675451336310646
36                 Content-Disposition: form-data; name="b"
37                 
38                 2
39                 -----------------------------69343412719991675451336310646--
40             ),
41         ),
42     ),
43 },
44 {
45     type => "misc",
46     comment => "multipart parser (no final CRLF)",
47     conf => qq(
48         SecRuleEngine On
49         SecDebugLog $ENV{DEBUG_LOG}
50         SecDebugLogLevel 9
51         SecRequestBodyAccess On
52         SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny"
53         SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny"
54         SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny"
55     ),
56     match_log => {
57         debug => [ qr/Adding request argument \(BODY\): name "a", value "1".*Adding request argument \(BODY\): name "b", value "2"/s, 1 ],
58         -debug => [ qr/Multipart error:/, 1 ],
59
60     },
61     match_response => {
62         status => qr/^200$/,
63     },
64     request => new HTTP::Request(
65         POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
66         [
67             "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646",
68         ],
69         normalize_raw_request_data(
70             q(
71                 -----------------------------69343412719991675451336310646
72                 Content-Disposition: form-data; name="a"
73                 
74                 1
75                 -----------------------------69343412719991675451336310646
76                 Content-Disposition: form-data; name="b"
77                 
78                 2
79                 -----------------------------69343412719991675451336310646--),
80         ),
81     ),
82 },
83
84 # Should work with a boundary of "boundary"
85 {
86     type => "misc",
87     comment => "multipart parser (boundary contains \"boundary\")",
88     conf => qq(
89         SecRuleEngine On
90         SecDebugLog $ENV{DEBUG_LOG}
91         SecDebugLogLevel 9
92         SecRequestBodyAccess On
93         SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny"
94         SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny"
95         SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny"
96     ),
97     match_log => {
98         debug => [ qr/Adding request argument \(BODY\): name "a", value "1".*Adding request argument \(BODY\): name "b", value "2"/s, 1 ],
99         -debug => [ qr/Multipart error:/, 1 ],
100
101     },
102     match_response => {
103         status => qr/^200$/,
104     },
105     request => new HTTP::Request(
106         POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
107         [
108             "Content-Type" => "multipart/form-data; boundary=------------------------------------------------boundary",
109         ],
110         normalize_raw_request_data(
111             q(
112                 --------------------------------------------------boundary
113                 Content-Disposition: form-data; name="a"
114                 
115                 1
116                 --------------------------------------------------boundary
117                 Content-Disposition: form-data; name="b"
118                 
119                 2
120                 --------------------------------------------------boundary--
121             ),
122         ),
123     ),
124 },
125 {
126     type => "misc",
127     comment => "multipart parser (boundary contains \"bOuNdArY\")",
128     note => q(
129         KHTML Boundary
130     ),
131     conf => qq(
132         SecRuleEngine On
133         SecDebugLog $ENV{DEBUG_LOG}
134         SecDebugLogLevel 9
135         SecRequestBodyAccess On
136         SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny"
137         SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny"
138         SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny"
139     ),
140     match_log => {
141         debug => [ qr/Adding request argument \(BODY\): name "a", value "1".*Adding request argument \(BODY\): name "b", value "2"/s, 1 ],
142         -debug => [ qr/Multipart error:/, 1 ],
143
144     },
145     match_response => {
146         status => qr/^200$/,
147     },
148     request => new HTTP::Request(
149         POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
150         [
151             "Content-Type" => "multipart/form-data; boundary=--------0xKhTmLbOuNdArY",
152         ],
153         normalize_raw_request_data(
154             q(
155                 ----------0xKhTmLbOuNdArY
156                 Content-Disposition: form-data; name="a"
157                 
158                 1
159                 ----------0xKhTmLbOuNdArY
160                 Content-Disposition: form-data; name="b"
161                 
162                 2
163                 ----------0xKhTmLbOuNdArY--
164             ),
165         ),
166     ),
167 },
168
169 # We should handle data starting with a "--"
170 {
171     type => "misc",
172     comment => "multipart parser (data contains \"--\")",
173     conf => qq(
174         SecRuleEngine On
175         SecDebugLog $ENV{DEBUG_LOG}
176         SecDebugLogLevel 9
177         SecRequestBodyAccess On
178         SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny"
179         SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny"
180     ),
181     match_log => {
182         debug => [ qr/Adding request argument \(BODY\): name "a", value "--test".*Adding request argument \(BODY\): name "b", value "--"/s, 1 ],
183         -debug => [ qr/Multipart error:/, 1 ],
184
185     },
186     match_response => {
187         status => qr/^200$/,
188     },
189     request => new HTTP::Request(
190         POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
191         [
192             "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646",
193         ],
194         normalize_raw_request_data(
195             q(
196                 -----------------------------69343412719991675451336310646
197                 Content-Disposition: form-data; name="a"
198                 
199                 --test
200                 -----------------------------69343412719991675451336310646
201                 Content-Disposition: form-data; name="b"
202                 
203                 --
204                 -----------------------------69343412719991675451336310646--),
205         ),
206     ),
207 },
208
209 # We should emit warnings for parsing errors
210 {
211     type => "misc",
212     comment => "multipart parser error (no final boundary)",
213     conf => qq(
214         SecRuleEngine On
215         SecDebugLog $ENV{DEBUG_LOG}
216         SecDebugLogLevel 9
217         SecRequestBodyAccess On
218         SecAuditLog "$ENV{AUDIT_LOG}"
219         SecAuditEngine RelevantOnly
220     ),
221     match_log => {
222         audit => [ qr/Final boundary missing/, 1 ],
223         debug => [ qr/Final boundary missing/, 1 ],
224
225     },
226     match_response => {
227         status => qr/^200$/,
228     },
229     request => new HTTP::Request(
230         POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
231         [
232             "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646",
233         ],
234         normalize_raw_request_data(
235             q(
236                 -----------------------------69343412719991675451336310646
237                 Content-Disposition: form-data; name="a"
238                 
239                 1
240                 -----------------------------69343412719991675451336310646
241                 Content-Disposition: form-data; name="b"
242                 
243                 2
244             ),
245         ),
246     ),
247 },
248 {
249     type => "misc",
250     comment => "multipart parser error (no disposition)",
251     conf => qq(
252         SecRuleEngine On
253         SecDebugLog $ENV{DEBUG_LOG}
254         SecDebugLogLevel 9
255         SecRequestBodyAccess On
256         SecAuditLog "$ENV{AUDIT_LOG}"
257         SecAuditEngine RelevantOnly
258     ),
259     match_log => {
260         -debug => [ qr/Multipart error:/, 1 ],
261         audit => [ qr/Part missing Content-Disposition header/, 1 ],
262         debug => [ qr/Part missing Content-Disposition header/, 1 ],
263
264     },
265     match_response => {
266         status => qr/^200$/,
267     },
268     request => new HTTP::Request(
269         POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
270         [
271             "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646",
272         ],
273         normalize_raw_request_data(
274             q(
275                 -----------------------------69343412719991675451336310646
276                 
277                 1
278                 -----------------------------69343412719991675451336310646
279                 
280                 2
281                 -----------------------------69343412719991675451336310646--
282             ),
283         ),
284     ),
285 },
286 {
287     type => "misc",
288     comment => "multipart parser error (bad disposition)",
289     conf => qq(
290         SecRuleEngine On
291         SecDebugLog $ENV{DEBUG_LOG}
292         SecDebugLogLevel 9
293         SecRequestBodyAccess On
294         SecAuditLog "$ENV{AUDIT_LOG}"
295         SecAuditEngine RelevantOnly
296     ),
297     match_log => {
298         audit => [ qr/Invalid Content-Disposition header/, 1 ],
299         debug => [ qr/Invalid Content-Disposition header/, 1 ],
300
301     },
302     match_response => {
303         status => qr/^200$/,
304     },
305     request => new HTTP::Request(
306         POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
307         [
308             "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646",
309         ],
310         normalize_raw_request_data(
311             q(
312                 -----------------------------69343412719991675451336310646
313                 Content-Disposition: form-data name="a"
314                 
315                 1
316                 -----------------------------69343412719991675451336310646
317                 Content-Disposition: form-data name="b"
318                 
319                 2
320                 -----------------------------69343412719991675451336310646--
321             ),
322         ),
323     ),
324 },
325 {
326     type => "misc",
327     comment => "multipart parser error (no disposition name)",
328     conf => qq(
329         SecRuleEngine On
330         SecDebugLog $ENV{DEBUG_LOG}
331         SecDebugLogLevel 9
332         SecRequestBodyAccess On
333         SecAuditLog "$ENV{AUDIT_LOG}"
334         SecAuditEngine RelevantOnly
335     ),
336     match_log => {
337         -debug => [ qr/Multipart error:/, 1 ],
338         audit => [ qr/Content-Disposition header missing name field/, 1 ],
339         debug => [ qr/Content-Disposition header missing name field/, 1 ],
340
341     },
342     match_response => {
343         status => qr/^200$/,
344     },
345     request => new HTTP::Request(
346         POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
347         [
348             "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646",
349         ],
350         normalize_raw_request_data(
351             q(
352                 -----------------------------69343412719991675451336310646
353                 Content-Disposition: form-data;
354                 
355                 1
356                 -----------------------------69343412719991675451336310646
357                 Content-Disposition: form-data;
358                 
359                 2
360                 -----------------------------69343412719991675451336310646--
361             ),
362         ),
363     ),
364 },
365 # Zero length part name should not crash
366 {
367     type => "misc",
368     comment => "multipart parser (zero length part name)",
369     conf => qq(
370         SecRuleEngine On
371         SecDebugLog $ENV{DEBUG_LOG}
372         SecDebugLogLevel 9
373         SecRequestBodyAccess On
374         #SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny,status:403"
375         SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny,status:403"
376         SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,status:403"
377     ),
378     match_log => {
379         debug => [ qr/name: a.*variable: 1.*Invalid part header \(header name missing\)/s, 1 ],
380         -debug => [ qr/Adding request argument \(BODY\): name "b"/s, 1 ],
381     },
382     match_response => {
383         status => qr/^403$/,
384     },
385     request => new HTTP::Request(
386         POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
387         [
388             "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646",
389         ],
390         normalize_raw_request_data(
391             q(
392                 -----------------------------69343412719991675451336310646
393                 Content-Disposition: form-data; name="a"
394                 
395                 1
396                 -----------------------------69343412719991675451336310646
397                 :
398                 -----------------------------69343412719991675451336310646
399                 Content-Disposition: form-data; name="b"
400                 
401                 2
402                 -----------------------------69343412719991675451336310646--
403             ),
404         ),
405     ),
406 },
407 # Data following final boundary should set flag
408 {
409     type => "misc",
410     comment => "multipart parser (data after final boundary)",
411     conf => qq(
412         SecRuleEngine On
413         SecDebugLog $ENV{DEBUG_LOG}
414         SecDebugLogLevel 9
415         SecRequestBodyAccess On
416         SecRule MULTIPART_DATA_AFTER "\@eq 1" "phase:2,deny,status:403"
417     ),
418     match_log => {
419         debug => [ qr/name: a.*variable: 1.*Ignoring data after last boundary/s, 1 ],
420         -debug => [ qr/Adding request argument \(BODY\): name "b"/s, 1 ],
421     },
422     match_response => {
423         status => qr/^403$/,
424     },
425     request => new HTTP::Request(
426         POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
427         [
428             "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646",
429         ],
430         normalize_raw_request_data(
431             q(
432                 -----------------------------69343412719991675451336310646
433                 Content-Disposition: form-data; name="a"
434                 
435                 1
436                 -----------------------------69343412719991675451336310646--
437                 -----------------------------69343412719991675451336310646
438                 Content-Disposition: form-data; name="b"
439                 
440                 2
441                 -----------------------------69343412719991675451336310646--
442             ),
443         ),
444     ),
445 },
446 # Single quoted data is invalid
447 {
448     type => "misc",
449     comment => "multipart parser (C-D uses single quotes)",
450     conf => qq(
451         SecRuleEngine On
452         SecDebugLog $ENV{DEBUG_LOG}
453         SecDebugLogLevel 9
454         SecRequestBodyAccess On
455         #SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny,status:403"
456         SecRule MULTIPART_INVALID_QUOTING "\@eq 1" "chain,phase:2,deny,status:403"
457         SecRule REQBODY_PROCESSOR_ERROR "\@eq 1"
458     ),
459     match_log => {
460         debug => [ qr/name: a.*variable: 1.*Duplicate Content-Disposition name/s, 1 ],
461         -debug => [ qr/Adding request argument \(BODY\): name "b/s, 1 ],
462     },
463     match_response => {
464         status => qr/^403$/,
465     },
466     request => new HTTP::Request(
467         POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
468         [
469             "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646",
470         ],
471         normalize_raw_request_data(
472             q(
473                 -----------------------------69343412719991675451336310646
474                 Content-Disposition: form-data; name="a"
475                 
476                 1
477                 -----------------------------69343412719991675451336310646
478                 Content-Disposition: form-data; name=';filename="dummy';name=b;"
479                 
480                 2
481                 -----------------------------69343412719991675451336310646--
482             ),
483         ),
484     ),
485 },