diff --git a/headers/modsecurity/rules_properties.h b/headers/modsecurity/rules_properties.h index 7e7fb10a..89a95ad8 100644 --- a/headers/modsecurity/rules_properties.h +++ b/headers/modsecurity/rules_properties.h @@ -28,11 +28,13 @@ #include "modsecurity/modsecurity.h" #include "modsecurity/transaction.h" +#include "modsecurity/rules_exceptions.h" #ifdef __cplusplus namespace modsecurity { class Rule; +class RulesExceptions; namespace audit_log { class AuditLog; } @@ -232,6 +234,8 @@ class RulesProperties { audit_log::AuditLog *audit_log; OnFailedRemoteRulesAction remoteRulesActionOnFailed; + + RulesExceptions m_exceptions; }; #endif diff --git a/src/Makefile.am b/src/Makefile.am index 6b9e9389..527bd10d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -244,6 +244,7 @@ libmodsecurity_la_SOURCES = \ macro_expansion.cc \ rule.cc \ unique_id.cc \ + rules_exceptions.cc \ ${BODY_PROCESSORS} \ ${ACTIONS} \ ${COLLECTION} \ diff --git a/src/parser/seclang-parser.yy b/src/parser/seclang-parser.yy index d79f60b4..7174b819 100644 --- a/src/parser/seclang-parser.yy +++ b/src/parser/seclang-parser.yy @@ -217,6 +217,8 @@ using modsecurity::Variables::XML; %token CONFIG_DIR_AUDIT_STS %token CONFIG_DIR_AUDIT_TPE +%token CONFIG_SEC_RULE_REMOVE_BY_ID + %token CONFIG_UPDLOAD_KEEP_FILES %token CONFIG_UPDLOAD_SAVE_TMP_FILES %token CONFIG_UPLOAD_FILE_LIMIT @@ -605,6 +607,19 @@ expression: { driver.components.push_back($1); } + | CONFIG_SEC_RULE_REMOVE_BY_ID + { + std::string error; + if (driver.m_exceptions.load($1, &error) == false) { + std::stringstream ss; + ss << "SecRuleRemoveById: failed to load:"; + ss << $1; + ss << ". "; + ss << error; + driver.error(@0, ss.str()); + YYERROR; + } + } /* Debug log: start */ | CONFIG_DIR_DEBUG_LVL { diff --git a/src/parser/seclang-scanner.ll b/src/parser/seclang-scanner.ll index cdf3edc6..99cf106a 100755 --- a/src/parser/seclang-scanner.ll +++ b/src/parser/seclang-scanner.ll @@ -114,6 +114,7 @@ CONFIG_INCLUDE (?i:Include) CONFIG_SEC_REMOTE_RULES (?i:SecRemoteRules) CONFIG_SEC_REMOTE_RULES_FAIL_ACTION (?i:SecRemoteRulesFailAction) +CONFIG_SEC_REMOVE_RULES_BY_ID (?i:SecRuleRemoveById) DICT_ELEMENT [^ \t|]+ @@ -230,6 +231,9 @@ CONFIG_DIR_UNICODE_MAP_FILE (?i:SecUnicodeMapFile) {ACTION_CTL_AUDIT_ENGINE}= { return yy::seclang_parser::make_ACTION_CTL_AUDIT_ENGINE(yytext, *driver.loc.back()); } {ACTION_CTL_FORCE_REQ_BODY_VAR}= { return yy::seclang_parser::make_ACTION_CTL_FORCE_REQ_BODY_VAR(yytext, *driver.loc.back()); } +%{ /* Remove Rules */ %} +{CONFIG_SEC_REMOVE_RULES_BY_ID}[ ]{FREE_TEXT_NEW_LINE} { return yy::seclang_parser::make_CONFIG_SEC_RULE_REMOVE_BY_ID(strchr(yytext, ' ') + 1, *driver.loc.back()); } + %{ /* Upload */ %} {CONFIG_UPLOAD_FILE_LIMIT}[ ]{CONFIG_VALUE_NUMBER} { return yy::seclang_parser::make_CONFIG_UPLOAD_FILE_LIMIT(strchr(yytext, ' ') + 1, *driver.loc.back()); } {CONFIG_UPLOAD_FILE_MODE}[ ]{CONFIG_VALUE_NUMBER} { return yy::seclang_parser::make_CONFIG_UPLOAD_FILE_MODE(strchr(yytext, ' ') + 1, *driver.loc.back()); } diff --git a/src/rules.cc b/src/rules.cc index f5ab60ca..6baacc10 100644 --- a/src/rules.cc +++ b/src/rules.cc @@ -220,6 +220,9 @@ int Rules::evaluate(int phase, Transaction *transaction) { } else if (transaction->m_allowType != actions::NoneAllowType) { debug(9, "Skipped rule id '" + std::to_string(rule->rule_id) \ + "' as request trough the utilization of an `allow' action."); + } else if (m_exceptions.contains(rule->rule_id)) { + debug(9, "Skipped rule id '" + std::to_string(rule->rule_id) \ + + "'. Removed by an SecRuleRemove directive."); } else { rule->evaluate(transaction); } @@ -271,6 +274,8 @@ int Rules::merge(Driver *from) { m_responseBodyTypeToBeInspected.insert(*it); } + this->m_exceptions = from->m_exceptions; + /* * * default Actions is something per configuration context, there is diff --git a/test/test-cases/regression/config-remove_by_id.json b/test/test-cases/regression/config-remove_by_id.json new file mode 100644 index 00000000..509e3fb5 --- /dev/null +++ b/test/test-cases/regression/config-remove_by_id.json @@ -0,0 +1,125 @@ +[ + { + "enabled":1, + "version_min":300000, + "title":"SecRuleRemoveById (1/3)", + "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":"/?key=value&key=other_value", + "method":"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":"Skipped rule id '2'. Removed by an SecRuleRemove directive." + }, + "rules":[ + "SecRuleRemoveById 2", + "SecRule ARGS \"@contains test\" \"id:1,pass,t:trim\"", + "SecRule ARGS \"@contains test\" \"id:2,pass,t:trim\"", + "SecRule ARGS \"@contains test\" \"id:3,pass,t:trim\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"SecRuleRemoveById (2/3)", + "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":"/?key=value&key=other_value", + "method":"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":"Skipped rule id '2'. Removed by an SecRuleRemove directive." + }, + "rules":[ + "SecRuleRemoveById 1-3", + "SecRule ARGS \"@contains test\" \"id:1,pass,t:trim\"", + "SecRule ARGS \"@contains test\" \"id:2,pass,t:trim\"", + "SecRule ARGS \"@contains test\" \"id:3,pass,t:trim\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"SecRuleRemoveById (3/3)", + "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":"/?key=value&key=other_value", + "method":"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":"Skipped rule id '2'. Removed by an SecRuleRemove directive." + }, + "rules":[ + "SecRuleRemoveById 1 2-3", + "SecRule ARGS \"@contains test\" \"id:1,pass,t:trim\"", + "SecRule ARGS \"@contains test\" \"id:2,pass,t:trim\"", + "SecRule ARGS \"@contains test\" \"id:3,pass,t:trim\"" + ] + } +] \ No newline at end of file