mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-19 11:19:36 +00:00
Fix problem with alternate file name check
This commit is contained in:
1 parent
41899acece
commit
de296af3eb
2 files changed
+47
-16
No files matched your search
@@ -7748,14 +7748,10 @@ public:
|
|||||||
file_.content_type =
|
file_.content_type =
|
||||||
trim_copy(header.substr(str_len(header_content_type)));
|
trim_copy(header.substr(str_len(header_content_type)));
|
||||||
} else {
|
} else {
|
||||||
thread_local const std::regex re_content_disposition(
|
std::string disposition_params;
|
||||||
R"~(^Content-Disposition:\s*form-data;\s*(.*)$)~",
|
if (parse_content_disposition(header, disposition_params)) {
|
||||||
std::regex_constants::icase);
|
|
||||||
|
|
||||||
std::smatch m;
|
|
||||||
if (std::regex_match(header, m, re_content_disposition)) {
|
|
||||||
Params params;
|
Params params;
|
||||||
parse_disposition_params(m[1], params);
|
parse_disposition_params(disposition_params, params);
|
||||||
|
|
||||||
auto it = params.find("name");
|
auto it = params.find("name");
|
||||||
if (it != params.end()) {
|
if (it != params.end()) {
|
||||||
@@ -7772,10 +7768,10 @@ public:
|
|||||||
if (it != params.end()) {
|
if (it != params.end()) {
|
||||||
// RFC 5987: only UTF-8 encoding is allowed
|
// RFC 5987: only UTF-8 encoding is allowed
|
||||||
const auto &val = it->second;
|
const auto &val = it->second;
|
||||||
constexpr size_t prefix_len = 7; // length of "UTF-8''"
|
constexpr const char utf8_prefix[] = "UTF-8''";
|
||||||
|
constexpr size_t prefix_len = str_len(utf8_prefix);
|
||||||
if (val.size() > prefix_len &&
|
if (val.size() > prefix_len &&
|
||||||
case_ignore::to_lower(val.substr(0, prefix_len)) ==
|
start_with_case_ignore(val, utf8_prefix)) {
|
||||||
"utf-8''") {
|
|
||||||
file_.filename = decode_path_component(
|
file_.filename = decode_path_component(
|
||||||
val.substr(prefix_len)); // override...
|
val.substr(prefix_len)); // override...
|
||||||
} else {
|
} else {
|
||||||
@@ -7845,17 +7841,48 @@ private:
|
|||||||
file_.headers.clear();
|
file_.headers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool start_with_case_ignore(const std::string &a, const char *b) const {
|
bool start_with_case_ignore(const std::string &a, const char *b,
|
||||||
|
size_t offset = 0) const {
|
||||||
const auto b_len = strlen(b);
|
const auto b_len = strlen(b);
|
||||||
if (a.size() < b_len) { return false; }
|
if (a.size() < offset + b_len) { return false; }
|
||||||
for (size_t i = 0; i < b_len; i++) {
|
for (size_t i = 0; i < b_len; i++) {
|
||||||
if (case_ignore::to_lower(a[i]) != case_ignore::to_lower(b[i])) {
|
if (case_ignore::to_lower(a[offset + i]) != case_ignore::to_lower(b[i])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parses "Content-Disposition: form-data; <params>" without std::regex.
|
||||||
|
// Returns true if header matches, with the params portion in `params_out`.
|
||||||
|
bool parse_content_disposition(const std::string &header,
|
||||||
|
std::string ¶ms_out) const {
|
||||||
|
constexpr const char prefix[] = "Content-Disposition:";
|
||||||
|
constexpr size_t prefix_len = str_len(prefix);
|
||||||
|
|
||||||
|
if (!start_with_case_ignore(header, prefix)) { return false; }
|
||||||
|
|
||||||
|
// Skip whitespace after "Content-Disposition:"
|
||||||
|
auto pos = prefix_len;
|
||||||
|
while (pos < header.size() && (header[pos] == ' ' || header[pos] == '\t')) {
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match "form-data;" (case-insensitive)
|
||||||
|
constexpr const char form_data[] = "form-data;";
|
||||||
|
constexpr size_t form_data_len = str_len(form_data);
|
||||||
|
if (!start_with_case_ignore(header, form_data, pos)) { return false; }
|
||||||
|
pos += form_data_len;
|
||||||
|
|
||||||
|
// Skip whitespace after "form-data;"
|
||||||
|
while (pos < header.size() && (header[pos] == ' ' || header[pos] == '\t')) {
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
params_out = header.substr(pos);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string dash_ = "--";
|
const std::string dash_ = "--";
|
||||||
const std::string crlf_ = "\r\n";
|
const std::string crlf_ = "\r\n";
|
||||||
std::string boundary_;
|
std::string boundary_;
|
||||||
|
|||||||
+7
-3
@@ -11617,7 +11617,11 @@ TEST(MultipartFormDataTest, AlternateFilenameLongValueAndCaseInsensitive) {
|
|||||||
// Case-insensitive "utf-8''" prefix with a long value
|
// Case-insensitive "utf-8''" prefix with a long value
|
||||||
const auto &file = req.form.get_file("file1");
|
const auto &file = req.form.get_file("file1");
|
||||||
ASSERT_EQ("file1", file.name);
|
ASSERT_EQ("file1", file.name);
|
||||||
std::string expected_filename(2000, 'A');
|
// 8000 chars exercises both the Content-Disposition parser and the
|
||||||
|
// filename* parser near the CPPHTTPLIB_HEADER_MAX_LENGTH limit (8192).
|
||||||
|
// Prior to the fix, std::regex_match on this header would cause O(N)
|
||||||
|
// stack recursion in libstdc++, leading to SIGSEGV.
|
||||||
|
std::string expected_filename(8000, 'A');
|
||||||
ASSERT_EQ(expected_filename, file.filename);
|
ASSERT_EQ(expected_filename, file.filename);
|
||||||
|
|
||||||
res.set_content("ok", "text/plain");
|
res.set_content("ok", "text/plain");
|
||||||
@@ -11635,8 +11639,8 @@ TEST(MultipartFormDataTest, AlternateFilenameLongValueAndCaseInsensitive) {
|
|||||||
svr.wait_until_ready();
|
svr.wait_until_ready();
|
||||||
|
|
||||||
// Build body with a long filename* value using mixed-case prefix "Utf-8''"
|
// Build body with a long filename* value using mixed-case prefix "Utf-8''"
|
||||||
// Prior to the fix, this would cause O(N) stack recursion in std::regex
|
// Regression test for GHSA-qq6v-r583-3h69
|
||||||
std::string long_filename(2000, 'A');
|
std::string long_filename(8000, 'A');
|
||||||
std::string body = "----------\r\n"
|
std::string body = "----------\r\n"
|
||||||
"Content-Disposition: form-data; name=\"file1\"; "
|
"Content-Disposition: form-data; name=\"file1\"; "
|
||||||
"filename*=\"Utf-8''" +
|
"filename*=\"Utf-8''" +
|
||||||
|
|||||||
Reference in new issue
Block a user