diff --git a/headers/modsecurity/assay.h b/headers/modsecurity/assay.h index abdfc984..c7abe9a5 100644 --- a/headers/modsecurity/assay.h +++ b/headers/modsecurity/assay.h @@ -170,6 +170,7 @@ class Assay { void store_variable(std::string, const std::string &value); bool update_variable_first(std::string var, const std::string &value); + void delete_variable(std::string key); ModSecurityStringVariables m_variables_strings; diff --git a/src/assay.cc b/src/assay.cc index e753127d..3ed2a26a 100644 --- a/src/assay.cc +++ b/src/assay.cc @@ -1139,6 +1139,10 @@ bool Assay::update_variable_first(std::string var, const std::string &value) { return false; } +void Assay::delete_variable(std::string key) { + this->m_variables_strings.erase(key); +} + std::list> Assay::resolve_variable(std::string var) { diff --git a/src/parser/seclang-scanner.ll b/src/parser/seclang-scanner.ll index 30dc0a39..87ba3966 100755 --- a/src/parser/seclang-scanner.ll +++ b/src/parser/seclang-scanner.ll @@ -59,7 +59,7 @@ OPERATORNOARG (?i:@detectSQLi|@detectXSS|@geoLookup|@validateUrlEncoding|@valida TRANSFORMATION t:(lowercase|urlDecodeUni|urlDecode|none|compressWhitespace|removeWhitespace|replaceNulls|removeNulls|htmlEntityDecode|jsDecode|cssDecode|trim) -VARIABLE (?i:MATCHED_VAR|INBOUND_DATA_ERROR|FULL_REQUEST|FILES|AUTH_TYPE|ARGS_NAMES|ARGS|QUERY_STRING|REMOTE_ADDR|REQUEST_BASENAME|REQUEST_BODY|REQUEST_COOKIES_NAMES|REQUEST_COOKIES|REQUEST_FILENAME|REQUEST_HEADERS_NAMES|REQUEST_HEADERS|REQUEST_METHOD|REQUEST_PROTOCOL|REQUEST_URI|RESPONSE_BODY|RESPONSE_CONTENT_LENGTH|RESPONSE_CONTENT_TYPE|RESPONSE_HEADERS_NAMES|RESPONSE_HEADERS|RESPONSE_PROTOCOL|RESPONSE_STATUS|TX|GEO) +VARIABLE (?i:MATCHED_VAR|MATCHED_VARS|INBOUND_DATA_ERROR|FULL_REQUEST|FILES|AUTH_TYPE|ARGS_NAMES|ARGS|QUERY_STRING|REMOTE_ADDR|REQUEST_BASENAME|REQUEST_BODY|REQUEST_COOKIES_NAMES|REQUEST_COOKIES|REQUEST_FILENAME|REQUEST_HEADERS_NAMES|REQUEST_HEADERS|REQUEST_METHOD|REQUEST_PROTOCOL|REQUEST_URI|RESPONSE_BODY|RESPONSE_CONTENT_LENGTH|RESPONSE_CONTENT_TYPE|RESPONSE_HEADERS_NAMES|RESPONSE_HEADERS|RESPONSE_PROTOCOL|RESPONSE_STATUS|TX|GEO) RUN_TIME_VAR_DUR (?i:DURATION) RUN_TIME_VAR_ENV (?i:ENV) RUN_TIME_VAR_BLD (?i:MODSEC_BUILD) diff --git a/src/rule.cc b/src/rule.cc index f0918f29..51b19af6 100644 --- a/src/rule.cc +++ b/src/rule.cc @@ -113,15 +113,19 @@ bool Rule::evaluate(Assay *assay) { assay->debug(4, "Running action: " + a->action); a->evaluate(assay); } - + if (this->chained && this->chainedRule == NULL) { + assay->debug(4, "Rule is marked as chained but there isn't a subsequent rule."); + } if (this->chained && this->chainedRule != NULL) { assay->debug(4, "Executing chained rule."); if (assay->update_variable_first("MATCHED_VAR", value) == false) { assay->store_variable("MATCHED_VAR", value); } + assay->store_variable("MATCHED_VARS:" + v.first, value); this->chainedRule->evaluate(assay); assay->update_variable_first("MATCHED_VAR", ""); + assay->delete_variable("MATCHED_VARS:" + v.first); } } else { assay->debug(4, "Rule returned 0."); diff --git a/test/test-cases/regression/variable-MATCHED_VARS.json b/test/test-cases/regression/variable-MATCHED_VARS.json new file mode 100644 index 00000000..dcfa903d --- /dev/null +++ b/test/test-cases/regression/variable-MATCHED_VARS.json @@ -0,0 +1,90 @@ +[ + { + "enabled":1, + "version_min":300000, + "title":"Testing Variables :: MATCHED_VARS (1/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":"*/*" + }, + "uri":"/?keyI=value&keyII=other_value", + "protocol":"GET" + }, + "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":{ + "debug_log":"Target value: \"value\" \\(Variable: MATCHED_VARS:ARGS:keyI\\)" + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecDebugLogLevel 9", + "SecRule ARGS:keyI \"@contains value\" \"chain,id:28\"", + "SecRule ARGS:keyII \"@contains other_value\" \"chain\"", + "SecRule MATCHED_VARS \"@contains asdf\" \"pass\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing Variables :: MATCHED_VARS (2/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":"*/*" + }, + "uri":"/?keyI=value&keyII=other_value", + "protocol":"GET" + }, + "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":{ + "debug_log":"Target value: \"value\" \\(Variable: MATCHED_VARS:ARGS:keyI\\)" + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecDebugLogLevel 9", + "SecRule ARGS:keyI \"@contains value\" \"chain,id:28\"", + "SecRule ARGS:keyII \"@contains other_value\" \"chain\"", + "SecRule MATCHED_VARS \"@contains asdf\" \"pass\"", + "SecRule MATCHED_VARS \"@contains value\" \"id:29\"" + ] + } +] +