diff --git a/Makefile.am b/Makefile.am index 1c93343b..0d3be836 100644 --- a/Makefile.am +++ b/Makefile.am @@ -217,3 +217,4 @@ TESTS+=test/test-cases/secrules-language-tests/operators/eq.json TESTS+=test/test-cases/regression/variable-REQBODY_PROCESSOR.json TESTS+=test/test-cases/regression/variable-REQBODY_PROCESSOR_ERROR.json TESTS+=test/test-cases/regression/variable-URLENCODED_ERROR.json +TESTS+=test/test-cases/regression/variable-RULE.json diff --git a/src/Makefile.am b/src/Makefile.am index befb2412..a33bd671 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -71,7 +71,8 @@ VARIABLES = \ variables/variable.cc \ variables/variations/count.cc \ variables/variations/exclusion.cc \ - variables/xml.cc + variables/xml.cc \ + variables/rule.cc ACTIONS = \ diff --git a/src/actions/log_data.cc b/src/actions/log_data.cc index fef327d6..ac45c5d9 100644 --- a/src/actions/log_data.cc +++ b/src/actions/log_data.cc @@ -29,13 +29,15 @@ namespace actions { bool LogData::evaluate(Rule *rule, Transaction *transaction, RuleMessage *rm) { - std::string data = MacroExpansion::expand(m_parser_payload, transaction); - - rm->m_data = data; + rm->m_data = data(transaction); return true; } +std::string LogData::data(Transaction *transaction) { + return MacroExpansion::expand(m_parser_payload, transaction); +} + } // namespace actions } // namespace modsecurity diff --git a/src/actions/log_data.h b/src/actions/log_data.h index e502f197..d6af3aff 100644 --- a/src/actions/log_data.h +++ b/src/actions/log_data.h @@ -34,6 +34,8 @@ class LogData : public Action { bool evaluate(Rule *rule, Transaction *transaction, RuleMessage *rm) override; + + std::string data(Transaction *Transaction); }; diff --git a/src/actions/msg.cc b/src/actions/msg.cc index c8088bdc..5b3b6247 100644 --- a/src/actions/msg.cc +++ b/src/actions/msg.cc @@ -53,11 +53,16 @@ bool Msg::evaluate(Rule *rule, Transaction *transaction) { transaction->debug(9, "Saving msg: " + msg); #endif - rule->m_log_message = msg; + rule->m_log_message = data(transaction); return true; } +std::string Msg::data(Transaction *transaction) { + return MacroExpansion::expand(m_parser_payload, transaction); +} + + } // namespace actions } // namespace modsecurity diff --git a/src/actions/msg.h b/src/actions/msg.h index b8690223..b9517877 100644 --- a/src/actions/msg.h +++ b/src/actions/msg.h @@ -33,6 +33,8 @@ class Msg : public Action { : Action(action, RunTimeOnlyIfMatchKind) { } bool evaluate(Rule *rule, Transaction *transaction) override; + + std::string data(Transaction *Transaction); }; diff --git a/src/actions/severity.h b/src/actions/severity.h index 5190be12..06ef56bf 100644 --- a/src/actions/severity.h +++ b/src/actions/severity.h @@ -38,7 +38,6 @@ class Severity : public Action { RuleMessage *rm) override; bool init(std::string *error); - private: int m_severity; }; diff --git a/src/parser/seclang-parser.yy b/src/parser/seclang-parser.yy index d372398c..e2be4813 100644 --- a/src/parser/seclang-parser.yy +++ b/src/parser/seclang-parser.yy @@ -65,6 +65,7 @@ class Driver; #include "variables/time_year.h" #include "variables/tx.h" #include "variables/xml.h" +#include "variables/rule.h" using modsecurity::ModSecurity; @@ -247,6 +248,7 @@ using modsecurity::Variables::XML; %token RUN_TIME_VAR_TIME_WDAY %token RUN_TIME_VAR_TIME_YEAR %token RUN_TIME_VAR_XML +%token RUN_TIME_VAR_RULE %token CONFIG_SEC_REMOTE_RULES_FAIL_ACTION @@ -891,6 +893,17 @@ var: if (!var) { var = new XML(name); } $$ = var; } + | RUN_TIME_VAR_RULE + { + std::string name($1); + CHECK_VARIATION_DECL + CHECK_VARIATION(&) { var = new Count( + new modsecurity::Variables::Rule(name)); } + CHECK_VARIATION(!) { var = new Exclusion( + new modsecurity::Variables::Rule(name)); } + if (!var) { var = new modsecurity::Variables::Rule(name); } + $$ = var; + } ; act: diff --git a/src/parser/seclang-scanner.ll b/src/parser/seclang-scanner.ll index e82a6d50..729cce42 100755 --- a/src/parser/seclang-scanner.ll +++ b/src/parser/seclang-scanner.ll @@ -148,6 +148,7 @@ RUN_TIME_VAR_TIME_SEC (?i:TIME_SEC) RUN_TIME_VAR_TIME_WDAY (?i:TIME_WDAY) RUN_TIME_VAR_TIME_YEAR (?i:TIME_YEAR) RUN_TIME_VAR_XML (?i:XML) +RUN_TIME_VAR_RULE (?i:RULE) VARIABLENOCOLON (?i:URLENCODED_ERROR|REQBODY_PROCESSOR_ERROR_MSG|REQBODY_PROCESSOR_ERROR|REQBODY_PROCESSOR|REQBODY_ERROR_MSG|REQBODY_ERROR|MULTIPART_FILE_LIMIT_EXCEEDED|MULTIPART_INVALID_QUOTING|MULTIPART_HEADER_FOLDING|MULTIPART_INVALID_HEADER_FOLDING|MULTIPART_STRICT_ERROR|MULTIPART_UNMATCHED_BOUNDARY|REMOTE_ADDR|REQUEST_LINE) @@ -248,6 +249,8 @@ CONFIG_DIR_UNICODE_MAP_FILE (?i:SecUnicodeMapFile) [!&]?{VARIABLE_COL}(\:[\']{FREE_TEXT_QUOTE}[\'])? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE_COL(yytext, *driver.loc.back()); } [!&]?{RUN_TIME_VAR_XML}(\:{DICT_ELEMENT})? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_XML(yytext, *driver.loc.back()); } [!&]?{RUN_TIME_VAR_XML}(\:[\']{FREE_TEXT_QUOTE}[\'])? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_XML(yytext, *driver.loc.back()); } +[!&]?{RUN_TIME_VAR_RULE}(\:{DICT_ELEMENT})? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_RULE(yytext, *driver.loc.back()); } +[!&]?{RUN_TIME_VAR_RULE}(\:[\']{FREE_TEXT_QUOTE}[\'])? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_RULE(yytext, *driver.loc.back()); } [!&]?{VARIABLE_TX}(\:{DICT_ELEMENT})? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE_TX(yytext, *driver.loc.back()); } [!&]?{VARIABLE_TX}(\:[\']{FREE_TEXT_QUOTE}[\'])? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE_TX(yytext, *driver.loc.back()); } [!&]?{RUN_TIME_VAR_DUR} { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_DUR(yytext, *driver.loc.back()); } @@ -267,6 +270,8 @@ CONFIG_DIR_UNICODE_MAP_FILE (?i:SecUnicodeMapFile) ["][!&]?{VARIABLE_COL}(\:[\']{FREE_TEXT_QUOTE}[\'])?["] { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE_COL(yytext, *driver.loc.back()); } ["][!&]?{RUN_TIME_VAR_XML}(\:{DICT_ELEMENT})? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_XML(yytext, *driver.loc.back()); } ["][!&]?{RUN_TIME_VAR_XML}(\:[\']{FREE_TEXT_QUOTE}[\'])?["] { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_XML(yytext, *driver.loc.back()); } +["][!&]?{RUN_TIME_VAR_RULE}(\:{DICT_ELEMENT})? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_RULE(yytext, *driver.loc.back()); } +["][!&]?{RUN_TIME_VAR_RULE}(\:[\']{FREE_TEXT_QUOTE}[\'])?["] { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_RULE(yytext, *driver.loc.back()); } ["][!&]?{RUN_TIME_VAR_DUR}["] { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_DUR(yytext, *driver.loc.back()); } ["][!&]?{RUN_TIME_VAR_ENV}(\:{DICT_ELEMENT})?["] { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_ENV(yytext, *driver.loc.back()); } diff --git a/src/variables/rule.cc b/src/variables/rule.cc new file mode 100644 index 00000000..76e2ad11 --- /dev/null +++ b/src/variables/rule.cc @@ -0,0 +1,103 @@ +/* + * ModSecurity, http://www.modsecurity.org/ + * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/) + * + * You may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Trustwave Holdings, Inc. + * directly using the email address security@modsecurity.org. + * + */ + +#include "variables/rule.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "modsecurity/transaction.h" +#include "modsecurity/rules_properties.h" +#include "modsecurity/rules.h" + +#include "src/request_body_processor/xml.h" +#include "src/actions/action.h" +#include "src/actions/severity.h" +#include "src/actions/xmlns.h" +#include "src/actions/log_data.h" +#include "src/actions/msg.h" + +namespace modsecurity { +namespace Variables { + +void Rule::evaluateInternal(Transaction *t, + modsecurity::Rule *rule, + std::vector *l) { + std::map envs; + + // id + envs.insert(std::pair("RULE:id", + std::to_string(rule->rule_id))); + + // rev + envs.insert(std::pair("RULE:rev", + rule->m_rev)); + + // severity + std::vector acts = rule->getActionsByName("severity"); + for (actions::Action *i : acts) { + actions::Severity *a = reinterpret_cast(i); + if (a) { + envs.insert(std::pair("RULE:severity", + std::to_string(a->m_severity))); + } + } + + // logdata + acts = rule->getActionsByName("logdata"); + for (actions::Action *i : acts) { + actions::LogData *a = reinterpret_cast(i); + if (a) { + envs.insert(std::pair("RULE:logdata", + a->data(t))); + } + } + + // msg + acts = rule->getActionsByName("msg"); + for (actions::Action *i : acts) { + actions::Msg *a = reinterpret_cast(i); + if (a) { + envs.insert(std::pair("RULE:msg", + a->data(t))); + } + } + + for (auto& x : envs) { + if ((x.first.substr(0, m_name.size() + 1).compare(m_name + ":") != 0) + && (x.first != m_name)) { + continue; + } + l->push_back(new collection::Variable(x.first, x.second)); + } +} + +} // namespace Variables +} // namespace modsecurity diff --git a/src/variables/rule.h b/src/variables/rule.h new file mode 100644 index 00000000..0e2f99b6 --- /dev/null +++ b/src/variables/rule.h @@ -0,0 +1,43 @@ +/* + * ModSecurity, http://www.modsecurity.org/ + * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/) + * + * You may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Trustwave Holdings, Inc. + * directly using the email address security@modsecurity.org. + * + */ + +#include +#include +#include + +#ifndef SRC_VARIABLES_RULE_H_ +#define SRC_VARIABLES_RULE_H_ + +#include "variables/variable.h" + +namespace modsecurity { + +class Transaction; +namespace Variables { + +class Rule : public Variable { + public: + explicit Rule(std::string _name) + : Variable(_name) { }; + + void evaluateInternal(Transaction *transaction, + modsecurity::Rule *rule, + std::vector *l) override; +}; + +} // namespace Variables +} // namespace modsecurity + +#endif // SRC_VARIABLES_RULE_H_ diff --git a/test/test-cases/regression/variable-RULE.json b/test/test-cases/regression/variable-RULE.json new file mode 100644 index 00000000..a2c1a76f --- /dev/null +++ b/test/test-cases/regression/variable-RULE.json @@ -0,0 +1,233 @@ +[ + { + "enabled":1, + "version_min":300000, + "title":"Testing Variables :: RULE (1/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-Length": "27", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri":"/", + "method":"POST", + "body": [ + "param1=value1¶m2=value2" + ] + }, + "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: \"1\" \\(Variable: RULE:id\\)" + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecDebugLogLevel 9", + "SecRule RULE:id \"@contains test\" \"id:1,phase:3,rev:1.3,pass,t:trim\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing Variables :: RULE (2/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-Length": "27", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri":"/", + "method":"POST", + "body": [ + "param1=value1¶m2=value2" + ] + }, + "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: \"200\" \\(Variable: RULE:rev\\)" + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecDebugLogLevel 9", + "SecRule RULE:rev \"@contains test\" \"id:1,rev:200,phase:3,pass,t:trim\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing Variables :: RULE (3/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-Length": "27", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri":"/", + "method":"POST", + "body": [ + "param1=value1¶m2=value2" + ] + }, + "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: \"2\" \\(Variable: RULE:severity\\)" + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecDebugLogLevel 9", + "SecRule RULE:severity \"@contains test\" \"id:1,phase:3,severity:critical,pass,t:trim\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing Variables :: RULE (4/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-Length": "27", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri":"/", + "method":"POST", + "body": [ + "param1=value1¶m2=value2" + ] + }, + "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: \"data123\" \\(Variable: RULE:logdata\\)" + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecDebugLogLevel 9", + "SecRule RULE:logdata \"@contains test\" \"id:1,logdata:'data123',phase:3,pass,t:trim\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing Variables :: RULE (5/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-Length": "27", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri":"/", + "method":"POST", + "body": [ + "param1=value1¶m2=value2" + ] + }, + "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: \"message123\" \\(Variable: RULE:msg\\)" + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecDebugLogLevel 9", + "SecRule RULE:msg \"@contains test\" \"id:1,msg:'message123',phase:3,pass,t:trim\"" + ] + } +] +