xref: /openbmc/bmcweb/test/include/multipart_test.cpp (revision 1516c21b27faf8dcf7c41e9b7253da97025a5f28)
1 #include "http_request.hpp"
2 #include "multipart_parser.hpp"
3 
4 #include <boost/beast/http/fields.hpp>
5 
6 #include <iterator>
7 #include <string_view>
8 #include <system_error>
9 #include <vector>
10 
11 #include <gtest/gtest.h> // IWYU pragma: keep
12 
13 // IWYU pragma: no_include <gtest/gtest-message.h>
14 // IWYU pragma: no_include <gtest/gtest-test-part.h>
15 // IWYU pragma: no_include "gtest/gtest_pred_impl.h"
16 // IWYU pragma: no_include <boost/beast/http/impl/fields.hpp>
17 // IWYU pragma: no_include <boost/intrusive/detail/list_iterator.hpp>
18 // IWYU pragma: no_include <boost/intrusive/detail/tree_iterator.hpp>
19 
20 namespace
21 {
22 using ::testing::Test;
23 
24 class MultipartTest : public Test
25 {
26   public:
27     MultipartParser parser;
28     std::error_code ec;
29 };
30 
31 TEST_F(MultipartTest, TestGoodMultipartParser)
32 {
33     std::string_view body =
34         "-----------------------------d74496d66958873e\r\n"
35         "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
36         "111111111111111111111111112222222222222222222222222222222\r\n"
37         "-----------------------------d74496d66958873e\r\n"
38         "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
39         "{\r\n-----------------------------d74496d66958873e123456\r\n"
40         "-----------------------------d74496d66958873e\r\n"
41         "Content-Disposition: form-data; name=\"Test3\"\r\n\r\n"
42         "{\r\n--------d74496d6695887}\r\n"
43         "-----------------------------d74496d66958873e--\r\n";
44 
45     crow::Request reqIn(body, ec);
46 
47     reqIn.addHeader("Content-Type",
48                     "multipart/form-data; "
49                     "boundary=---------------------------d74496d66958873e");
50 
51     ParserError rc = parser.parse(reqIn);
52     ASSERT_EQ(rc, ParserError::PARSER_SUCCESS);
53 
54     EXPECT_EQ(parser.boundary,
55               "\r\n-----------------------------d74496d66958873e");
56     EXPECT_EQ(parser.mime_fields.size(), 3);
57 
58     EXPECT_EQ(parser.mime_fields[0].fields.at("Content-Disposition"),
59               "form-data; name=\"Test1\"");
60     EXPECT_EQ(parser.mime_fields[0].content,
61               "111111111111111111111111112222222222222222222222222222222");
62 
63     EXPECT_EQ(parser.mime_fields[1].fields.at("Content-Disposition"),
64               "form-data; name=\"Test2\"");
65     EXPECT_EQ(parser.mime_fields[1].content,
66               "{\r\n-----------------------------d74496d66958873e123456");
67     EXPECT_EQ(parser.mime_fields[2].fields.at("Content-Disposition"),
68               "form-data; name=\"Test3\"");
69     EXPECT_EQ(parser.mime_fields[2].content, "{\r\n--------d74496d6695887}");
70 }
71 
72 TEST_F(MultipartTest, TestBadMultipartParser1)
73 {
74     std::string_view body =
75         "-----------------------------d74496d66958873e\r\n"
76         "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
77         "1234567890\r\n"
78         "-----------------------------d74496d66958873e\r-\r\n";
79 
80     crow::Request reqIn(body, ec);
81 
82     reqIn.addHeader("Content-Type",
83                     "multipart/form-data; "
84                     "boundary=---------------------------d74496d66958873e");
85 
86     ParserError rc = parser.parse(reqIn);
87 
88     EXPECT_EQ(rc, ParserError::ERROR_UNEXPECTED_END_OF_INPUT);
89 }
90 
91 TEST_F(MultipartTest, TestBadMultipartParser2)
92 {
93     std::string_view body =
94         "-----------------------------d74496d66958873e\r\n"
95         "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
96         "abcd\r\n"
97         "-----------------------------d74496d66958873e-\r\n";
98     crow::Request reqIn(body, ec);
99 
100     reqIn.addHeader("Content-Type",
101                     "multipart/form-data; "
102                     "boundary=---------------------------d74496d66958873e");
103 
104     ParserError rc = parser.parse(reqIn);
105 
106     EXPECT_EQ(rc, ParserError::ERROR_UNEXPECTED_END_OF_INPUT);
107 }
108 
109 TEST_F(MultipartTest, TestErrorBoundaryFormat)
110 {
111     std::string_view body =
112         "-----------------------------d74496d66958873e\r\n"
113         "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
114         "{\"Key1\": 11223333333333333333333333333333333333333333}\r\n"
115         "-----------------------------d74496d66958873e\r\n"
116         "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
117         "123456\r\n"
118         "-----------------------------d74496d66958873e--\r\n";
119 
120     crow::Request reqIn(body, ec);
121 
122     reqIn.addHeader("Content-Type",
123                     "multipart/form-data; "
124                     "boundary+=-----------------------------d74496d66958873e");
125 
126     EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_BOUNDARY_FORMAT);
127 }
128 
129 TEST_F(MultipartTest, TestErrorBoundaryCR)
130 {
131     std::string_view body =
132         "-----------------------------d74496d66958873e"
133         "Content-Disposition: form-data; name=\"Test1\"\r\n\r"
134         "{\"Key1\": 112233}\r\n"
135         "-----------------------------d74496d66958873e\r\n"
136         "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
137         "123456\r\n"
138         "-----------------------------d74496d66958873e--\r\n";
139     crow::Request reqIn(body, ec);
140 
141     reqIn.addHeader("Content-Type",
142                     "multipart/form-data; "
143                     "boundary=---------------------------d74496d66958873e");
144 
145     EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_BOUNDARY_CR);
146 }
147 
148 TEST_F(MultipartTest, TestErrorBoundaryLF)
149 {
150     std::string_view body =
151         "-----------------------------d74496d66958873e\r"
152         "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
153         "{\"Key1\": 112233}\r\n"
154         "-----------------------------d74496d66958873e\r\n"
155         "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
156         "123456\r\n"
157         "-----------------------------d74496d66958873e--\r\n";
158 
159     crow::Request reqIn(body, ec);
160 
161     reqIn.addHeader("Content-Type",
162                     "multipart/form-data; "
163                     "boundary=---------------------------d74496d66958873e");
164 
165     EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_BOUNDARY_LF);
166 }
167 
168 TEST_F(MultipartTest, TestErrorBoundaryData)
169 {
170     std::string_view body =
171         "-----------------------------d74496d66958873e\r\n"
172         "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
173         "{\"Key1\": 112233}\r\n"
174         "-----------------------------d74496d66958873e\r\n"
175         "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
176         "123456\r\n"
177         "-----------------------------d74496d66958873e--\r\n";
178 
179     crow::Request reqIn(body, ec);
180 
181     reqIn.addHeader("Content-Type",
182                     "multipart/form-data; "
183                     "boundary=---------------------------d7449sd6d66958873e");
184 
185     EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_BOUNDARY_DATA);
186 }
187 
188 TEST_F(MultipartTest, TestErrorEmptyHeader)
189 {
190     std::string_view body =
191         "-----------------------------d74496d66958873e\r\n"
192         ": form-data; name=\"Test1\"\r\n"
193         "{\"Key1\": 112233}\r\n"
194         "-----------------------------d74496d66958873e\r\n"
195         "Content-Disposition: form-data; name=\"Test2\"\r\n"
196         "123456\r\n"
197         "-----------------------------d74496d66958873e--\r\n";
198     crow::Request reqIn(body, ec);
199 
200     reqIn.addHeader("Content-Type",
201                     "multipart/form-data; "
202                     "boundary=---------------------------d74496d66958873e");
203 
204     EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_EMPTY_HEADER);
205 }
206 
207 TEST_F(MultipartTest, TestErrorHeaderName)
208 {
209     std::string_view body =
210         "-----------------------------d74496d66958873e\r\n"
211         "Content-!!Disposition: form-data; name=\"Test1\"\r\n"
212         "{\"Key1\": 112233}\r\n"
213         "-----------------------------d74496d66958873e\r\n"
214         "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
215         "123456\r\n"
216         "-----------------------------d74496d66958873e--\r\n";
217     crow::Request reqIn(body, ec);
218 
219     reqIn.addHeader("Content-Type",
220                     "multipart/form-data; "
221                     "boundary=---------------------------d74496d66958873e");
222 
223     EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_HEADER_NAME);
224 }
225 
226 TEST_F(MultipartTest, TestErrorHeaderValue)
227 {
228     std::string_view body =
229         "-----------------------------d74496d66958873e\r\n"
230         "Content-Disposition: form-data; name=\"Test1\"\r"
231         "{\"Key1\": 112233}\r\n"
232         "-----------------------------d74496d66958873e\r\n"
233         "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
234         "123456\r\n"
235         "-----------------------------d74496d66958873e--\r\n";
236 
237     crow::Request reqIn(body, ec);
238 
239     reqIn.addHeader("Content-Type",
240                     "multipart/form-data; "
241                     "boundary=---------------------------d74496d66958873e");
242 
243     EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_HEADER_VALUE);
244 }
245 
246 TEST_F(MultipartTest, TestErrorHeaderEnding)
247 {
248     std::string_view body =
249         "-----------------------------d74496d66958873e\r\n"
250         "Content-Disposition: form-data; name=\"Test1\"\r\n\r"
251         "{\"Key1\": 112233}\r\n"
252         "-----------------------------d74496d66958873e\r\n"
253         "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
254         "123456\r\n"
255         "-----------------------------d74496d66958873e--\r\n";
256 
257     crow::Request reqIn(body, ec);
258 
259     reqIn.addHeader("Content-Type",
260                     "multipart/form-data; "
261                     "boundary=---------------------------d74496d66958873e");
262 
263     EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_HEADER_ENDING);
264 }
265 
266 TEST_F(MultipartTest, TestGoodMultipartParserMultipleHeaders)
267 {
268     std::string_view body =
269         "-----------------------------d74496d66958873e\r\n"
270         "Content-Disposition: form-data; name=\"Test1\"\r\n"
271         "Other-Header: value=\"v1\"\r\n"
272         "\r\n"
273         "Data1\r\n"
274         "-----------------------------d74496d66958873e--";
275 
276     crow::Request reqIn(body, ec);
277 
278     reqIn.addHeader("Content-Type",
279                     "multipart/form-data; "
280                     "boundary=---------------------------d74496d66958873e");
281 
282     ParserError rc = parser.parse(reqIn);
283     ASSERT_EQ(rc, ParserError::PARSER_SUCCESS);
284 
285     EXPECT_EQ(parser.boundary,
286               "\r\n-----------------------------d74496d66958873e");
287     ASSERT_EQ(parser.mime_fields.size(), 1);
288 
289     EXPECT_EQ(parser.mime_fields[0].fields.at("Content-Disposition"),
290               "form-data; name=\"Test1\"");
291     EXPECT_EQ(parser.mime_fields[0].fields.at("Other-Header"), "value=\"v1\"");
292     EXPECT_EQ(parser.mime_fields[0].content, "Data1");
293 }
294 
295 TEST_F(MultipartTest, TestErrorHeaderWithoutColon)
296 {
297     std::string_view body =
298         "----end\r\n"
299         "abc\r\n"
300         "\r\n"
301         "Data1\r\n"
302         "----end--\r\n";
303     crow::Request reqIn(body, ec);
304 
305     reqIn.addHeader("Content-Type", "multipart/form-data; "
306                                     "boundary=--end");
307 
308     EXPECT_EQ(parser.parse(reqIn), ParserError::ERROR_UNEXPECTED_END_OF_HEADER);
309 }
310 
311 TEST_F(MultipartTest, TestUnknownHeaderIsCorrectlyParsed)
312 {
313     std::string_view body =
314         "----end\r\n"
315         "t-DiPpcccc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccgcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccaaaaaa\r\n"
316         "\r\n"
317         "Data1\r\n"
318         "----end--\r\n";
319 
320     crow::Request reqIn(body, ec);
321 
322     reqIn.addHeader("Content-Type", "multipart/form-data; "
323                                     "boundary=--end");
324     ParserError rc = parser.parse(reqIn);
325 
326     ASSERT_EQ(rc, ParserError::PARSER_SUCCESS);
327 
328     EXPECT_EQ(parser.boundary, "\r\n----end");
329     ASSERT_EQ(parser.mime_fields.size(), 1);
330 
331     EXPECT_EQ(
332         parser.mime_fields[0].fields.at("t-DiPpcccc"),
333         "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccgcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccaaaaaa");
334     EXPECT_EQ(parser.mime_fields[0].content, "Data1");
335 }
336 
337 TEST_F(MultipartTest, TestErrorMissingSeparatorBetweenMimeFieldsAndData)
338 {
339     std::string_view body =
340         "-----------------------------d74496d66958873e\r\n"
341         "t-DiPpcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccgcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccaaaaaa\r\n"
342         "Data1"
343         "-----------------------------d74496d66958873e--";
344 
345     crow::Request reqIn(body, ec);
346 
347     reqIn.addHeader(
348         "Content-Type",
349         "multipart/form-data; boundary=---------------------------d74496d66958873e");
350 
351     ParserError rc = parser.parse(reqIn);
352 
353     EXPECT_EQ(rc, ParserError::ERROR_UNEXPECTED_END_OF_HEADER);
354 }
355 
356 TEST_F(MultipartTest, TestDataWithoutMimeFields)
357 {
358     std::string_view body =
359         "-----------------------------d74496d66958873e\r\n"
360         "\r\n"
361         "Data1\r\n"
362         "-----------------------------d74496d66958873e--";
363 
364     crow::Request reqIn(body, ec);
365 
366     reqIn.addHeader(
367         "Content-Type",
368         "multipart/form-data; boundary=---------------------------d74496d66958873e");
369 
370     ParserError rc = parser.parse(reqIn);
371 
372     ASSERT_EQ(rc, ParserError::PARSER_SUCCESS);
373 
374     EXPECT_EQ(parser.boundary,
375               "\r\n-----------------------------d74496d66958873e");
376     ASSERT_EQ(parser.mime_fields.size(), 1);
377 
378     EXPECT_EQ(std::distance(parser.mime_fields[0].fields.begin(),
379                             parser.mime_fields[0].fields.end()),
380               0);
381     EXPECT_EQ(parser.mime_fields[0].content, "Data1");
382 }
383 
384 TEST_F(MultipartTest, TestErrorMissingFinalBoundry)
385 {
386     std::string_view body =
387         "----XX\r\n"
388         "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
389         "t-DiPpccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccAAAAAAAAAAAAAAABCDz\r\n"
390         "\335\r\n\r\n";
391 
392     crow::Request reqIn(body, ec);
393 
394     reqIn.addHeader("Content-Type", "multipart/form-data; boundary=--XX");
395 
396     ParserError rc = parser.parse(reqIn);
397 
398     EXPECT_EQ(rc, ParserError::ERROR_UNEXPECTED_END_OF_INPUT);
399 }
400 
401 TEST_F(MultipartTest, TestIgnoreDataAfterFinalBoundary)
402 {
403     std::string_view body =
404         "----XX\r\n"
405         "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
406         "Data1\r\n"
407         "----XX--\r\n"
408         "Content-Disposition: form-data; name=\"Test2\"\r\n\r\n"
409         "Data2\r\n"
410         "----XX--\r\n";
411 
412     crow::Request reqIn(body, ec);
413 
414     reqIn.addHeader("Content-Type", "multipart/form-data; boundary=--XX");
415 
416     ParserError rc = parser.parse(reqIn);
417 
418     ASSERT_EQ(rc, ParserError::PARSER_SUCCESS);
419 
420     EXPECT_EQ(parser.boundary, "\r\n----XX");
421     EXPECT_EQ(parser.mime_fields.size(), 1);
422 
423     EXPECT_EQ(parser.mime_fields[0].fields.at("Content-Disposition"),
424               "form-data; name=\"Test1\"");
425     EXPECT_EQ(parser.mime_fields[0].content, "Data1");
426 }
427 
428 TEST_F(MultipartTest, TestFinalBoundaryIsCorrectlyRecognized)
429 {
430     std::string_view body =
431         "----XX\r\n"
432         "Content-Disposition: form-data; name=\"Test1\"\r\n\r\n"
433         "Data1\r\n"
434         "----XX-abc-\r\n"
435         "StillData1\r\n"
436         "----XX--\r\n";
437 
438     crow::Request reqIn(body, ec);
439 
440     reqIn.addHeader("Content-Type", "multipart/form-data; boundary=--XX");
441 
442     ParserError rc = parser.parse(reqIn);
443 
444     ASSERT_EQ(rc, ParserError::PARSER_SUCCESS);
445 
446     EXPECT_EQ(parser.boundary, "\r\n----XX");
447     EXPECT_EQ(parser.mime_fields.size(), 1);
448 
449     EXPECT_EQ(parser.mime_fields[0].fields.at("Content-Disposition"),
450               "form-data; name=\"Test1\"");
451     EXPECT_EQ(parser.mime_fields[0].content,
452               "Data1\r\n"
453               "----XX-abc-\r\n"
454               "StillData1");
455 }
456 
457 } // namespace
458