diff --git a/headers/modsecurity/transaction.h b/headers/modsecurity/transaction.h index 3e70caa38..23374d6d0 100644 --- a/headers/modsecurity/transaction.h +++ b/headers/modsecurity/transaction.h @@ -397,7 +397,7 @@ class Transaction : public TransactionAnchoredVariables, public TransactionSecMa bool addArgument(const std::string& orig, const std::string& key, const std::string& value, size_t offset); bool extractArguments(const std::string &orig, const std::string& buf, - size_t offset); + size_t offset, bool partial_processing_enabled = false); const char *getResponseBody() const; size_t getResponseBodyLength(); @@ -645,6 +645,11 @@ class Transaction : public TransactionAnchoredVariables, public TransactionSecMa * the web server (connector) log. */ void *m_logCbData; + + /** + * Whether the request body was bigger than RequestBodyLimit. + */ + bool m_requestBodyLimitExceeded; }; diff --git a/src/request_body_processor/json.cc b/src/request_body_processor/json.cc index f56704eff..81e910cc0 100644 --- a/src/request_body_processor/json.cc +++ b/src/request_body_processor/json.cc @@ -29,7 +29,8 @@ namespace RequestBodyProcessor { static const double json_depth_limit_default = 10000.0; static const char* json_depth_limit_exceeded_msg = ". Parsing depth limit exceeded"; -JSON::JSON(Transaction *transaction) : m_transaction(transaction), +JSON::JSON(Transaction *transaction) + : m_transaction(transaction), m_handle(NULL), m_current_key(""), m_max_depth(json_depth_limit_default), @@ -68,8 +69,6 @@ JSON::JSON(Transaction *transaction) : m_transaction(transaction), * TODO: make UTF8 validation optional, as it depends on Content-Encoding */ m_handle = yajl_alloc(&callbacks, NULL, this); - - yajl_config(m_handle, yajl_allow_partial_values, 0); } @@ -83,7 +82,8 @@ JSON::~JSON() { } -bool JSON::init() { +bool JSON::init(unsigned int allow_partial_values) { + yajl_config(m_handle, yajl_allow_partial_values, allow_partial_values); return true; } diff --git a/src/request_body_processor/json.h b/src/request_body_processor/json.h index 961ea94ea..7a2c2b505 100644 --- a/src/request_body_processor/json.h +++ b/src/request_body_processor/json.h @@ -60,7 +60,7 @@ class JSON { explicit JSON(Transaction *transaction); ~JSON(); - static bool init(); + bool init(unsigned int allow_partial_values = 0); bool processChunk(const char *buf, unsigned int size, std::string *err); bool complete(std::string *err); diff --git a/src/request_body_processor/multipart.cc b/src/request_body_processor/multipart.cc index 3ae591671..3b53c7ab9 100644 --- a/src/request_body_processor/multipart.cc +++ b/src/request_body_processor/multipart.cc @@ -121,6 +121,7 @@ Multipart::Multipart(const std::string &header, Transaction *transaction) m_reserve{0}, m_seen_data(0), m_is_complete(0), + m_allow_partial(false), m_flag_error(0), m_flag_data_before(0), m_flag_data_after(0), @@ -1151,53 +1152,108 @@ int Multipart::multipart_complete(std::string *error) { * processed yet) in the buffer. */ if (m_buf_contains_line) { - if (((unsigned int)(MULTIPART_BUF_SIZE - m_bufleft) - == (4 + m_boundary.size())) + /* + * Note that the buffer may end with the final boundary followed by only CR, + * coming from the [CRLF epilogue], when allow_process_partial == 1 (which is + * set when SecRequestBodyLimitAction is ProcessPartial and the request body + * length exceeds SecRequestBodyLimit). + * + * The following definitions are copied from RFC 2046: + * + * dash-boundary := "--" boundary + * + * delimiter := CRLF dash-boundary + * + * close-delimiter := delimiter "--" + * + * multipart-body := [preamble CRLF] + * dash-boundary transport-padding CRLF + * body-part *encapsulation + * close-delimiter transport-padding + * [CRLF epilogue] + */ + unsigned int buf_data_len = (unsigned int)(MULTIPART_BUF_SIZE - m_bufleft); + if ((buf_data_len >= 2 + m_boundary.size()) && (*(m_buf) == '-') && (*(m_buf + 1) == '-') && (strncmp(m_buf + 2, m_boundary.c_str(), - m_boundary.size()) == 0) - && (*(m_buf + 2 + m_boundary.size()) == '-') - && (*(m_buf + 2 + m_boundary.size() + 1) == '-')) { - // these next two checks may result in repeating work from earlier in this fn - // ignore the duplication for now to minimize refactoring - if ((m_crlf_state_buf_end == 2) && (m_flag_lf_line != 1)) { - m_flag_lf_line = 1; - m_transaction->m_variableMultipartLFLine.set(std::to_string(m_flag_lf_line), - m_transaction->m_variableOffset); - m_transaction->m_variableMultipartCrlfLFLines.set(std::to_string(m_flag_crlf_line && m_flag_lf_line), - m_transaction->m_variableOffset); - if (m_flag_crlf_line && m_flag_lf_line) { - ms_dbg_a(m_transaction, 4, "Multipart: Warning: mixed line endings used (CRLF/LF)."); - } else if (m_flag_lf_line) { - ms_dbg_a(m_transaction, 4, "Multipart: Warning: incorrect line endings used (LF)."); + m_boundary.size()) == 0)) { + if ((buf_data_len >= 2 + m_boundary.size() + 2) + && (*(m_buf + 2 + m_boundary.size()) == '-') + && (*(m_buf + 2 + m_boundary.size() + 1) == '-')) { + /* If body fits in limit and ends with final boundary plus just CR, reject it. */ + if ( (m_allow_partial == 0) + && (buf_data_len == 2 + m_boundary.size() + 2 + 1) + && (*(m_buf + 2 + m_boundary.size() + 2) == '\r') ) { + ms_dbg_a(m_transaction, 1, + "Multipart: Invalid epilogue after final boundary."); + error->assign("Multipart: Invalid epilogue after final boundary."); + return false; + } + // these next two checks may result in repeating work from earlier in this fn + // ignore the duplication for now to minimize refactoring + if ((m_crlf_state_buf_end == 2) && (m_flag_lf_line != 1)) { + m_flag_lf_line = 1; + m_transaction->m_variableMultipartLFLine.set(std::to_string(m_flag_lf_line), + m_transaction->m_variableOffset); + m_transaction->m_variableMultipartCrlfLFLines.set(std::to_string(m_flag_crlf_line && m_flag_lf_line), + m_transaction->m_variableOffset); + if (m_flag_crlf_line && m_flag_lf_line) { + ms_dbg_a(m_transaction, 4, "Multipart: Warning: mixed line endings used (CRLF/LF)."); + } else if (m_flag_lf_line) { + ms_dbg_a(m_transaction, 4, "Multipart: Warning: incorrect line endings used (LF)."); + } + m_transaction->m_variableMultipartStrictError.set( + std::to_string(m_flag_lf_line) , m_transaction->m_variableOffset); + } + if ((m_mpp_substate_part_data_read == 0) && (m_flag_invalid_part != 1)) { + // it looks like the final boundary, but it's where part data should begin + m_flag_invalid_part = 1; + ms_dbg_a(m_transaction, 3, "Multipart: Invalid part (data contains final boundary)"); + m_transaction->m_variableMultipartStrictError.set( + std::to_string(m_flag_invalid_part) , m_transaction->m_variableOffset); + m_transaction->m_variableMultipartInvalidPart.set(std::to_string(m_flag_invalid_part), + m_transaction->m_variableOffset); + ms_dbg_a(m_transaction, 4, "Multipart: Warning: invalid part parsing."); } - m_transaction->m_variableMultipartStrictError.set( - std::to_string(m_flag_lf_line) , m_transaction->m_variableOffset); - } - if ((m_mpp_substate_part_data_read == 0) && (m_flag_invalid_part != 1)) { - // it looks like the final boundary, but it's where part data should begin - m_flag_invalid_part = 1; - ms_dbg_a(m_transaction, 3, "Multipart: Invalid part (data contains final boundary)"); - m_transaction->m_variableMultipartStrictError.set( - std::to_string(m_flag_invalid_part) , m_transaction->m_variableOffset); - m_transaction->m_variableMultipartInvalidPart.set(std::to_string(m_flag_invalid_part), - m_transaction->m_variableOffset); - ms_dbg_a(m_transaction, 4, "Multipart: Warning: invalid part parsing."); - } - /* Looks like the final boundary - process it. */ - if (process_boundary(1 /* final */) < 0) { - m_flag_error = 1; - return -1; - } + /* Looks like the final boundary - process it. */ + if (process_boundary(1 /* final */) < 0) { + m_flag_error = 1; + return -1; + } - /* The payload is complete after all. */ - m_is_complete = 1; + /* The payload is complete after all. */ + m_is_complete = 1; + } else if (m_allow_partial) { + if (buf_data_len >= 2 + m_boundary.size() + 1) { + if (*(m_buf + 2 + m_boundary.size()) == '-') { + if ((buf_data_len >= 2 + m_boundary.size() + 2) + && (*(m_buf + 2 + m_boundary.size() + 1) != '-')) { + ms_dbg_a(m_transaction, 1, + "Multipart: Invalid final boundary."); + error->assign("Multipart: Invalid final boundary."); + return false; + } + } else if ((*(m_buf + 2 + m_boundary.size()) != '\r') + || ((buf_data_len >= 2 + m_boundary.size() + 2) + && (*(m_buf + 2 + m_boundary.size() + 1) != '\n'))) { + ms_dbg_a(m_transaction, 1, + "Multipart: Invalid boundary."); + error->assign("Multipart: Invalid boundary."); + return false; + } + } + /* process it as a non-final boundary to avoid building a new part. */ + if (process_boundary(0) < 0) { + m_flag_error = 1; + return -1; + } + } } } - if (m_is_complete == 0) { + if (m_is_complete == 0 && !m_allow_partial) { ms_dbg_a(m_transaction, 1, "Multipart: Final boundary missing."); error->assign("Multipart: Final boundary missing."); @@ -1563,7 +1619,7 @@ bool Multipart::process(const std::string& data, std::string *error, m_boundary.size()) == 0)) { if (m_crlf_state_buf_end == 2) { m_flag_lf_line = 1; - } + } if ((m_mpp_substate_part_data_read == 0) && (m_boundary_count > 0)) { /* string matches our boundary, but it's where part data should begin */ m_flag_invalid_part = 1; diff --git a/src/request_body_processor/multipart.h b/src/request_body_processor/multipart.h index 08d4ffe92..35395148b 100644 --- a/src/request_body_processor/multipart.h +++ b/src/request_body_processor/multipart.h @@ -232,6 +232,7 @@ class Multipart { int m_seen_data; int m_is_complete; + bool m_allow_partial; int m_flag_error; int m_flag_data_before; diff --git a/src/request_body_processor/xml.cc b/src/request_body_processor/xml.cc index cbb7894c9..8cd169eb1 100644 --- a/src/request_body_processor/xml.cc +++ b/src/request_body_processor/xml.cc @@ -150,7 +150,7 @@ extern "C" { } XML::XML(Transaction *transaction) - : m_transaction(transaction) { + : m_transaction(transaction), m_require_well_formed(false) { m_data.doc = NULL; m_data.parsing_ctx = NULL; m_data.sax_handler = NULL; @@ -171,7 +171,8 @@ XML::~XML() { } } -bool XML::init() { +bool XML::init(bool require_well_formed) { + m_require_well_formed = require_well_formed; //xmlParserInputBufferCreateFilenameFunc entity; if (m_transaction->m_rules->m_secXMLExternalEntity == RulesSetProperties::TrueConfigBoolean) { @@ -280,7 +281,7 @@ bool XML::processChunk(const char *buf, unsigned int size, != RulesSetProperties::OnlyArgsConfigXMLParseXmlIntoArgs) { xmlParseChunk(m_data.parsing_ctx, buf, size, 0); m_data.xml_parser_state->parsing_ctx_arg = m_data.parsing_ctx_arg; - if (m_data.parsing_ctx->wellFormed != 1) { + if (m_require_well_formed && m_data.parsing_ctx->wellFormed != 1) { error->assign("XML: Failed to parse document."); ms_dbg_a(m_transaction, 4, "XML: Failed to parse document."); return false; @@ -296,7 +297,7 @@ bool XML::processChunk(const char *buf, unsigned int size, == RulesSetProperties::TrueConfigXMLParseXmlIntoArgs) ) { xmlParseChunk(m_data.parsing_ctx_arg, buf, size, 0); - if (m_data.parsing_ctx_arg->wellFormed != 1) { + if (m_require_well_formed && m_data.parsing_ctx_arg->wellFormed != 1) { error->assign("XML: Failed to parse document for ARGS."); ms_dbg_a(m_transaction, 4, "XML: Failed to parse document for ARGS."); return false; @@ -326,7 +327,7 @@ bool XML::complete(std::string *error) { ms_dbg_a(m_transaction, 4, "XML: Parsing complete (well_formed " \ + std::to_string(m_data.well_formed) + ")."); - if (m_data.well_formed != 1) { + if (m_require_well_formed && m_data.well_formed != 1) { error->assign("XML: Failed to parse document."); ms_dbg_a(m_transaction, 4, "XML: Failed to parse document."); return false; diff --git a/src/request_body_processor/xml.h b/src/request_body_processor/xml.h index df766d03b..b3618ed48 100644 --- a/src/request_body_processor/xml.h +++ b/src/request_body_processor/xml.h @@ -87,7 +87,7 @@ class XML { public: explicit XML(Transaction *transaction); ~XML(); - bool init(); + bool init(bool require_well_formed = true); bool processChunk(const char *buf, unsigned int size, std::string *err); bool complete(std::string *err); static xmlParserInputBufferPtr unloadExternalEntity(const char *URI, @@ -98,6 +98,7 @@ class XML { private: Transaction *m_transaction; std::string m_header; + bool m_require_well_formed; }; #endif diff --git a/src/transaction.cc b/src/transaction.cc index 6c8ae9744..5e356f683 100644 --- a/src/transaction.cc +++ b/src/transaction.cc @@ -150,6 +150,7 @@ Transaction::Transaction(ModSecurity *ms, RulesSet *rules, const char *id, m_secRuleEngine(RulesSetProperties::PropertyNotSetRuleEngine), m_secXMLParseXmlIntoArgs(rules->m_secXMLParseXmlIntoArgs), m_logCbData(logCbData), + m_requestBodyLimitExceeded(false), TransactionAnchoredVariables(this) { m_variableUrlEncodedError.set("0", 0); m_variableMscPcreError.set("0", 0); @@ -252,12 +253,15 @@ int Transaction::processConnection(const char *client, int cPort, bool Transaction::extractArguments(const std::string &orig, - const std::string& buf, size_t offset) { + const std::string& buf, size_t offset, bool partial_processing_enabled) { char sep1 = '&'; if (m_rules->m_secArgumentSeparator.m_set) { sep1 = m_rules->m_secArgumentSeparator.m_value.at(0); } - const auto key_value_sets = utils::string::ssplit(buf, sep1); + auto key_value_sets = utils::string::ssplit(buf, sep1); + if (partial_processing_enabled && (buf.empty() || buf.back() != sep1)) { + key_value_sets.pop_back(); + } for (const auto &t : key_value_sets) { const auto sep2 = '='; @@ -694,27 +698,36 @@ int Transaction::processRequestBody() { std::unique_ptr a = m_variableRequestHeaders.resolveFirst( "Content-Type"); + bool is_process_partial = (m_rules->m_requestBodyLimitAction + == RulesSet::BodyLimitAction::ProcessPartialBodyLimitAction); + bool requestBodyNoFilesLimitExceeded = false; if ((m_requestBodyType == WWWFormUrlEncoded) || (m_requestBodyProcessor == JSONRequestBody) || (m_requestBodyProcessor == XMLRequestBody)) { if ((m_rules->m_requestBodyNoFilesLimit.m_set) && (m_requestBody.str().size() > m_rules->m_requestBodyNoFilesLimit.m_value)) { - m_variableReqbodyError.set("1", 0); - m_variableReqbodyErrorMsg.set("Request body excluding files is bigger than the maximum expected.", 0); - m_variableInboundDataError.set("1", m_variableOffset); - ms_dbg(5, "Request body excluding files is bigger than the maximum expected. Limit: " \ - + std::to_string(m_rules->m_requestBodyNoFilesLimit.m_value)); + if (!is_process_partial) { + m_variableReqbodyError.set("1", 0); + m_variableReqbodyErrorMsg.set("Request body excluding files is bigger than the maximum expected.", 0); + m_variableInboundDataError.set("1", m_variableOffset); + ms_dbg(5, "Request body excluding files is bigger than the maximum expected. Limit: " \ + + std::to_string(m_rules->m_requestBodyNoFilesLimit.m_value)); + } requestBodyNoFilesLimitExceeded = true; - } + } } #ifdef WITH_LIBXML2 if (m_requestBodyProcessor == XMLRequestBody) { // large size might cause issues in the parsing itself; omit if exceeded - if (!requestBodyNoFilesLimitExceeded) { + if (!requestBodyNoFilesLimitExceeded || is_process_partial) { std::string error; - if (m_xml->init() == true) { + bool require_well_formed = !(is_process_partial && m_requestBodyLimitExceeded); + if (!require_well_formed) { + ms_dbg(4, "XML: Allow partial processing of request body"); + } + if (m_xml->init(require_well_formed) == true) { m_xml->processChunk(m_requestBody.str().c_str(), m_requestBody.str().size(), &error); @@ -740,12 +753,16 @@ int Transaction::processRequestBody() { if (m_requestBodyProcessor == JSONRequestBody) { #endif // large size might cause issues in the parsing itself; omit if exceeded - if (!requestBodyNoFilesLimitExceeded) { + if (!requestBodyNoFilesLimitExceeded || is_process_partial) { std::string error; if (m_rules->m_requestBodyJsonDepthLimit.m_set) { m_json->setMaxDepth(m_rules->m_requestBodyJsonDepthLimit.m_value); } - if (m_json->init() == true) { + unsigned int allow_partial_values = is_process_partial && m_requestBodyLimitExceeded; + if (allow_partial_values) { + ms_dbg(4, "JSON: Allow partial processing of request body"); + } + if (m_json->init(allow_partial_values) == true) { m_json->processChunk(m_requestBody.str().c_str(), m_requestBody.str().size(), &error); @@ -774,6 +791,10 @@ int Transaction::processRequestBody() { if (a != NULL) { Multipart m(*a, this); if (m.init(&error) == true) { + m.m_allow_partial = is_process_partial && m_requestBodyLimitExceeded; + if (m.m_allow_partial) { + ms_dbg(4, "Multipart: Allow partial processing of request body"); + } m.process(m_requestBody.str(), &error, m_variableOffset); } reqbodyNoFilesLength = m.m_reqbody_no_files_length; @@ -800,9 +821,10 @@ int Transaction::processRequestBody() { } else if (m_requestBodyType == WWWFormUrlEncoded) { m_variableOffset++; // large size might cause issues in the parsing itself; omit if exceeded - if (!requestBodyNoFilesLimitExceeded) { - extractArguments("POST", m_requestBody.str(), m_variableOffset); - } + if (!requestBodyNoFilesLimitExceeded || is_process_partial) { + bool partial_processing_enabled = is_process_partial && m_requestBodyLimitExceeded; + extractArguments("POST", m_requestBody.str(), m_variableOffset, partial_processing_enabled); + } } else if (m_requestBodyType != UnknownFormat) { /** * FIXME: double check to see if that is a valid scenario... @@ -935,6 +957,7 @@ int Transaction::appendRequestBody(const unsigned char *buf, size_t len) { if (this->m_rules->m_requestBodyLimit.m_value > 0 && this->m_rules->m_requestBodyLimit.m_value < len + current_size) { + m_requestBodyLimitExceeded = true; m_variableInboundDataError.set("1", m_variableOffset); ms_dbg(5, "Request body is bigger than the maximum expected."); diff --git a/test/common/modsecurity_test.cc b/test/common/modsecurity_test.cc index 23eed49e5..aa8cf9f59 100644 --- a/test/common/modsecurity_test.cc +++ b/test/common/modsecurity_test.cc @@ -157,6 +157,12 @@ void ModSecurityTest::cmd_options(int argc, char **argv) { if (std::getenv("AUTOMAKE_TESTS")) { m_automake_output = true; } + if (std::getenv("ALWAYS_SHOW_LOG")) { + m_always_show_log = true; + } + if (std::getenv("SHOW_REQUEST_BODY_SIZE")) { + m_show_request_body_size = true; + } if (argc > i && argv[i]) { this->target = argv[i]; diff --git a/test/common/modsecurity_test.h b/test/common/modsecurity_test.h index 6e8a3bbc8..99b958ad0 100644 --- a/test/common/modsecurity_test.h +++ b/test/common/modsecurity_test.h @@ -48,6 +48,8 @@ template class ModSecurityTest : bool m_test_multithreaded{false}; bool m_format{false}; bool m_update_content_length{false}; + bool m_always_show_log{false}; + bool m_show_request_body_size{false}; }; } // namespace modsecurity_test diff --git a/test/regression/regression.cc b/test/regression/regression.cc index 5b1ca514e..7101f39c8 100644 --- a/test/regression/regression.cc +++ b/test/regression/regression.cc @@ -297,6 +297,10 @@ void perform_unit_test(const ModSecurityTest &test, modsec_transaction.appendRequestBody( (unsigned char *)t->request_body.c_str(), t->request_body.size()); + if (test.m_show_request_body_size) { + std::cout << std::endl << "[Debug] appended request body size: " \ + << t->request_body.size() << std::endl; + } modsec_transaction.processRequestBody(); actions(&r, &modsec_transaction, &context.m_server_log); @@ -312,6 +316,7 @@ void perform_unit_test(const ModSecurityTest &test, modsec_transaction.appendResponseBody( (unsigned char *)t->response_body.c_str(), t->response_body.size()); + modsec_transaction.processResponseBody(); actions(&r, &modsec_transaction, &context.m_server_log); @@ -376,7 +381,7 @@ void perform_unit_test(const ModSecurityTest &test, testRes->passed = true; } - if (testRes->passed == false) { + if (!testRes->passed || test.m_always_show_log) { testRes->reason << std::endl; testRes->reason << KWHT << "Debug log:" << RESET << std::endl; testRes->reason << d->log_messages() << std::endl; @@ -537,6 +542,17 @@ int main(int argc, char **argv) std::cout << r->reason.str() << std::endl; } failed++; + } else if (test.m_always_show_log && r->passed) { + if (!test.m_automake_output) { + std::cout << KGRN << "Test passed." << RESET << KWHT \ + << " From: " \ + << RESET << r->test->filename << "." << std::endl; + std::cout << KWHT << "Test name: " << RESET \ + << r->test->name \ + << "." << std::endl; + std::cout << KWHT << "Logs: " << RESET << std::endl; + std::cout << r->reason.str() << std::endl; + } } delete r; } diff --git a/test/test-cases/regression/config-body_limits.json b/test/test-cases/regression/config-body_limits.json index e45dfce22..9aa2b5d2c 100644 --- a/test/test-cases/regression/config-body_limits.json +++ b/test/test-cases/regression/config-body_limits.json @@ -107,26 +107,26 @@ "User-Agent": "curl/7.38.0", "Accept": "*/*", "Content-Type": "multipart/form-data; boundary=------------------------756b6d74fa1a8ee2", - "Content-Length": "493" + "Content-Length": "508" }, "uri": "/?key=value&key=other_value", "method": "POST", "body": [ - "--------------------------756b6d74fa1a8ee2", - "Content-Disposition: form-data; name=\"name\"", - "", - "test", - "--------------------------756b6d74fa1a8ee2", - "Content-Disposition: form-data; name=\"filedata\"; filename=\"small_text_file.txt\"", - "Content-Type: text/plain", - "", - "This is a very small test file..", - "--------------------------756b6d74fa1a8ee2", - "Content-Disposition: form-data; name=\"filedata\"; filename=\"small_text_file.txt\"", - "Content-Type: text/plain", - "", - "This is another very small test file..", - "--------------------------756b6d74fa1a8ee2--" + "--------------------------756b6d74fa1a8ee2\n", + "Content-Disposition: form-data; name=\"name\"\n", + "\n", + "test\n", + "--------------------------756b6d74fa1a8ee2\n", + "Content-Disposition: form-data; name=\"filedata\"; filename=\"small_text_file.txt\"\n", + "Content-Type: text/plain\n", + "\n", + "This is a very small test file..\n", + "--------------------------756b6d74fa1a8ee2\n", + "Content-Disposition: form-data; name=\"filedata\"; filename=\"small_text_file.txt\"\n", + "Content-Type: text/plain\n", + "\n", + "This is another very small test file..\n", + "--------------------------756b6d74fa1a8ee2--\n" ] }, "response": { @@ -167,26 +167,26 @@ "User-Agent": "curl/7.38.0", "Accept": "*/*", "Content-Type": "multipart/form-data; boundary=------------------------756b6d74fa1a8ee2", - "Content-Length": "493" + "Content-Length": "508" }, "uri": "/?key=value&key=other_value", "method": "POST", "body": [ - "--------------------------756b6d74fa1a8ee2", - "Content-Disposition: form-data; name=\"name\"", - "", - "test", - "--------------------------756b6d74fa1a8ee2", - "Content-Disposition: form-data; name=\"filedata\"; filename=\"small_text_file.txt\"", - "Content-Type: text/plain", - "", - "This is a very small test file..", - "--------------------------756b6d74fa1a8ee2", - "Content-Disposition: form-data; name=\"filedata\"; filename=\"small_text_file.txt\"", - "Content-Type: text/plain", - "", - "This is another very small test file..", - "--------------------------756b6d74fa1a8ee2--" + "--------------------------756b6d74fa1a8ee2\n", + "Content-Disposition: form-data; name=\"name\"\n", + "\n", + "test\n", + "--------------------------756b6d74fa1a8ee2\n", + "Content-Disposition: form-data; name=\"filedata\"; filename=\"small_text_file.txt\"\n", + "Content-Type: text/plain\n", + "\n", + "This is a very small test file..\n", + "--------------------------756b6d74fa1a8ee2\n", + "Content-Disposition: form-data; name=\"filedata\"; filename=\"small_text_file.txt\"\n", + "Content-Type: text/plain\n", + "\n", + "This is another very small test file..\n", + "--------------------------756b6d74fa1a8ee2--\n" ] }, "response": { @@ -227,26 +227,26 @@ "User-Agent": "curl/7.38.0", "Accept": "*/*", "Content-Type": "multipart/form-data; boundary=------------------------756b6d74fa1a8ee2", - "Content-Length": "493" + "Content-Length": "508" }, "uri": "/?key=value&key=other_value", "method": "POST", "body": [ - "--------------------------756b6d74fa1a8ee2", - "Content-Disposition: form-data; name=\"name\"", - "", - "test", - "--------------------------756b6d74fa1a8ee2", - "Content-Disposition: form-data; name=\"filedata\"; filename=\"small_text_file.txt\"", - "Content-Type: text/plain", - "", - "This is a very small test file..", - "--------------------------756b6d74fa1a8ee2", - "Content-Disposition: form-data; name=\"filedata\"; filename=\"small_text_file.txt\"", - "Content-Type: text/plain", - "", - "This is another very small test file..", - "--------------------------756b6d74fa1a8ee2--" + "--------------------------756b6d74fa1a8ee2\n", + "Content-Disposition: form-data; name=\"name\"\n", + "\n", + "test\n", + "--------------------------756b6d74fa1a8ee2\n", + "Content-Disposition: form-data; name=\"filedata\"; filename=\"small_text_file.txt\"\n", + "Content-Type: text/plain\n", + "\n", + "This is a very small test file..\n", + "--------------------------756b6d74fa1a8ee2\n", + "Content-Disposition: form-data; name=\"filedata\"; filename=\"small_text_file.txt\"\n", + "Content-Type: text/plain\n", + "\n", + "This is another very small test file..\n", + "--------------------------756b6d74fa1a8ee2--\n" ] }, "response": { @@ -272,7 +272,7 @@ { "enabled": 1, "version_min": 300000, - "title": "SecRequestBodyLimitAction ProcessPartial", + "title": "SecRequestBodyLimitAction ProcessPartial - multipart", "client": { "ip": "200.249.12.31", "port": 123 @@ -285,27 +285,28 @@ "headers": { "Host": "localhost", "User-Agent": "curl/7.38.0", + "Accept": "*/*", "Content-Type": "multipart/form-data; boundary=------------------------756b6d74fa1a8ee2", - "Content-Length": "493" + "Content-Length": "508" }, "uri": "/?key=value&key=other_value", "method": "POST", "body": [ - "--------------------------756b6d74fa1a8ee2", - "Content-Disposition: form-data; name=\"name\"", - "", - "test", - "--------------------------756b6d74fa1a8ee2", - "Content-Disposition: form-data; name=\"filedata\"; filename=\"small_text_file.txt\"", - "Content-Type: text/plain", - "", - "This is a very small test file..", - "--------------------------756b6d74fa1a8ee2", - "Content-Disposition: form-data; name=\"filedata\"; filename=\"small_text_file.txt\"", - "Content-Type: text/plain", - "", - "This is another very small test file..", - "--------------------------756b6d74fa1a8ee2--" + "--------------------------756b6d74fa1a8ee2\n", + "Content-Disposition: form-data; name=\"name\"\n", + "\n", + "test\n", + "--------------------------756b6d74fa1a8ee2\n", + "Content-Disposition: form-data; name=\"filedata\"; filename=\"small_text_file.txt\"\n", + "Content-Type: text/plain\n", + "\n", + "This is a very small test file..\n", + "--------------------------756b6d74fa1a8ee2\n", + "Content-Disposition: form-data; name=\"filedata\"; filename=\"small_text_file.txt\"\n", + "Content-Type: text/plain\n", + "\n", + "This is another very small test file..\n", + "--------------------------756b6d74fa1a8ee2--\n" ] }, "response": { @@ -325,13 +326,13 @@ "rules": [ "SecRuleEngine On", "SecRequestBodyLimitAction ProcessPartial", - "SecRequestBodyLimit 5" + "SecRequestBodyLimit 508" ] }, { "enabled": 1, "version_min": 300000, - "title": "SecResponseBodyLimitAction Reject - Engine Disabled", + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/bad_name before limit", "client": { "ip": "200.249.12.31", "port": 123 @@ -345,12 +346,16 @@ "Host": "localhost", "User-Agent": "curl/7.38.0", "Accept": "*/*", - "Content-Length": "0" + "Content-Length": "60", + "Content-Type": "multipart/form-data; boundary=0000" }, - "uri": "/?key=value&key=other_value", - "method": "GET", + "uri": "/test.txt", + "method": "POST", "body": [ - "" + "--0000\r\n", + "Content-Disposition: form-data; name=\"bad_name\"\r\n", + "\r\n", + "a" ] }, "response": { @@ -365,18 +370,20 @@ ] }, "expected": { - "http_code": 200 + "http_code": 403 }, "rules": [ - "SecRuleEngine Off", - "SecResponseBodyLimitAction Reject", - "SecResponseBodyLimit 5" + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 59", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_NAME \"bad_name\" \"id:'200002',phase:2,t:none,deny" ] }, { "enabled": 1, "version_min": 300000, - "title": "SecResponseBodyLimitAction Reject - Engine Detection Only", + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/bad_name after limit", "client": { "ip": "200.249.12.31", "port": 123 @@ -390,12 +397,15 @@ "Host": "localhost", "User-Agent": "curl/7.38.0", "Accept": "*/*", - "Content-Length": "0" + "Content-Length": "59", + "Content-Type": "multipart/form-data; boundary=0000" }, - "uri": "/?key=value&key=other_value", - "method": "GET", + "uri": "/test.txt", + "method": "POST", "body": [ - "" + "--0000\r\n", + "Content-Disposition: form-data; name=\"bad_name\"\r\n", + "\r\n" ] }, "response": { @@ -413,15 +423,17 @@ "http_code": 200 }, "rules": [ - "SecRuleEngine DetectionOnly", - "SecResponseBodyLimitAction Reject", - "SecResponseBodyLimit 5" + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 58", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_NAME \"bad_name\" \"id:'200002',phase:2,t:none,deny" ] }, { "enabled": 1, "version_min": 300000, - "title": "SecRequestBodyNoFilesLimit - urlencoded, limit exceeded", + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/no epilogue", "client": { "ip": "200.249.12.31", "port": 123 @@ -435,13 +447,17 @@ "Host": "localhost", "User-Agent": "curl/7.38.0", "Accept": "*/*", - "Content-Length": "41", - "Content-Type": "application/x-www-form-urlencoded" + "Content-Length": "176", + "Content-Type": "multipart/form-data; boundary=---------------------------69343412719991675451336310646" }, - "uri": "/", + "uri": "/test.txt", "method": "POST", "body": [ - "param1=value1¶m2=value2¶m3=value3" + "-----------------------------69343412719991675451336310646\r\n", + "Content-Disposition: form-data; name=\"name1\"\r\n", + "\r\n", + "value1\r\n", + "-----------------------------69343412719991675451336310646--" ] }, "response": { @@ -456,20 +472,19 @@ ] }, "expected": { - "debug_log": "Request body excluding files is bigger than the maximum expected.", - "http_code": 400 + "http_code": 200 }, "rules": [ "SecRuleEngine On", - "SecRequestBodyAccess On", - "SecRequestBodyNoFilesLimit 20", - "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 176", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" ] }, { "enabled": 1, "version_min": 300000, - "title": "SecRequestBodyNoFilesLimit - urlencoded, limit not exceeded", + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CR after limit", "client": { "ip": "200.249.12.31", "port": 123 @@ -483,13 +498,17 @@ "Host": "localhost", "User-Agent": "curl/7.38.0", "Accept": "*/*", - "Content-Length": "41", - "Content-Type": "application/x-www-form-urlencoded" + "Content-Length": "177", + "Content-Type": "multipart/form-data; boundary=---------------------------69343412719991675451336310646" }, - "uri": "/", + "uri": "/test.txt", "method": "POST", "body": [ - "param1=value1¶m2=value2¶m3=value3" + "-----------------------------69343412719991675451336310646\r\n", + "Content-Disposition: form-data; name=\"name1\"\r\n", + "\r\n", + "value1\r\n", + "-----------------------------69343412719991675451336310646--\r" ] }, "response": { @@ -508,15 +527,15 @@ }, "rules": [ "SecRuleEngine On", - "SecRequestBodyAccess On", - "SecRequestBodyNoFilesLimit 60", - "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 176", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" ] }, { "enabled": 1, "version_min": 300000, - "title": "SecRequestBodyNoFilesLimit - json, limit exceeded", + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CR just in limit", "client": { "ip": "200.249.12.31", "port": 123 @@ -530,13 +549,17 @@ "Host": "localhost", "User-Agent": "curl/7.38.0", "Accept": "*/*", - "Content-Length": "48", - "Content-Type": "application/json" + "Content-Length": "177", + "Content-Type": "multipart/form-data; boundary=---------------------------69343412719991675451336310646" }, - "uri": "/", + "uri": "/test.txt", "method": "POST", "body": [ - "{\"param1\":{\"param2\":\"value2\",\"param3\":\"value3\"}}" + "-----------------------------69343412719991675451336310646\r\n", + "Content-Disposition: form-data; name=\"name1\"\r\n", + "\r\n", + "value1\r\n", + "-----------------------------69343412719991675451336310646--\r" ] }, "response": { @@ -551,21 +574,19 @@ ] }, "expected": { - "debug_log": "Request body excluding files is bigger than the maximum expected.", "http_code": 400 }, "rules": [ "SecRuleEngine On", - "SecRequestBodyAccess On", - "SecRequestBodyNoFilesLimit 20", - "SecRule REQUEST_HEADERS:Content-Type \"application/json\" \"id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON\"", - "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 177", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" ] }, { "enabled": 1, "version_min": 300000, - "title": "SecRequestBodyNoFilesLimit - json, limit not exceeded", + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CRLF across limit", "client": { "ip": "200.249.12.31", "port": 123 @@ -579,13 +600,17 @@ "Host": "localhost", "User-Agent": "curl/7.38.0", "Accept": "*/*", - "Content-Length": "48", - "Content-Type": "application/json" + "Content-Length": "178", + "Content-Type": "multipart/form-data; boundary=---------------------------69343412719991675451336310646" }, - "uri": "/", + "uri": "/test.txt", "method": "POST", "body": [ - "{\"param1\":{\"param2\":\"value2\",\"param3\":\"value3\"}}" + "-----------------------------69343412719991675451336310646\r\n", + "Content-Disposition: form-data; name=\"name1\"\r\n", + "\r\n", + "value1\r\n", + "-----------------------------69343412719991675451336310646--\r\n" ] }, "response": { @@ -604,17 +629,15 @@ }, "rules": [ "SecRuleEngine On", - "SecRequestBodyAccess On", - "SecRequestBodyNoFilesLimit 80", - "SecRule REQUEST_HEADERS:Content-Type \"application/json\" \"id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON\"", - "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 177", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" ] }, { "enabled": 1, "version_min": 300000, - "title": "SecRequestBodyNoFilesLimit - xml, limit exceeded", - "resource": "libxml2", + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CR before limit, non-LF after", "client": { "ip": "200.249.12.31", "port": 123 @@ -628,13 +651,17 @@ "Host": "localhost", "User-Agent": "curl/7.38.0", "Accept": "*/*", - "Content-Length": "77", - "Content-Type": "application/xml" + "Content-Length": "203", + "Content-Type": "multipart/form-data; boundary=---------------------------69343412719991675451336310646" }, - "uri": "/", + "uri": "/test.txt", "method": "POST", "body": [ - "ccceee" + "-----------------------------69343412719991675451336310646\r\n", + "Content-Disposition: form-data; name=\"name1\"\r\n", + "\r\n", + "value1\r\n", + "-----------------------------69343412719991675451336310646--\rbad epilogue after just CR" ] }, "response": { @@ -649,22 +676,19 @@ ] }, "expected": { - "debug_log": "Request body excluding files is bigger than the maximum expected.", - "http_code": 400 + "http_code": 200 }, "rules": [ "SecRuleEngine On", - "SecRequestBodyAccess On", - "SecRequestBodyNoFilesLimit 20", - "SecRule REQUEST_HEADERS:Content-Type \"(?:application(?:/soap\\+|/)|text/)xml\" \"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML\"", - "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 177", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" ] }, { "enabled": 1, "version_min": 300000, - "title": "SecRequestBodyNoFilesLimit - xml, limit not exceeded", - "resource": "libxml2", + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/empty epilogue just in limit", "client": { "ip": "200.249.12.31", "port": 123 @@ -678,13 +702,17 @@ "Host": "localhost", "User-Agent": "curl/7.38.0", "Accept": "*/*", - "Content-Length": "77", - "Content-Type": "application/xml" + "Content-Length": "178", + "Content-Type": "multipart/form-data; boundary=---------------------------69343412719991675451336310646" }, - "uri": "/", + "uri": "/test.txt", "method": "POST", "body": [ - "ccceee" + "-----------------------------69343412719991675451336310646\r\n", + "Content-Disposition: form-data; name=\"name1\"\r\n", + "\r\n", + "value1\r\n", + "-----------------------------69343412719991675451336310646--\r\n" ] }, "response": { @@ -703,16 +731,15 @@ }, "rules": [ "SecRuleEngine On", - "SecRequestBodyAccess On", - "SecRequestBodyNoFilesLimit 90", - "SecRule REQUEST_HEADERS:Content-Type \"(?:application(?:/soap\\+|/)|text/)xml\" \"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML\"", - "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 178", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" ] }, { "enabled": 1, "version_min": 300000, - "title": "SecRequestBodyNoFilesLimit - multipart, limit exceeded", + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CRLF/partial/bad-header in part across limit #1", "client": { "ip": "200.249.12.31", "port": 123 @@ -726,21 +753,18 @@ "Host": "localhost", "User-Agent": "curl/7.38.0", "Accept": "*/*", - "Content-Length": "198", - "Content-Type": "multipart/form-data; boundary=0000" + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "115" }, - "uri": "/", + "uri": "/test.txt", "method": "POST", "body": [ - "--0000\n", - "Content-Disposition: form-data; name=\"a\"\n", - "\n", - "1\n", - "--0000\n", - "Content-Disposition: form-data; name=\"b\"; filename=\"c.txt\"\n", - "\n", - "2222222222222222222222222222222222222222222222222222222222222222222222\n", - "--0000--\n" + "--0000\r\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\r\n", + "Content-Type: bad_type\r\n", + "\r\n", + "value\r\n", + "--000X" ] }, "response": { @@ -755,20 +779,21 @@ ] }, "expected": { - "debug_log": "Request body excluding files is bigger than the maximum expected.", - "http_code": 400 + "debug_log": "Appending request body: 115 bytes. Limit set to: 114", + "http_code": 200 }, "rules": [ "SecRuleEngine On", - "SecRequestBodyAccess On", - "SecRequestBodyNoFilesLimit 80", - "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 114", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS:name1 \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" ] }, { "enabled": 1, "version_min": 300000, - "title": "SecRequestBodyNoFilesLimit - multipart, limit not exceeded", + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CRLF/partial/bad-header in part before limit #1", "client": { "ip": "200.249.12.31", "port": 123 @@ -782,21 +807,18 @@ "Host": "localhost", "User-Agent": "curl/7.38.0", "Accept": "*/*", - "Content-Length": "198", - "Content-Type": "multipart/form-data; boundary=0000" + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "116" }, - "uri": "/", + "uri": "/test.txt", "method": "POST", "body": [ - "--0000\n", - "Content-Disposition: form-data; name=\"a\"\n", - "\n", - "1\n", - "--0000\n", - "Content-Disposition: form-data; name=\"b\"; filename=\"c.txt\"\n", - "\n", - "2222222222222222222222222222222222222222222222222222222222222222222222\n", - "--0000--\n" + "--0000\r\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\r\n", + "Content-Type: bad_type\r\n", + "\r\n", + "value\r\n", + "--0000X" ] }, "response": { @@ -811,13 +833,3005 @@ ] }, "expected": { - "http_code": 200 + "debug_log": "Appending request body: 116 bytes. Limit set to: 115", + "http_code": 403 }, "rules": [ "SecRuleEngine On", - "SecRequestBodyAccess On", - "SecRequestBodyNoFilesLimit 120", - "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 115", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS:name1 \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CRLF/partial/bad-header in part before limit #2", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "117" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\r\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\r\n", + "Content-Type: bad_type\r\n", + "\r\n", + "value\r\n", + "--0000\rX" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 117 bytes. Limit set to: 116", + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 116", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS:name1 \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CRLF/partial/bad-header in part before limit #3", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "118" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\r\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\r\n", + "Content-Type: bad_type\r\n", + "\r\n", + "value\r\n", + "--0000\r\n", + "X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 118 bytes. Limit set to: 117", + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 117", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS:name1 \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CRLF/partial/bad-header in part before limit #4", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "119" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\r\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\r\n", + "Content-Type: bad_type\r\n", + "\r\n", + "value\r\n", + "--0000\r\n", + "CX" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 119 bytes. Limit set to: 118", + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 118", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS:name1 \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CRLF/partial/bad-header in part before limit #5", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "161" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\r\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\r\n", + "Content-Type: bad_type\r\n", + "\r\n", + "value\r\n", + "--0000\r\n", + "Content-Disposition: form-data; name=\"name2X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 161 bytes. Limit set to: 160", + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 160", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS:name1 \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CRLF/partial/bad-header in final part across limit #1", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "117" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\r\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\r\n", + "Content-Type: bad_type\r\n", + "\r\n", + "value\r\n", + "--0000-X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 117 bytes. Limit set to: 116", + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 116", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS:name1 \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CRLF/partial/bad-header in final part before limit #1", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "118" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\r\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\r\n", + "Content-Type: bad_type\r\n", + "\r\n", + "value\r\n", + "--0000--X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 118 bytes. Limit set to: 117", + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 117", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CRLF/partial/bad-header in final part across limit #2", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "206" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\r\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\r\n", + "Content-Type: text/plain\r\n", + "\r\n", + "value\r\n", + "--0000\r\n", + "Content-Disposition: form-data; name=\"name2\"\r\n", + "Content-Type: bad_type\r\n", + "\r\n", + "value\r\n", + "--0000-X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 206 bytes. Limit set to: 205", + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 205", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CRLF/partial/bad-header in final part before limit #2", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "207" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\r\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\r\n", + "Content-Type: text/plain\r\n", + "\r\n", + "value\r\n", + "--0000\r\n", + "Content-Disposition: form-data; name=\"name2\"\r\n", + "Content-Type: bad_type\r\n", + "\r\n", + "value\r\n", + "--0000--X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 207 bytes. Limit set to: 206", + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 206", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CRLF/partial/invalid boundary before limit #1", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "119" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\r\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\r\n", + "Content-Type: text/plain\r\n", + "\r\n", + "value\r\n", + "--0000!X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 119 bytes. Limit set to: 118", + "error_log": "Multipart parsing error: Multipart: Invalid boundary.", + "http_code": 400 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 118", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CRLF/partial/invalid boundary before limit #2", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "120" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\r\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\r\n", + "Content-Type: text/plain\r\n", + "\r\n", + "value\r\n", + "--0000\r!X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 120 bytes. Limit set to: 119", + "error_log": "Multipart parsing error: Multipart: Invalid boundary.", + "http_code": 400 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 119", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CRLF/partial/invalid final boundary before limit #1", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "120" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\r\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\r\n", + "Content-Type: text/plain\r\n", + "\r\n", + "value\r\n", + "--0000-!X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 120 bytes. Limit set to: 119", + "error_log": "Multipart parsing error: Multipart: Invalid final boundary.", + "http_code": 400 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 119", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/LF/partial/bad-header in part across limit #1", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "110" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\n", + "Content-Type: bad_type\n", + "\n", + "value\n", + "--000X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 110 bytes. Limit set to: 109", + "http_code": 200 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 109", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS:name1 \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/LF/partial/bad-header in part before limit #1", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "111" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\n", + "Content-Type: bad_type\n", + "\n", + "value\n", + "--0000X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 111 bytes. Limit set to: 110", + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 110", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS:name1 \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/LF/partial/bad-header in part before limit #2", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "112" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\n", + "Content-Type: bad_type\n", + "\n", + "value\n", + "--0000\n", + "X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 112 bytes. Limit set to: 111", + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 111", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS:name1 \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/LF/partial/bad-header in part before limit #3", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "113" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\n", + "Content-Type: bad_type\n", + "\n", + "value\n", + "--0000\n", + "CX" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 113 bytes. Limit set to: 112", + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 112", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS:name1 \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/LF/partial/bad-header in part before limit #4", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "155" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\n", + "Content-Type: bad_type\n", + "\n", + "value\n", + "--0000\n", + "Content-Disposition: form-data; name=\"name2X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 155 bytes. Limit set to: 154", + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 154", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS:name1 \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/LF/partial/bad-header in final part across limit #1", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "112" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\n", + "Content-Type: bad_type\n", + "\n", + "value\n", + "--0000-X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 112 bytes. Limit set to: 111", + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 111", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/LF/partial/bad-header in final part before limit #1", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "113" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\n", + "Content-Type: bad_type\n", + "\n", + "value\n", + "--0000--X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 113 bytes. Limit set to: 112", + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 112", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/LF/partial/bad-header in final part across limit #2", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "196" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\n", + "Content-Type: text/plain\n", + "\n", + "value\n", + "--0000\n", + "Content-Disposition: form-data; name=\"name2\"\n", + "Content-Type: bad_type\n", + "\n", + "value\n", + "--0000-X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 196 bytes. Limit set to: 195", + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 195", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/LF/partial/bad-header in final part before limit #2", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "197" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\n", + "Content-Type: text/plain\n", + "\n", + "value\n", + "--0000\n", + "Content-Disposition: form-data; name=\"name2\"\n", + "Content-Type: bad_type\n", + "\n", + "value\n", + "--0000--X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 197 bytes. Limit set to: 196", + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 196", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule MULTIPART_PART_HEADERS \"content-type:.*bad_type\" \"id:'200002',phase:2,t:none,t:lowercase,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/LF/partial/invalid boundary before limit #1", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "114" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\n", + "Content-Type: text/plain\n", + "\n", + "value\n", + "--0000!X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 114 bytes. Limit set to: 113", + "error_log": "Multipart parsing error: Multipart: Invalid boundary.", + "http_code": 400 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 113", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/LF/partial/invalid final boundary before limit #1", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "multipart/form-data; boundary=0000", + "Content-Length": "115" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "--0000\n", + "Content-Disposition: form-data; name=\"name1\"; filename=\"name1.txt\"\n", + "Content-Type: text/plain\n", + "\n", + "value\n", + "--0000-!X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Appending request body: 115 bytes. Limit set to: 114", + "error_log": "Multipart parsing error: Multipart: Invalid final boundary.", + "http_code": 400 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 114", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - url-encoded/entire/bad_name without value", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "8", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "bad_name" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 8", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule ARGS_NAMES \"bad_name\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - url-encoded/partial/bad_name without value without delimeter before limit", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "9", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "bad_nameX" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 200 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 8", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule ARGS_NAMES \"bad_name\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - url-encoded/partial/bad_name without value with delimiter before limit", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "10", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "bad_name&X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 9", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule ARGS_NAMES \"bad_name\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - url-encoded/entire/bad_name with value", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "10", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "bad_name=1" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 10", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule ARGS_NAMES \"bad_name\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - url-encoded/partial/bad_name with value without delimeter before limit", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "11", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "bad_name=1X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 200 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 10", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule ARGS_NAMES \"bad_name\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - url-encoded/partial/bad_name with value with delimiter before limit", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "12", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "bad_name=1&X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 11", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule ARGS_NAMES \"bad_name\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - url-encoded/entire/bad_value", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "11", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "a=bad_value" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 11", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule ARGS \"bad_value\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - url-encoded/partial/bad_value without delimeter before limit", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "12", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "a=bad_valueX" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 200 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 11", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule ARGS \"bad_value\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - url-encoded/partial/bad_value with delimeter before limit", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "13", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "a=bad_value&X" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 12", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule ARGS \"bad_value\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - json/bad_name after limit", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "application/json", + "Content-Length": "14" + }, + "uri": "/", + "method": "POST", + "body": [ + "{\"bad_name\":1}" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 200 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 12", + "SecRule REQUEST_HEADERS:Content-Type \"application/json\" \"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON\"", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule ARGS_NAMES \"bad_name\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - json/bad_name before limit", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "application/json", + "Content-Length": "14" + }, + "uri": "/", + "method": "POST", + "body": [ + "{\"bad_name\":1}" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 13", + "SecRule REQUEST_HEADERS:Content-Type \"application/json\" \"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON\"", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule ARGS_NAMES \"bad_name\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - json/bad_value after limit", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "application/json", + "Content-Length": "17" + }, + "uri": "/", + "method": "POST", + "body": [ + "{\"a\":\"bad_value\"}" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 200 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 15", + "SecRule REQUEST_HEADERS:Content-Type \"application/json\" \"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON\"", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule ARGS \"bad_value\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - json/bad_value before limit", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "application/json", + "Content-Length": "17" + }, + "uri": "/", + "method": "POST", + "body": [ + "{\"a\":\"bad_value\"}" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 16", + "SecRule REQUEST_HEADERS:Content-Type \"application/json\" \"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON\"", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule ARGS \"bad_value\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - json/ill-formed after limit", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "application/json", + "Content-Length": "18" + }, + "uri": "/", + "method": "POST", + "body": [ + "{\"a\":\"bad_value\"}}" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 17", + "SecRule REQUEST_HEADERS:Content-Type \"application/json\" \"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON\"", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule ARGS \"bad_value\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - json/ill-formed before limit", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "application/json", + "Content-Length": "18" + }, + "uri": "/", + "method": "POST", + "body": [ + "{\"a\":\"bad_value\"}}" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 400 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 18", + "SecRule REQUEST_HEADERS:Content-Type \"application/json\" \"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON\"", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule ARGS \"bad_value\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - xml/bad_value after limit", + "resource": "libxml2", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "application/xml", + "Content-Length": "16" + }, + "uri": "/", + "method": "POST", + "body": [ + "bad_value" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 200 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 11", + "SecRule REQUEST_HEADERS:Content-Type \"(?:application(?:/soap\\+|/)|text/)xml\" \"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML\"", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule XML:/* \"bad_value\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - xml/bad_value before limit", + "resource": "libxml2", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "application/xml", + "Content-Length": "16" + }, + "uri": "/", + "method": "POST", + "body": [ + "bad_value" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 12", + "SecRule REQUEST_HEADERS:Content-Type \"(?:application(?:/soap\\+|/)|text/)xml\" \"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML\"", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule XML:/* \"bad_value\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - xml/ill-formed after limit", + "resource": "libxml2", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "application/xml", + "Content-Length": "20" + }, + "uri": "/", + "method": "POST", + "body": [ + "bad_value" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 19", + "SecRule REQUEST_HEADERS:Content-Type \"(?:application(?:/soap\\+|/)|text/)xml\" \"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML\"", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule XML:/* \"bad_value\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - xml/ill-formed before limit", + "resource": "libxml2", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Type": "application/xml", + "Content-Length": "20" + }, + "uri": "/", + "method": "POST", + "body": [ + "bad_value" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 400 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 20", + "SecRule REQUEST_HEADERS:Content-Type \"(?:application(?:/soap\\+|/)|text/)xml\" \"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML\"", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"", + "SecRule XML:/* \"bad_value\" \"id:'200002',phase:2,t:none,deny" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - RequestBodyNoFilesLimit ignored", + "resource": "libxml2", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "12", + "Content-Type": "application/xml" + }, + "uri": "/", + "method": "POST", + "body": [ + "\n" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 200 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyAccess On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 7", + "SecRequestBodyNoFilesLimit 3", + "SecRule REQUEST_HEADERS:Content-Type \"(?:application(?:/soap\\+|/)|text/)xml\" \"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML\"", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecResponseBodyLimitAction Reject - Engine Disabled", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "0" + }, + "uri": "/?key=value&key=other_value", + "method": "GET", + "body": [ + "" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 200 + }, + "rules": [ + "SecRuleEngine Off", + "SecResponseBodyLimitAction Reject", + "SecResponseBodyLimit 5" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecResponseBodyLimitAction Reject - Engine Detection Only", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "0" + }, + "uri": "/?key=value&key=other_value", + "method": "GET", + "body": [ + "" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 200 + }, + "rules": [ + "SecRuleEngine DetectionOnly", + "SecResponseBodyLimitAction Reject", + "SecResponseBodyLimit 5" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyNoFilesLimit - urlencoded, limit exceeded", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "42", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri": "/", + "method": "POST", + "body": [ + "param1=value1¶m2=value2¶m3=value3\n" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Request body excluding files is bigger than the maximum expected.", + "http_code": 400 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyAccess On", + "SecRequestBodyNoFilesLimit 20", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyNoFilesLimit - urlencoded, limit not exceeded", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "42", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri": "/", + "method": "POST", + "body": [ + "param1=value1¶m2=value2¶m3=value3\n" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 200 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyAccess On", + "SecRequestBodyNoFilesLimit 60", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyNoFilesLimit - json, limit exceeded", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "49", + "Content-Type": "application/json" + }, + "uri": "/", + "method": "POST", + "body": [ + "{\"param1\":{\"param2\":\"value2\",\"param3\":\"value3\"}}\n" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Request body excluding files is bigger than the maximum expected.", + "http_code": 400 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyAccess On", + "SecRequestBodyNoFilesLimit 20", + "SecRule REQUEST_HEADERS:Content-Type \"application/json\" \"id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON\"", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyNoFilesLimit - json, limit not exceeded", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "49", + "Content-Type": "application/json" + }, + "uri": "/", + "method": "POST", + "body": [ + "{\"param1\":{\"param2\":\"value2\",\"param3\":\"value3\"}}\n" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 200 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyAccess On", + "SecRequestBodyNoFilesLimit 80", + "SecRule REQUEST_HEADERS:Content-Type \"application/json\" \"id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON\"", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyNoFilesLimit - xml, limit exceeded", + "resource": "libxml2", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "78", + "Content-Type": "application/xml" + }, + "uri": "/", + "method": "POST", + "body": [ + "ccceee\n" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Request body excluding files is bigger than the maximum expected.", + "http_code": 400 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyAccess On", + "SecRequestBodyNoFilesLimit 20", + "SecRule REQUEST_HEADERS:Content-Type \"(?:application(?:/soap\\+|/)|text/)xml\" \"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML\"", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyNoFilesLimit - xml, limit not exceeded", + "resource": "libxml2", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "78", + "Content-Type": "application/xml" + }, + "uri": "/", + "method": "POST", + "body": [ + "ccceee\n" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 200 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyAccess On", + "SecRequestBodyNoFilesLimit 90", + "SecRule REQUEST_HEADERS:Content-Type \"(?:application(?:/soap\\+|/)|text/)xml\" \"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML\"", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyNoFilesLimit - multipart, limit exceeded", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "198", + "Content-Type": "multipart/form-data; boundary=0000" + }, + "uri": "/", + "method": "POST", + "body": [ + "--0000\n", + "Content-Disposition: form-data; name=\"a\"\n", + "\n", + "1\n", + "--0000\n", + "Content-Disposition: form-data; name=\"b\"; filename=\"c.txt\"\n", + "\n", + "2222222222222222222222222222222222222222222222222222222222222222222222\n", + "--0000--\n" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Request body excluding files is bigger than the maximum expected.", + "http_code": 400 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyAccess On", + "SecRequestBodyNoFilesLimit 80", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyNoFilesLimit - multipart, limit not exceeded", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "198", + "Content-Type": "multipart/form-data; boundary=0000" + }, + "uri": "/", + "method": "POST", + "body": [ + "--0000\n", + "Content-Disposition: form-data; name=\"a\"\n", + "\n", + "1\n", + "--0000\n", + "Content-Disposition: form-data; name=\"b\"; filename=\"c.txt\"\n", + "\n", + "2222222222222222222222222222222222222222222222222222222222222222222222\n", + "--0000--\n" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 200 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyAccess On", + "SecRequestBodyNoFilesLimit 120", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/no epilogue", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "176", + "Content-Type": "multipart/form-data; boundary=---------------------------69343412719991675451336310646" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "-----------------------------69343412719991675451336310646\r\n", + "Content-Disposition: form-data; name=\"name1\"\r\n", + "\r\n", + "value1\r\n", + "-----------------------------69343412719991675451336310646--" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 200 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 176", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CR after limit", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "177", + "Content-Type": "multipart/form-data; boundary=---------------------------69343412719991675451336310646" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "-----------------------------69343412719991675451336310646\r\n", + "Content-Disposition: form-data; name=\"name1\"\r\n", + "\r\n", + "value1\r\n", + "-----------------------------69343412719991675451336310646--\r" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 200 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 176", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CR just in limit", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "177", + "Content-Type": "multipart/form-data; boundary=---------------------------69343412719991675451336310646" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "-----------------------------69343412719991675451336310646\r\n", + "Content-Disposition: form-data; name=\"name1\"\r\n", + "\r\n", + "value1\r\n", + "-----------------------------69343412719991675451336310646--\r" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "error_log": "Multipart: Invalid epilogue after final boundary.", + "http_code": 400 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 177", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CRLF across limit", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "178", + "Content-Type": "multipart/form-data; boundary=---------------------------69343412719991675451336310646" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "-----------------------------69343412719991675451336310646\r\n", + "Content-Disposition: form-data; name=\"name1\"\r\n", + "\r\n", + "value1\r\n", + "-----------------------------69343412719991675451336310646--\r\n" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 200 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 177", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/CR before limit, non-LF after", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "203", + "Content-Type": "multipart/form-data; boundary=---------------------------69343412719991675451336310646" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "-----------------------------69343412719991675451336310646\r\n", + "Content-Disposition: form-data; name=\"name1\"\r\n", + "\r\n", + "value1\r\n", + "-----------------------------69343412719991675451336310646--\rbad epilogue after just CR" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 200 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 177", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "SecRequestBodyLimitAction ProcessPartial - multipart/empty epilogue just in limit", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "178", + "Content-Type": "multipart/form-data; boundary=---------------------------69343412719991675451336310646" + }, + "uri": "/test.txt", + "method": "POST", + "body": [ + "-----------------------------69343412719991675451336310646\r\n", + "Content-Disposition: form-data; name=\"name1\"\r\n", + "\r\n", + "value1\r\n", + "-----------------------------69343412719991675451336310646--\r\n" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "http_code": 200 + }, + "rules": [ + "SecRuleEngine On", + "SecRequestBodyLimitAction ProcessPartial", + "SecRequestBodyLimit 178", + "SecRule REQBODY_ERROR \"!@eq 0\" \"id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2\"" ] } ] diff --git a/test/test-cases/regression/issue-1576.json b/test/test-cases/regression/issue-1576.json index 17239727e..4671b2bad 100644 --- a/test/test-cases/regression/issue-1576.json +++ b/test/test-cases/regression/issue-1576.json @@ -25,17 +25,17 @@ "uri": "/?key=value&key=other_value", "method": "POST", "body": [ - "{", - " \"foo\":\"bar\",", - " \"mod\":\"sec\",", - " \"ops\": [", - " [\"um\", \"um e meio\"], ", - " \"dois\",", - " \"tres\",", - " { \"eins\": [\"zwei\", \"drei\"] }", - " ],", - " \"whee\": \"lhebs\"", - "}" + "{\n", + " \"foo\":\"bar\",\n", + " \"mod\":\"sec\",\n", + " \"ops\": [\n", + " [\"um\", \"um e meio\"], \n", + " \"dois\",\n", + " \"tres\",\n", + " { \"eins\": [\"zwei\", \"drei\"] }\n", + " ],\n", + " \"whee\": \"lhebs\"\n", + "}\n" ] }, "response": { @@ -83,11 +83,11 @@ "uri": "/?key=value&key=other_value", "method": "POST", "body": [ - "[", - " \"one\",", - " \"two\",", - " \"three\"", - "]" + "[\n", + " \"one\",\n", + " \"two\",\n", + " \"three\"\n", + "]\n" ] }, "response": { @@ -135,20 +135,20 @@ "uri": "/?key=value&key=other_value", "method": "POST", "body": [ - "{", - " \"foo\":\"bar\",", - " \"mod\":\"sec\",", - " \"ops\": {", - " \"um\": \"um e meio\", ", - " \"dois\": \"tres\",", - " \"quatro\": \"cinco\",", - " \"seis\": {", - " \"dez\": \"onze\",", - " \"doze\": \"treze\"", - " }", - " },", - " \"whee\": \"lhebs\"", - "}" + "{\n", + " \"foo\":\"bar\",\n", + " \"mod\":\"sec\",\n", + " \"ops\": {\n", + " \"um\": \"um e meio\", \n", + " \"dois\": \"tres\",\n", + " \"quatro\": \"cinco\",\n", + " \"seis\": {\n", + " \"dez\": \"onze\",\n", + " \"doze\": \"treze\"\n", + " }\n", + " },\n", + " \"whee\": \"lhebs\"\n", + "}\n" ] }, "response": { diff --git a/test/test-cases/regression/request-body-parser-json.json b/test/test-cases/regression/request-body-parser-json.json index f80a64d37..d8d64caea 100644 --- a/test/test-cases/regression/request-body-parser-json.json +++ b/test/test-cases/regression/request-body-parser-json.json @@ -23,10 +23,10 @@ "uri": "/?key=value&key=other_value", "method": "POST", "body": [ - "{", - " \"foo\":\"bar\",", - " \"mod\":\"sec\"", - "}" + "{\n", + " \"foo\":\"bar\",\n", + " \"mod\":\"sec\"\n", + "}\n" ] }, "response": { @@ -72,13 +72,13 @@ "uri": "/?key=value&key=other_value", "method": "POST", "body": [ - "{", - "\"first_level\":", - "{", - " \"first_key\":\"bar\",", - " \"second_key\":\"sec\"", - "}", - "}" + "{\n", + "\"first_level\":\n", + "{\n", + " \"first_key\":\"bar\",\n", + " \"second_key\":\"sec\"\n", + "}\n", + "}\n" ] }, "response": { @@ -170,7 +170,7 @@ "uri": "/?key=value&key=other_value", "method": "POST", "body": [ - "a" + "a\n" ] }, "response": { @@ -214,17 +214,17 @@ "uri": "/?foo=bar", "method": "POST", "body": [ - "{", - " \"key1\":", - "{", - " \"key2\":", - "{", - " \"key3\":", - "{", - " \"key4\":", - "{", - " \"key5\":\"thevalue\"", - "}}}}}" + "{\n", + " \"key1\":\n", + "{\n", + " \"key2\":\n", + "{\n", + " \"key3\":\n", + "{\n", + " \"key4\":\n", + "{\n", + " \"key5\":\"thevalue\"\n", + "}}}}}\n" ] }, "response": { @@ -268,17 +268,17 @@ "uri": "/?foo=bar", "method": "POST", "body": [ - "{", - " \"key1\":", - "{", - " \"key2\":", - "{", - " \"key3\":", - "{", - " \"key4\":", - "{", - " \"key5\":\"thevalue\"", - "}}}}}" + "{\n", + " \"key1\":\n", + "{\n", + " \"key2\":\n", + "{\n", + " \"key3\":\n", + "{\n", + " \"key4\":\n", + "{\n", + " \"key5\":\"thevalue\"\n", + "}}}}}\n" ] }, "response": { diff --git a/test/test-cases/regression/request-body-parser-xml-validade-dtd.json b/test/test-cases/regression/request-body-parser-xml-validade-dtd.json index 58e1f8689..dc7e9c43c 100644 --- a/test/test-cases/regression/request-body-parser-xml-validade-dtd.json +++ b/test/test-cases/regression/request-body-parser-xml-validade-dtd.json @@ -24,15 +24,15 @@ "uri": "/?key=value&key=other_value", "method": "POST", "body": [ - "", - " ", - " ", - " ", - " ", - " 12123", - " ", - " ", - " " + "\n", + " \n", + " \n", + " \n", + " \n", + " 12123\n", + " \n", + " \n", + " \n" ] }, "response": { @@ -80,15 +80,15 @@ "uri": "/?key=value&key=other_value", "method": "POST", "body": [ - "", - " ", - " ", - " ", - " ", - " 12123", - " ", - " ", - " " + "\n", + " \n", + " \n", + " \n", + " \n", + " 12123\n", + " \n", + " \n", + " \n" ] }, "response": { @@ -136,15 +136,15 @@ "uri": "/?key=value&key=other_value", "method": "POST", "body": [ - "", - "", - " ", - " ", - " ", - " 12123", - " ", - " ", - " " + "\n", + "\n", + " \n", + " \n", + " \n", + " 12123\n", + " \n", + " \n", + " \n" ] }, "response": { @@ -192,15 +192,15 @@ "uri": "/?key=value&key=other_value", "method": "POST", "body": [ - "", - " ", - " ", - " ", - " ", - " 12123", - " ", - " ", - " " + "\n", + " \n", + " \n", + " \n", + " \n", + " 12123\n", + " \n", + " \n", + " \n" ] }, "response": { diff --git a/test/test-cases/regression/secargumentslimit.json b/test/test-cases/regression/secargumentslimit.json index d763f4206..011f683d7 100644 --- a/test/test-cases/regression/secargumentslimit.json +++ b/test/test-cases/regression/secargumentslimit.json @@ -21,13 +21,13 @@ "uri": "/?foo=bar", "method": "POST", "body": [ - "{", - " \"k1\":\"v1\",", - " \"k2\":\"v2\",", - " \"k3\":\"v3\",", - " \"k4\":\"v4\",", - " \"k5\":\"v5\"", - "}" + "{\n", + " \"k1\":\"v1\",\n", + " \"k2\":\"v2\",\n", + " \"k3\":\"v3\",\n", + " \"k4\":\"v4\",\n", + " \"k5\":\"v5\"\n", + "}\n" ] }, "response": { @@ -72,13 +72,13 @@ "uri": "/?foo=bar", "method": "POST", "body": [ - "{", - " \"k1\":\"v1\",", - " \"k2\":\"v2\",", - " \"k3\":\"v3\",", - " \"k4\":\"v4\",", - " \"k5\":\"v5\"", - "}" + "{\n", + " \"k1\":\"v1\",\n", + " \"k2\":\"v2\",\n", + " \"k3\":\"v3\",\n", + " \"k4\":\"v4\",\n", + " \"k5\":\"v5\"\n", + "}\n" ] }, "response": {