diff --git a/src/actions/capture.cc b/src/actions/capture.cc index 21f18902..9f9301ed 100644 --- a/src/actions/capture.cc +++ b/src/actions/capture.cc @@ -25,6 +25,7 @@ #include "operators/operator.h" #include "operators/pm.h" #include "operators/rx.h" +#include "operators/contains.h" namespace ModSecurity { namespace actions { @@ -35,6 +36,7 @@ bool Capture::evaluate(Rule *rule, Assay *assay) { operators::Pm *pm = dynamic_cast(op); operators::Rx *rx = dynamic_cast(op); + operators::Contains *contains = dynamic_cast(op); if (pm != NULL) { match = pm->matched; @@ -44,20 +46,19 @@ bool Capture::evaluate(Rule *rule, Assay *assay) { match = rx->matched; } + if (contains != NULL) { + match = contains->matched; + } + if (match.empty()) { return false; } int i = 0; while (match.empty() == false) { - std::string varName = "TX:" + std::to_string(i); - std::string *a = assay->resolve_variable_first(varName); - if (a == NULL) { - assay->store_variable(varName, match.back()); - } else { - assay->update_variable_first(varName, match.back()); - } + assay->setCollection("TX", std::to_string(i), match.back()); match.pop_back(); + i++; } return true; diff --git a/src/operators/contains.cc b/src/operators/contains.cc index 8bfb7349..2aed433d 100644 --- a/src/operators/contains.cc +++ b/src/operators/contains.cc @@ -23,6 +23,10 @@ namespace operators { bool Contains::evaluate(Assay *assay, const std::string &input) { bool contains = input.find(param) != std::string::npos; + if (contains) { + matched.push_back(param); + } + if (negation) { return !contains; } diff --git a/src/operators/contains.h b/src/operators/contains.h index af24e0e9..44081cd9 100644 --- a/src/operators/contains.h +++ b/src/operators/contains.h @@ -31,6 +31,8 @@ class Contains : public Operator { Contains(std::string op, std::string param, bool negation) : Operator(op, param, negation) { } bool evaluate(Assay *assay, const std::string &exp) override; + + std::list matched; }; } // namespace operators diff --git a/test/test-cases/regression/action-msg.json b/test/test-cases/regression/action-msg.json index f443df0f..f0f58b17 100644 --- a/test/test-cases/regression/action-msg.json +++ b/test/test-cases/regression/action-msg.json @@ -58,5 +58,65 @@ "SecRule REQUEST_HEADERS \"@contains PHPSESSID\" \"id:1,t:lowercase,t:none,msg:'This is a test, %{REQUEST_HEADERS:Accept}%'\"", "SecRule TX \"@contains to_test\" \"id:2,t:lowercase,t:none\"" ] + }, + { + "enabled":1, + "version_min":300000, + "version_max":0, + "title":"Testing action :: msg - variable expansion", + "client":{ + "ip":"200.249.12.31", + "port":2313 + }, + "server":{ + "ip":"200.249.12.31", + "port":80 + }, + "request":{ + "headers":{ + "User-Agent":"Mozilla\/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko\/20091102 Firefox\/3.5.5 (.NET CLR 3.5.30729)", + "Accept":"text\/html,application\/xhtml+xml,application\/xml;q=0.9,*\/*;q=0.8", + "Accept-Language":"en-us,en;q=0.5", + "Accept-Encoding":"gzip,deflate", + "Accept-Charset":"ISO-8859-1,utf-8;q=0.7,*;q=0.7", + "Keep-Alive":"300", + "Connection":"keep-alive", + "Cookie":"PHPSESSID=rAAAAAAA2t5uvjq435r4q7ib3vtdjq120", + "Pragma":"no-cache", + "Cache-Control":"no-cache" + }, + "uri":"\/test.pl?param1= test ¶m2=test2", + "protocol":"GET", + "http_version":1.1, + "body":"" + }, + "response":{ + "headers":{ + "Content-Type":"text\/xml; charset=utf-8\n\r", + "Content-Length":"length\n\r" + }, + "body":[ + "\n\r", + "\n\r", + " \n\r", + " \n\r", + " string<\/EnlightenResult>\n\r", + " <\/EnlightenResponse>\n\r", + " <\/soap:Body>\n\r", + "<\/soap:Envelope>\n\r" + ] + }, + "expected":{ + "audit_log":"", + "debug_log":"Saving msg: This is a test: PHPSESSID ops", + "error_log":"" + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecDebugLogLevel 9", + "SecRule REQUEST_HEADERS \"@contains PHPSESSID\" \"id:1,capture,t:lowercase,t:none,msg:'This is a test: %{TX.0}% ops'\"", + "SecRule TX \"@contains to_test\" \"id:2,t:lowercase,capture,t:none\"" + ] } ] \ No newline at end of file