diff --git a/headers/modsecurity/rules_properties.h b/headers/modsecurity/rules_properties.h
index faf9982c12..f66f1c93e5 100644
--- a/headers/modsecurity/rules_properties.h
+++ b/headers/modsecurity/rules_properties.h
@@ -385,6 +385,7 @@ class RulesProperties {
PropertyNotSetConfigBoolean);
to->m_requestBodyLimit.merge(&from->m_requestBodyLimit);
+ to->m_requestBodyNoFilesLimit.merge(&from->m_requestBodyNoFilesLimit);
to->m_responseBodyLimit.merge(&from->m_responseBodyLimit);
merge_bodylimitaction_value(to->m_requestBodyLimitAction,
diff --git a/src/transaction.cc b/src/transaction.cc
index cd71a0391c..ffb57965fa 100644
--- a/src/transaction.cc
+++ b/src/transaction.cc
@@ -785,6 +785,9 @@ int Transaction::processRequestBody() {
m_variableInboundDataError.set("0", 0);
}
+ // set the default reqBody length
+ // if CT *IS NOT* multipart, no_files value will the whole request length
+ size_t reqbodyNoFilesLength = m_requestBody.str().size();
/*
* Process the request body even if there is nothing to be done.
*
@@ -852,6 +855,9 @@ int Transaction::processRequestBody() {
if (m.init(&error) == true) {
m.process(m_requestBody.str(), &error, m_variableOffset);
}
+ // if CT *IS* multipart, no_files value will the m.m_reqbody_no_files_length
+ // calculated in request_body_processor/multipart.cc
+ reqbodyNoFilesLength = m.m_reqbody_no_files_length;
m.multipart_complete(&error);
}
if (error.empty() == false) {
@@ -907,6 +913,16 @@ int Transaction::processRequestBody() {
}
}
+ // check the SecRequestBodyNoFilsLimit criteria
+ if (this->m_rules->m_requestBodyNoFilesLimit.m_value > 0
+ && this->m_rules->m_requestBodyNoFilesLimit.m_value < reqbodyNoFilesLength) {
+ m_variableInboundDataError.set("1", m_variableOffset);
+ ms_dbg(5, "Request body no files is bigger than the maximum expected.");
+ m_variableReqbodyError.set("1", m_variableOffset);
+ m_variableReqbodyErrorMsg.set("Request body no files is bigger than the maximum expected.",
+ m_variableOffset);
+ }
+
/**
* FIXME: This variable should be calculated on demand, it is
* computationally intensive.
diff --git a/test/test-cases/regression/config-body_nofiles_limits.json b/test/test-cases/regression/config-body_nofiles_limits.json
new file mode 100644
index 0000000000..853eb807ee
--- /dev/null
+++ b/test/test-cases/regression/config-body_nofiles_limits.json
@@ -0,0 +1,508 @@
+[
+ {
+ "enabled":1,
+ "version_min":300000,
+ "title":"SecRequestBodyNoFilesLimit 10/01 - True positive form-data",
+ "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":"/",
+ "method":"POST",
+ "body": [
+ "login=foo"
+ ]
+ },
+ "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"
+ },
+ "body":[
+ "no need."
+ ]
+ },
+ "expected":{
+ "http_code":400
+ },
+ "rules":[
+ "SecRuleEngine On",
+ "SecRequestBodyAccess On",
+ "SecRequestBodyNoFilesLimit 5",
+ "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 10/02 - True negative form-data",
+ "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":"/",
+ "method":"POST",
+ "body": [
+ "login=foo"
+ ]
+ },
+ "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"
+ },
+ "body":[
+ "no need."
+ ]
+ },
+ "expected":{
+ "http_code":200
+ },
+ "rules":[
+ "SecRuleEngine On",
+ "SecRequestBodyAccess On",
+ "SecRequestBodyNoFilesLimit 10",
+ "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 10/03 - True positive multipart",
+ "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": "300",
+ "Content-Type": "multipart/form-data; boundary=------------------------305d3b6fb72cf618"
+ },
+ "uri":"/",
+ "method":"POST",
+ "body": [
+ "--------------------------305d3b6fb72cf618\r",
+ "Content-Disposition: form-data; name=\"token\"\r",
+ "\r",
+ "blah\r",
+ "--------------------------305d3b6fb72cf618\r",
+ "Content-Disposition: form-data; name=\"attachments\"; filename=\"simple_empty.txt\"\r",
+ "Content-Type: text/plain\r",
+ "\r",
+ "a\r",
+ "--------------------------305d3b6fb72cf618--\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"
+ },
+ "body":[
+ "no need."
+ ]
+ },
+ "expected":{
+ "http_code":400
+ },
+ "rules":[
+ "SecRuleEngine On",
+ "SecRequestBodyAccess On",
+ "SecRequestBodyNoFilesLimit 5",
+ "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 10/04 - True negative multipart",
+ "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": "300",
+ "Content-Type": "multipart/form-data; boundary=------------------------305d3b6fb72cf618"
+ },
+ "uri":"/",
+ "method":"POST",
+ "body": [
+ "--------------------------305d3b6fb72cf618\r",
+ "Content-Disposition: form-data; name=\"token\"\r",
+ "\r",
+ "blah\r",
+ "--------------------------305d3b6fb72cf618\r",
+ "Content-Disposition: form-data; name=\"attachments\"; filename=\"simple_empty.txt\"\r",
+ "Content-Type: text/plain\r",
+ "\r",
+ "a\r",
+ "--------------------------305d3b6fb72cf618--\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"
+ },
+ "body":[
+ "no need."
+ ]
+ },
+ "expected":{
+ "http_code":200
+ },
+ "rules":[
+ "SecRuleEngine On",
+ "SecRequestBodyAccess On",
+ "SecRequestBodyNoFilesLimit 161",
+ "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 10/05 - True positive multipart full req",
+ "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": "300",
+ "Content-Type": "multipart/form-data; boundary=------------------------305d3b6fb72cf618"
+ },
+ "uri":"/",
+ "method":"POST",
+ "body": [
+ "--------------------------305d3b6fb72cf618\r",
+ "Content-Disposition: form-data; name=\"token\"\r",
+ "\r",
+ "blah\r",
+ "--------------------------305d3b6fb72cf618\r",
+ "Content-Disposition: form-data; name=\"attachments\"; filename=\"simple_empty.txt\"\r",
+ "Content-Type: text/plain\r",
+ "\r",
+ "a\r",
+ "--------------------------305d3b6fb72cf618--\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"
+ },
+ "body":[
+ "no need."
+ ]
+ },
+ "expected":{
+ "http_code":403
+ },
+ "rules":[
+ "SecRuleEngine On",
+ "SecRequestBodyAccess On",
+ "SecRequestBodyLimitAction Reject",
+ "SecRequestBodyLimit 299",
+ "SecRequestBodyNoFilesLimit 161",
+ "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 10/06 - True negative multipart full req",
+ "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": "300",
+ "Content-Type": "multipart/form-data; boundary=------------------------305d3b6fb72cf618"
+ },
+ "uri":"/",
+ "method":"POST",
+ "body": [
+ "--------------------------305d3b6fb72cf618\r",
+ "Content-Disposition: form-data; name=\"token\"\r",
+ "\r",
+ "blah\r",
+ "--------------------------305d3b6fb72cf618\r",
+ "Content-Disposition: form-data; name=\"attachments\"; filename=\"simple_empty.txt\"\r",
+ "Content-Type: text/plain\r",
+ "\r",
+ "a\r",
+ "--------------------------305d3b6fb72cf618--\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"
+ },
+ "body":[
+ "no need."
+ ]
+ },
+ "expected":{
+ "http_code":200
+ },
+ "rules":[
+ "SecRuleEngine On",
+ "SecRequestBodyAccess On",
+ "SecRequestBodyLimitAction Reject",
+ "SecRequestBodyLimit 300",
+ "SecRequestBodyNoFilesLimit 161",
+ "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 10/07 - True positive JSON",
+ "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": "17",
+ "Content-Type": "application/json"
+ },
+ "uri":"/",
+ "method":"POST",
+ "body": [
+ "{\"login\": \"foo\"}"
+ ]
+ },
+ "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"
+ },
+ "body":[
+ "no need."
+ ]
+ },
+ "expected":{
+ "http_code":400
+ },
+ "rules":[
+ "SecRuleEngine On",
+ "SecRequestBodyAccess On",
+ "SecRequestBodyNoFilesLimit 5",
+ "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 10/08 - True negative JSON",
+ "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": "17",
+ "Content-Type": "application/x-www-form-urlencoded"
+ },
+ "uri":"/",
+ "method":"POST",
+ "body": [
+ "{\"login\": \"foo\"}"
+ ]
+ },
+ "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"
+ },
+ "body":[
+ "no need."
+ ]
+ },
+ "expected":{
+ "http_code":200
+ },
+ "rules":[
+ "SecRuleEngine On",
+ "SecRequestBodyAccess On",
+ "SecRequestBodyNoFilesLimit 17",
+ "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 10/09 - True positive XML",
+ "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": "95",
+ "Content-Type": "application/xml"
+ },
+ "uri":"/",
+ "method":"POST",
+ "body": [
+ "\r",
+ "\r",
+ " - data
\r",
+ "\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"
+ },
+ "body":[
+ "no need."
+ ]
+ },
+ "expected":{
+ "http_code":400
+ },
+ "rules":[
+ "SecRuleEngine On",
+ "SecRequestBodyAccess On",
+ "SecRequestBodyNoFilesLimit 5",
+ "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 10/10 - True negative XML",
+ "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": "95",
+ "Content-Type": "application/xml"
+ },
+ "uri":"/",
+ "method":"POST",
+ "body": [
+ "\r",
+ "\r",
+ " - data
\r",
+ "\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"
+ },
+ "body":[
+ "no need."
+ ]
+ },
+ "expected":{
+ "http_code":200
+ },
+ "rules":[
+ "SecRuleEngine On",
+ "SecRequestBodyAccess On",
+ "SecRequestBodyNoFilesLimit 95",
+ "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\""
+ ]
+ }
+]