Skip to content

Commit 7642b05

Browse files
patrikjuvonenbotder
andcommitted
Add limits to ehs form fields
Co-authored-by: Marek Kulik <me@botder.com>
1 parent 3191bbb commit 7642b05

File tree

3 files changed

+92
-19
lines changed

3 files changed

+92
-19
lines changed

vendor/ehs/formvalue.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#define FORMVALUE_H
44

55
#include <string>
6+
#include <string_view>
67

78
#include "contentdisposition.h"
89

@@ -43,6 +44,11 @@ class FormValue {
4344
#endif
4445
}
4546

47+
/// Constructor
48+
explicit FormValue(std::string_view body) : sBody{body}
49+
{
50+
}
51+
4652
#ifdef EHS_MEMORY
4753
/// This is only for watching memory allocation
4854
FormValue ( const FormValue & iroFormValue ) {

vendor/ehs/httprequest.cpp

Lines changed: 80 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,96 @@
44

55
#include <assert.h>
66

7-
void HttpRequest::GetFormDataFromString ( const std::string & irsString ///< string to parse for form data
8-
)
7+
void HttpRequest::ParseRequestURI(std::string_view uri)
98
{
9+
if (uri.empty())
10+
return;
1011

11-
PME oNameValueRegex ( "[?]?([^?=]*)=([^&]*)&?", "g" );
12-
while ( oNameValueRegex.match ( irsString ) ) {
12+
// See: https://www.rfc-editor.org/rfc/rfc3986#section-3.4
13+
if (size_t queryDelimiter = uri.find('?'); queryDelimiter != std::string_view::npos)
14+
{
15+
// Skip repeating '?' (question mark) characters after the first one.
16+
if (queryDelimiter = uri.find_first_not_of('?', queryDelimiter + 1); queryDelimiter == std::string_view::npos)
17+
return;
1318

14-
ContentDisposition oContentDisposition;
15-
std::string sName = oNameValueRegex [ 1 ];
16-
std::string sValue = oNameValueRegex [ 2 ];
19+
std::string_view parameters = uri.substr(queryDelimiter);
1720

18-
#ifdef EHS_DEBUG
19-
fprintf ( stderr, "[EHS_DEBUG] Info: Got form data: '%s' => '%s'\n",
20-
sName.c_str ( ),
21-
sValue.c_str ( ) );
22-
#endif
21+
// Discard any trailing fragment.
22+
if (size_t fragmentDelimiter = parameters.find('#'); fragmentDelimiter != std::string_view::npos)
23+
{
24+
parameters = parameters.substr(0, fragmentDelimiter);
25+
}
2326

24-
oFormValueMap [ sName ] =
25-
FormValue ( sValue, oContentDisposition );
27+
// Limit request uri parameters to 4096 bytes.
28+
if (parameters.length() > 4096)
29+
{
30+
parameters = parameters.substr(0, 4096);
31+
}
2632

33+
// According to the RFC, query can be anything, but this web server implementation only supports "key=value" pairs,
34+
// which are parsable as form data.
35+
ParseFormData(parameters);
2736
}
28-
2937
}
3038

39+
void HttpRequest::ParseFormData(std::string_view formData)
40+
{
41+
size_t counter = 0;
42+
43+
// Limit the maximum acceptable form data fields to 256.
44+
while (!formData.empty() && counter < 256)
45+
{
46+
std::string_view parameter;
47+
48+
if (size_t delimiter = formData.find('&'); delimiter != std::string_view::npos)
49+
{
50+
parameter = formData.substr(0, delimiter);
51+
52+
// Skip repeating '&' (ampersand) characters after the first one.
53+
if (delimiter = formData.find_first_not_of('&', delimiter + 1); delimiter != std::string_view::npos)
54+
{
55+
formData = formData.substr(delimiter);
56+
}
57+
else
58+
{
59+
formData = {};
60+
}
61+
}
62+
else
63+
{
64+
parameter = formData;
65+
formData = {};
66+
}
67+
68+
if (!parameter.empty())
69+
{
70+
std::string_view key, value;
71+
72+
if (size_t delimiter = parameter.find('='); delimiter != std::string_view::npos)
73+
{
74+
key = parameter.substr(0, delimiter);
3175

76+
// Skip repeating '=' (equals) characters after the first one.
77+
if (delimiter = parameter.find_first_not_of('=', delimiter + 1); delimiter != std::string_view::npos)
78+
{
79+
value = parameter.substr(delimiter);
80+
}
81+
}
82+
else
83+
{
84+
key = parameter;
85+
// NOTE: value is empty.
86+
}
3287

88+
if (!key.empty())
89+
{
90+
oFormValueMap[std::string{key}] = FormValue{value};
91+
}
92+
}
3393

94+
counter += 1;
95+
}
96+
}
3497

3598
// this parses a single piece of a multipart form body
3699
// here are two examples -- first is an uploaded file, second is a
@@ -334,7 +397,7 @@ HttpRequest::HttpParseStates HttpRequest::ParseData ( std::string & irsData ///<
334397

335398

336399
// check to see if the uri appeared to have form data in it
337-
GetFormDataFromString ( sUri );
400+
ParseRequestURI(sUri);
338401

339402
// on to the headers
340403
nCurrentHttpParseState = HTTPPARSESTATE_HEADERS;
@@ -491,7 +554,7 @@ HttpRequest::HttpParseStates HttpRequest::ParseData ( std::string & irsData ///<
491554
// else the body is just one piece
492555
else {
493556
// check for any form data
494-
GetFormDataFromString ( sBody );
557+
ParseFormData(sBody);
495558

496559
#ifdef EHS_DEBUG
497560
fprintf ( stderr, "Done with body, done with entire request\n" );

vendor/ehs/httprequest.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#endif
99

1010
#include <map>
11+
#include <string_view>
1112

1213
#include <pme.h>
1314

@@ -87,8 +88,11 @@ class HttpRequest {
8788
/// takes the cookie header and breaks it down into usable chunks -- returns number of name/value pairs found
8889
int ParseCookieData ( std::string & irsData );
8990

90-
/// interprets the given string as if it's name=value pairs and puts them into oFormElements
91-
void GetFormDataFromString ( const std::string & irsString );
91+
/// parses the request uri, extracts the query segment and puts its key=value pairs into oFormElements
92+
void ParseRequestURI(std::string_view uri);
93+
94+
/// parses the form data as if it's key=value pairs and puts them into oFormElements
95+
void ParseFormData(std::string_view formData);
9296

9397
/// the current parse state -- where we are in looking at the data from the client
9498
HttpParseStates nCurrentHttpParseState;

0 commit comments

Comments
 (0)