From b0f69b1262cae63dc623e9ad37da6ed1b48a3614 Mon Sep 17 00:00:00 2001 From: Felipe Zimmerle Date: Thu, 30 Jun 2016 10:34:55 -0300 Subject: [PATCH] Adds support to the `skip' action --- headers/modsecurity/transaction.h | 8 +- src/Makefile.am | 1 + src/actions/skip.cc | 53 ++++++++++ src/actions/skip.h | 46 +++++++++ src/parser/seclang-parser.yy | 9 +- src/rules.cc | 11 +- test/test-cases/regression/action-skip.json | 107 ++++++++++++++++++++ 7 files changed, 224 insertions(+), 11 deletions(-) create mode 100644 src/actions/skip.cc create mode 100644 src/actions/skip.h create mode 100644 test/test-cases/regression/action-skip.json diff --git a/headers/modsecurity/transaction.h b/headers/modsecurity/transaction.h index 40825b7a..5594e09c 100644 --- a/headers/modsecurity/transaction.h +++ b/headers/modsecurity/transaction.h @@ -304,9 +304,15 @@ class Transaction { */ std::string m_marker; + /** + * Holds the amount of rules that should be skipped. If bigger than 0 the + * current rule should be skipped and the number needs to be decreased. + */ + int m_skip_next; + /** * Holds the decode URI. Notice that m_uri holds the raw version - * of the URI. + * of the URI. */ std::string m_uri_decoded; diff --git a/src/Makefile.am b/src/Makefile.am index 3e6eb096..9d8ab7c1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -101,6 +101,7 @@ ACTIONS = \ actions/set_uid.cc \ actions/set_var.cc \ actions/status.cc \ + actions/skip.cc \ actions/skip_after.cc \ actions/tag.cc \ actions/transformations/base64_decode.cc \ diff --git a/src/actions/skip.cc b/src/actions/skip.cc new file mode 100644 index 00000000..5e91e7ad --- /dev/null +++ b/src/actions/skip.cc @@ -0,0 +1,53 @@ +/* + * 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 "actions/skip.h" + +#include +#include + +#include "actions/action.h" +#include "modsecurity/transaction.h" +#include "src/utils.h" + +namespace modsecurity { +namespace actions { + + +bool Skip::init(std::string *error) { + try { + m_skip_next = std::stoi(m_parser_payload); + } catch (...) { + error->assign("Skip: The input \"" + m_parser_payload + "\" is " \ + "not a number."); + return false; + } + return true; +} + + +bool Skip::evaluate(Rule *rule, Transaction *transaction) { +#ifndef NO_LOGS + transaction->debug(5, "Skipping the next " + std::to_string(m_skip_next) \ + + " rules."); +#endif + transaction->m_skip_next = m_skip_next; + + return true; +} + + +} // namespace actions +} // namespace modsecurity diff --git a/src/actions/skip.h b/src/actions/skip.h new file mode 100644 index 00000000..1c5597dd --- /dev/null +++ b/src/actions/skip.h @@ -0,0 +1,46 @@ +/* + * 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 "actions/action.h" + +#ifndef SRC_ACTIONS_SKIP_H_ +#define SRC_ACTIONS_SKIP_H_ + +class Transaction; + +namespace modsecurity { +class Transaction; +namespace actions { + + +class Skip : public Action { + public: + explicit Skip(std::string action) + : Action(action, RunTimeOnlyIfMatchKind), + m_skip_next(0) { } + + bool init(std::string *error) override; + bool evaluate(Rule *rule, Transaction *transaction) override; + + int m_skip_next; +}; + + +} // namespace actions +} // namespace modsecurity + +#endif // SRC_ACTIONS_SKIP_H_ diff --git a/src/parser/seclang-parser.yy b/src/parser/seclang-parser.yy index 5ec24430..9fcc8885 100644 --- a/src/parser/seclang-parser.yy +++ b/src/parser/seclang-parser.yy @@ -30,6 +30,7 @@ class Driver; #include "actions/set_uid.h" #include "actions/set_var.h" #include "actions/severity.h" +#include "actions/skip.h" #include "actions/skip_after.h" #include "actions/msg.h" #include "actions/phase.h" @@ -1087,13 +1088,7 @@ act: | ACTION_SKIP { std::string error; - /* - - TODO: skip is not implemented yet. - - $$ = new modsecurity::actions::SkipAfter($1); - */ - $$ = Action::instantiate($1); + $$ = new modsecurity::actions::Skip($1); if ($$->init(&error) == false) { driver.error(@0, error); YYERROR; diff --git a/src/rules.cc b/src/rules.cc index 6274b528..f0d61be4 100644 --- a/src/rules.cc +++ b/src/rules.cc @@ -185,9 +185,7 @@ int Rules::evaluate(int phase, Transaction *transaction) { for (int i = 0; i < rules.size(); i++) { Rule *rule = rules[i]; - if (transaction->m_marker.empty()) { - rule->evaluate(transaction); - } else { + if (transaction->m_marker.empty() == false) { debug(9, "Skipped rule id '" + std::to_string(rule->rule_id) \ + "' due to a SecMarker: " + transaction->m_marker); m_secmarker_skipped++; @@ -198,6 +196,13 @@ int Rules::evaluate(int phase, Transaction *transaction) { transaction->m_marker.clear(); m_secmarker_skipped = 0; } + } else if (transaction->m_skip_next > 0) { + transaction->m_skip_next--; + debug(9, "Skipped rule id '" + std::to_string(rule->rule_id) \ + + "' due to `skip' action. Still " + \ + std::to_string(transaction->m_skip_next) + " to be skipped."); + } else { + rule->evaluate(transaction); } } return 1; diff --git a/test/test-cases/regression/action-skip.json b/test/test-cases/regression/action-skip.json new file mode 100644 index 00000000..829839ad --- /dev/null +++ b/test/test-cases/regression/action-skip.json @@ -0,0 +1,107 @@ +[ + { + "enabled":1, + "version_min":300000, + "title":"Testing skip action 1/3", + "expected":{ + "debug_log": "\\[9\\] Skipped rule id \\'2\\' due to \\`skip\\' action." + }, + "client":{ + "ip":"200.249.12.31", + "port":123 + }, + "request":{ + "headers":{ + "Host":"localhost", + "User-Agent":"curl/7.38.0", + "Accept":"*/*", + "User-Agent":"My sweet little browser", + "Cookie": "PHPSESSID=rAAAAAAA2t5uvjq435r4q7ib3vtdjq120" + }, + "uri":"/?key=value&key=other_value", + "method":"GET" + }, + "server":{ + "ip":"200.249.12.31", + "port":80 + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecRule REQUEST_HEADERS:User-Agent \"^(.*)$\" \"id:'1',phase:1,skip:1\"", + "SecRule REQUEST_HEADERS \"should be skipped\" \"id:'2',phase:1,setvar:SESSION.score=+10\"", + "SecRule REQUEST_HEADERS:User-Agent \"^(.*)$\" \"id:'3',phase:1,t:none,nolog,pass\"", + "SecRule REQUEST_HEADERS \".*\" \"id:'4',phase:1,setvar:SESSION.score=+5\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing skip action 2/3", + "expected":{ + "parser_error": "Rules error. File: action-skip.json. Line: 3. Column: 61. invalid character s" + }, + "client":{ + "ip":"200.249.12.31", + "port":123 + }, + "request":{ + "headers":{ + "Host":"localhost", + "User-Agent":"curl/7.38.0", + "Accept":"*/*", + "User-Agent":"My sweet little browser", + "Cookie": "PHPSESSID=rAAAAAAA2t5uvjq435r4q7ib3vtdjq120" + }, + "uri":"/?key=value&key=other_value", + "method":"GET" + }, + "server":{ + "ip":"200.249.12.31", + "port":80 + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecRule REQUEST_HEADERS:User-Agent \"^(.*)$\" \"id:'1',phase:1,skip:abc\"", + "SecRule REQUEST_HEADERS \"should be skipped\" \"id:'2',phase:1,setvar:SESSION.score=+10\"", + "SecRule REQUEST_HEADERS:User-Agent \"^(.*)$\" \"id:'3',phase:1,t:none,nolog,pass\"", + "SecRule REQUEST_HEADERS \".*\" \"id:'4',phase:1,setvar:SESSION.score=+5\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing skip action 3/3", + "expected":{ + "debug_log": "\\[9\\] Skipped rule id \\'3\\' due to \\`skip\\' action." + }, + "client":{ + "ip":"200.249.12.31", + "port":123 + }, + "request":{ + "headers":{ + "Host":"localhost", + "User-Agent":"curl/7.38.0", + "Accept":"*/*", + "User-Agent":"My sweet little browser", + "Cookie": "PHPSESSID=rAAAAAAA2t5uvjq435r4q7ib3vtdjq120" + }, + "uri":"/?key=value&key=other_value", + "method":"GET" + }, + "server":{ + "ip":"200.249.12.31", + "port":80 + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecRule REQUEST_HEADERS:User-Agent \"^(.*)$\" \"id:'1',phase:1,skip:2\"", + "SecRule REQUEST_HEADERS \"should be skipped\" \"id:'2',phase:1,setvar:SESSION.score=+10\"", + "SecRule REQUEST_HEADERS:User-Agent \"^(.*)$\" \"id:'3',phase:1,t:none,nolog,pass\"", + "SecRule REQUEST_HEADERS \".*\" \"id:'4',phase:1,setvar:SESSION.score=+5\"" + ] + } +] \ No newline at end of file