diff --git a/headers/modsecurity/rule.h b/headers/modsecurity/rule.h index 9354fe5b..a2a2c0f3 100644 --- a/headers/modsecurity/rule.h +++ b/headers/modsecurity/rule.h @@ -24,6 +24,7 @@ #define HEADERS_MODSECURITY_RULE_H_ #include "modsecurity/transaction.h" +#include "modsecurity/collection/variable.h" #ifdef __cplusplus @@ -51,11 +52,26 @@ class Rule { ~Rule(); bool evaluate(Transaction *transaction); bool evaluateActions(Transaction *transaction); + std::vector getFinalVars(Transaction *trasn); + void executeActionsAfterFullMatch(Transaction *trasn, + bool containsDisruptive, RuleMessage *ruleMessage); + std::vector executeSecDefaultActionTransofrmations( + Transaction *trasn, std::string &value, bool multiMatch); + bool executeOperatorAt(Transaction *trasn, std::string key, + std::string value); + void executeActionsIndependentOfChainedRuleResult(Transaction *trasn, + bool *b, RuleMessage *ruleMessage); + std::string resolveMatchMessage(std::string key, std::string value); + void updateMatchedVars(Transaction *trasn, std::string key, + std::string value); + void cleanMatchedVars(Transaction *trasn); + void updateRulesVariable(Transaction *trasn); + operators::Operator *op; - std::vector actions_conf; - std::vector actions_runtime_pre; - std::vector actions_runtime_pos; + std::vector m_actionsConf; + std::vector m_actionsRuntimePre; + std::vector m_actionsRuntimePos; std::vector getActionNames(); std::vector getActionsByName(const std::string& name); @@ -87,7 +103,6 @@ class Rule { std::string m_fileName; int m_lineNumber; - std::string m_log_message; std::string m_log_data; int m_accuracy; int m_maturity; diff --git a/src/actions/severity.cc b/src/actions/severity.cc index 15e7c128..3b9af4d5 100644 --- a/src/actions/severity.cc +++ b/src/actions/severity.cc @@ -82,6 +82,9 @@ bool Severity::evaluate(Rule *rule, Transaction *transaction, if (transaction->m_highestSeverityAction > this->m_severity) { transaction->m_highestSeverityAction = this->m_severity; } + + transaction->m_collections.storeOrUpdateFirst("RULE:severity", + std::to_string(m_severity)); return true; } diff --git a/src/macro_expansion.cc b/src/macro_expansion.cc index 409a3e86..096f26b8 100644 --- a/src/macro_expansion.cc +++ b/src/macro_expansion.cc @@ -73,7 +73,7 @@ std::string MacroExpansion::expand(const std::string& input, std::string var = std::string(variable, collection + 1, variable.length() - (collection + 1)); - if (utils::string::toupper(col) == "RULE") { + /*if (utils::string::toupper(col) == "RULE") { if (rule == NULL) { transaction->debug(9, "macro expansion: cannot resolve " \ "RULE variable without the Rule object"); @@ -89,6 +89,10 @@ std::string MacroExpansion::expand(const std::string& input, for (auto *i : l) { delete i; } + }*/ + if (utils::string::toupper(col) == "RULE") { + variableValue = transaction->m_collections.resolveFirst( + "RULE:" + var); } else { variableValue = transaction->m_collections.resolveFirst(col, var); diff --git a/src/parser/seclang-parser.yy b/src/parser/seclang-parser.yy index 308dcbd1..14a717db 100644 --- a/src/parser/seclang-parser.yy +++ b/src/parser/seclang-parser.yy @@ -943,6 +943,13 @@ var: } | RUN_TIME_VAR_RULE { + std::string name($1); + CHECK_VARIATION_DECL + CHECK_VARIATION(&) { var = new Count(new Variable(name, Variable::VariableKind::DirectVariable)); } + CHECK_VARIATION(!) { var = new Exclusion(new Variable(name, Variable::VariableKind::DirectVariable)); } + if (!var) { var = new Variable(name, Variable::VariableKind::DirectVariable); } + $$ = var; + /* std::string name($1); CHECK_VARIATION_DECL CHECK_VARIATION(&) { var = new Count( @@ -951,6 +958,7 @@ var: new modsecurity::Variables::Rule(name)); } if (!var) { var = new modsecurity::Variables::Rule(name); } $$ = var; + */ } ; diff --git a/src/rule.cc b/src/rule.cc index 9cc09b72..05ae45be 100644 --- a/src/rule.cc +++ b/src/rule.cc @@ -34,6 +34,9 @@ #include "modsecurity/rules.h" #include "modsecurity/rule_message.h" #include "src/macro_expansion.h" +#include "src/actions/msg.h" +#include "src/actions/log_data.h" +#include "src/actions/severity.h" using modsecurity::Variables::Variations::Exclusion; @@ -49,19 +52,19 @@ Rule::~Rule() { if (op != NULL) { delete op; } - while (actions_conf.empty() == false) { - auto *a = actions_conf.back(); - actions_conf.pop_back(); + while (m_actionsConf.empty() == false) { + auto *a = m_actionsConf.back(); + m_actionsConf.pop_back(); delete a; } - while (actions_runtime_pre.empty() == false) { - auto *a = actions_runtime_pre.back(); - actions_runtime_pre.pop_back(); + while (m_actionsRuntimePre.empty() == false) { + auto *a = m_actionsRuntimePre.back(); + m_actionsRuntimePre.pop_back(); delete a; } - while (actions_runtime_pos.empty() == false) { - auto *a = actions_runtime_pos.back(); - actions_runtime_pos.pop_back(); + while (m_actionsRuntimePos.empty() == false) { + auto *a = m_actionsRuntimePos.back(); + m_actionsRuntimePos.pop_back(); delete a; } while (variables != NULL && variables->empty() == false) { @@ -116,13 +119,13 @@ Rule::Rule(Operator *_op, if (actions != NULL) { for (Action *a : *actions) { if (a->action_kind == Action::ConfigurationKind) { - actions_conf.push_back(a); + m_actionsConf.push_back(a); a->evaluate(this, NULL); } else if (a->action_kind == Action::RunTimeBeforeMatchAttemptKind) { - actions_runtime_pre.push_back(a); + m_actionsRuntimePre.push_back(a); } else if (a->action_kind == Action::RunTimeOnlyIfMatchKind) { - actions_runtime_pos.push_back(a); + m_actionsRuntimePos.push_back(a); } else { std::cout << "General failure, action: " << a->m_name; std::cout << " has an unknown type." << std::endl; @@ -148,13 +151,13 @@ Rule::Rule(Operator *_op, std::vector Rule::getActionNames() { std::vector a; - for (auto &z : this->actions_runtime_pos) { + for (auto &z : this->m_actionsRuntimePos) { a.push_back(z->m_name); } - for (auto &z : this->actions_runtime_pre) { + for (auto &z : this->m_actionsRuntimePre) { a.push_back(z->m_name); } - for (auto &z : this->actions_conf) { + for (auto &z : this->m_actionsConf) { a.push_back(z->m_name); } @@ -163,164 +166,220 @@ std::vector Rule::getActionNames() { bool Rule::evaluateActions(Transaction *trasn) { + return true; +} + + +void Rule::updateMatchedVars(Transaction *trasn, std::string key, + std::string value) { + trasn->debug(4, "Matched vars updated."); + trasn->m_collections.storeOrUpdateFirst("MATCHED_VAR", value); + trasn->m_collections.storeOrUpdateFirst("MATCHED_VAR_NAME", key); + trasn->m_collections.store("MATCHED_VARS:" + key, value); + trasn->m_collections.store("MATCHED_VARS_NAMES:" + key, key); +} + + +void Rule::cleanMatchedVars(Transaction *trasn) { + trasn->debug(4, "Matched vars cleaned."); + trasn->m_collections.storeOrUpdateFirst("MATCHED_VAR", ""); + trasn->m_collections.del("MATCHED_VARS_NAME"); + trasn->m_collections.del("MATCHED_VARS"); + trasn->m_collections.del("MATCHED_VARS_NAMES"); +} + + +void Rule::updateRulesVariable(Transaction *trasn) { + if (rule_id != 0) { + trasn->m_collections.storeOrUpdateFirst("RULE:id", + std::to_string(rule_id)); + } + if (m_rev.empty() == false) { + trasn->m_collections.storeOrUpdateFirst("RULE:rev", m_rev); + } + if (getActionsByName("msg").size() > 0) { + actions::Msg *msg = dynamic_cast( + getActionsByName("msg")[0]); + trasn->m_collections.storeOrUpdateFirst("RULE:msg", msg->data(trasn)); + } + if (getActionsByName("logdata").size() > 0) { + actions::LogData *data = dynamic_cast( + getActionsByName("logdata")[0]); + trasn->m_collections.storeOrUpdateFirst("RULE:logdata", + data->data(trasn)); + } + if (getActionsByName("severity").size() > 0) { + actions::Severity *data = dynamic_cast( + getActionsByName("severity")[0]); + trasn->m_collections.storeOrUpdateFirst("RULE:severity", + std::to_string(data->m_severity)); + } +} + + +std::string Rule::resolveMatchMessage(std::string key, std::string value) { + std::string ret = this->op->m_match_message; + + if (ret.empty() == true) { + ret = "Matched \"Operator `" + this->op->m_op + "' with parameter `" + + utils::string::limitTo(200, this->op->m_param) + + "' against variable `" + key + "' (Value: `" + + utils::string::limitTo(100, utils::string::toHexIfNeeded(value)) + + "' ) \" at " + key; + } + + return ret; +} + + +void Rule::executeActionsIndependentOfChainedRuleResult(Transaction *trasn, + bool *containsDisruptive, RuleMessage *ruleMessage) { + for (Action *a : this->m_actionsRuntimePos) { + if (a->isDisruptive() == true) { + if (a->m_name == "pass") { + trasn->debug(4, "Rule contains a `pass' action"); + } else { + *containsDisruptive = true; + } + } else { + if (a->m_name == "setvar" + || a->m_name == "msg" + || a->m_name == "log") { + trasn->debug(4, "Running [I] (_non_ disruptive) " \ + "action: " + a->m_name); + a->evaluate(this, trasn, ruleMessage); + } + } + } + for (auto &z : ruleMessage->m_tmp_actions) { + trasn->m_actions.push_back(z); + } +} + + +bool Rule::executeOperatorAt(Transaction *trasn, std::string key, + std::string value) { + clock_t begin = clock(); + clock_t end; + double elapsed_s = 0; + bool ret; + + trasn->debug(9, "Target value: \"" + utils::string::limitTo(80, + utils::string::toHexIfNeeded(value)) \ + + "\" (Variable: " + key + ")"); + + ret = this->op->evaluateInternal(trasn, this, value); + if (ret == false) { + return false; + } + + end = clock(); + elapsed_s = static_cast(end - begin) / CLOCKS_PER_SEC; + + trasn->debug(4, "Operator completed in " + \ + std::to_string(elapsed_s) + " seconds"); + + return ret; +} + +// FIXME: this should be a list instead of a vector, keeping the but +// of v2 alive. +std::vector Rule::executeSecDefaultActionTransofrmations( + Transaction *trasn, std::string &value, bool multiMatch) { int none = 0; - bool containsDisruptive = false; - // int transformations = 0; - for (Action *a : this->actions_runtime_pre) { + int transformations = 0; + std::vector ret; + + if (multiMatch == true) { + ret.push_back(value); + ret.push_back(value); + } + + for (Action *a : this->m_actionsRuntimePre) { if (a->m_isNone) { none++; } } -#ifndef NO_LOGS - trasn->debug(4, "Running unconditional rule."); -#endif - + // Check for transformations on the SecDefaultAction + // Notice that first we make sure that won't be a t:none + // on the target rule. if (none == 0) { - /* for (Action *a : trasn->m_rules->defaultActions[this->phase]) { - if (a->action_kind == actions::Action::RunTimeBeforeMatchAttemptKind) { - value = a->evaluate(value, trasn); + if (a->action_kind \ + == actions::Action::RunTimeBeforeMatchAttemptKind) { + std::string oldValue = value; + if (multiMatch) { + oldValue = ret.back(); + } + std::string newValue = a->evaluate(oldValue, trasn); + if (multiMatch == true) { + if (newValue != oldValue) { + ret.push_back(newValue); + } + } else { + value = newValue; + } + trasn->debug(9, "(SecDefaultAction) T (" + \ std::to_string(transformations) + ") " + \ - a->name + ": \"" + value +"\""); - transformations++; + a->m_name + ": \"" + \ + utils::string::limitTo(80, newValue) +"\""); + + transformations++; } } - */ } - for (Action *a : this->actions_runtime_pre) { - /* + for (Action *a : this->m_actionsRuntimePre) { if (none == 0) { - value = a->evaluate(value, trasn); + std::string oldValue = value; + if (multiMatch) { + oldValue = ret.back(); + } + + std::string newValue = a->evaluate(oldValue, trasn); + if (multiMatch == true) { + if (newValue != oldValue) { + ret.push_back(newValue); + } + } else { + value = newValue; + } + trasn->debug(9, " T (" + \ std::to_string(transformations) + ") " + \ - a->name + ": \"" + value +"\""); - transformations++; + a->m_name + ": \"" + \ + utils::string::limitTo(80, newValue) + "\""); + transformations++; } - */ if (a->m_isNone) { none--; } } - - for (Action *a : this->actions_runtime_pos) { - if (a->isDisruptive() == false) { -#ifndef NO_LOGS - trasn->debug(4, "Running [III] (_non_ disruptive) action: " + - a->m_name + "."); -#endif - a->evaluate(this, trasn); - } else { - containsDisruptive = true; + if (multiMatch == true) { + // v2 checks the last entry twice. Don't know why. + ret.push_back(ret.back()); + trasn->debug(9, "multiMatch is enabled. " \ + + std::to_string(ret.size()) + \ + " values to be tested."); + for (const std::string &a : ret) { + trasn->debug(9, " - " + a); } + } else { + ret.push_back(value); } - for (Action *a : trasn->m_rules->defaultActions[this->phase]) { - if (a->action_kind == actions::Action::RunTimeOnlyIfMatchKind) { - if (a->isDisruptive()) { - if (containsDisruptive) { -#ifndef NO_LOGS - trasn->debug(4, "(SecDefaultAction) " \ - "_ignoring_ action: " + a->m_name + \ - " (rule contains a disruptive action)"); -#endif - } else { - if (trasn->m_rules->m_secRuleEngine - == Rules::EnabledRuleEngine) { -#ifndef NO_LOGS - trasn->debug(4, "(SecDefaultAction) " \ - "Running action: " + a->m_name + \ - " (rule _does not_ contains a " \ - "disruptive action)"); -#endif - a->evaluate(this, trasn); - } else { -#ifndef NO_LOGS - trasn->debug(4, "(SecDefaultAction) " \ - "_Not_ running action: " + a->m_name + \ - ". Rule _does not_ contains a " \ - "disruptive action, but SecRuleEngine is not On."); -#endif - } - } - } else { -#ifndef NO_LOGS - trasn->debug(4, "(SecDefaultAction) Running action: " + \ - a->m_name); - a->evaluate(this, trasn); -#endif - } - } - } - - for (Action *a : this->actions_runtime_pos) { - if (a->isDisruptive() - && trasn->m_rules->m_secRuleEngine - == Rules::EnabledRuleEngine) { -#ifndef NO_LOGS - trasn->debug(4, "Running (disruptive) action: " + a->m_name); -#endif - a->evaluate(this, trasn); - } else if (a->isDisruptive()) { -#ifndef NO_LOGS - trasn->debug(4, "Not running disruptive action: " + \ - a->m_name + ". SecRuleEngine is not On"); -#endif - } - } - - return true; + return ret; } -bool Rule::evaluate(Transaction *trasn) { - bool ret = false; - bool globalRet = false; - std::vector *variables = this->variables; - RuleMessage *ruleMessage = NULL; - - trasn->m_matched.clear(); - - if (m_secmarker == true) { - return true; - } - if (m_unconditional == true) { - return evaluateActions(trasn); - } - - for (auto &i : trasn->m_ruleRemoveById) { - if (rule_id != i) { - continue; - } - trasn->debug(9, "Rule id: " + std::to_string(rule_id) + - " was skipped due to an ruleRemoveById action..."); - return true; - } - - ruleMessage = new modsecurity::RuleMessage(this, m_log_message); - -#ifndef NO_LOGS - std::string eparam = MacroExpansion::expand(this->op->m_param, trasn); - - if (this->op->m_param != eparam) { - eparam = "\"" + eparam + "\" Was: \"" + this->op->m_param + "\""; - } else { - eparam = "\"" + eparam + "\""; - } - - trasn->debug(4, "(Rule: " + std::to_string(rule_id) \ - + ") Executing operator \"" + this->op->m_op \ - + "\" with param " \ - + eparam \ - + " against " \ - + Variable::to_s(variables) + "."); - - clock_t begin = clock(); -#endif - +std::vector Rule::getFinalVars( + Transaction *trasn) { std::list exclusions; + std::vector *variables = this->variables; + std::vector finalVars; + for (int i = 0; i < variables->size(); i++) { Variable *variable = variables->at(i); if (variable->m_isExclusion) { @@ -335,17 +394,16 @@ bool Rule::evaluate(Transaction *trasn) { } for (int i = 0; i < variables->size(); i++) { - int transformations = 0; Variable *variable = variables->at(i); + std::vector e; + bool ignoreVariable = false; + if (variable->m_isExclusion) { continue; } - std::vector e; variable->evaluateInternal(trasn, this, &e); - - for (auto &v : e) { - bool ignoreVariable = false; + for (const collection::Variable *v : e) { if (std::find(exclusions.begin(), exclusions.end(), v->m_key) != exclusions.end()) { #ifndef NO_LOGS @@ -371,6 +429,7 @@ bool Rule::evaluate(Transaction *trasn) { if (ignoreVariable) { continue; } + for (auto &i : trasn->m_ruleRemoveTargetById) { int id = i.first; std::string args = i.second; @@ -388,253 +447,208 @@ bool Rule::evaluate(Transaction *trasn) { continue; } - std::string value = v->m_value; - int none = 0; - for (Action *a : this->actions_runtime_pre) { - if (a->m_isNone) { - none++; - } - } + finalVars.push_back(v); + } + } + return finalVars; +} - // Check for transformations on the SecDefaultAction - // Notice that first we make sure that won't be a t:none - // on the target rule. - if (none == 0) { - for (Action *a : - trasn->m_rules->defaultActions[this->phase]) { - if (a->action_kind == - actions::Action::RunTimeBeforeMatchAttemptKind) { - value = a->evaluate(value, trasn); -#ifndef NO_LOGS - trasn->debug(9, "(SecDefaultAction) T (" + \ - std::to_string(transformations) + ") " + \ - a->m_name + ": \"" + \ - utils::string::limitTo(80, value) +"\""); -#endif - transformations++; - } - } - } - for (Action *a : this->actions_runtime_pre) { - if (none == 0) { - value = a->evaluate(value, trasn); -#ifndef NO_LOGS - trasn->debug(9, " T (" + \ - std::to_string(transformations) + ") " + \ - a->m_name + ": \"" + \ - utils::string::limitTo(80, value) + "\""); -#endif - transformations++; - } - if (a->m_isNone) { - none--; - } - } +void Rule::executeActionsAfterFullMatch(Transaction *trasn, + bool containsDisruptive, RuleMessage *ruleMessage) { -#ifndef NO_LOGS - trasn->debug(9, "Target value: \"" + utils::string::limitTo(80, - utils::string::toHexIfNeeded(value)) \ - + "\" (Variable: " + v->m_key + ")"); -#endif - - ret = this->op->evaluateInternal(trasn, this, value); - -#ifndef NO_LOGS - clock_t end = clock(); - double elapsed_secs = static_cast(end - begin) \ - / CLOCKS_PER_SEC; - - trasn->debug(4, "Operator completed in " + \ - std::to_string(elapsed_secs) + " seconds"); -#endif - - if (ret) { - bool containsDisruptive = false; - bool chainResult = false; - bool containsPassAction = false; - globalRet = true; - - if (this->op->m_match_message.empty() == true) { - ruleMessage->m_match = "Matched \"Operator `" + - this->op->m_op + "' with parameter `" + - utils::string::limitTo(200, this->op->m_param) + - "' against variable `" + v->m_key + "' (Value: `" + - utils::string::limitTo(100, - utils::string::toHexIfNeeded(value)) + - "' ) \" at " + v->m_key; - } else { - ruleMessage->m_match = this->op->m_match_message; - } -#ifndef NO_LOGS - trasn->debug(4, "Rule returned 1."); - trasn->m_collections.storeOrUpdateFirst("MATCHED_VAR", - value); - trasn->m_collections.storeOrUpdateFirst("MATCHED_VAR_NAME", - v->m_key); - trasn->m_collections.store("MATCHED_VARS:" - + v->m_key, value); - trasn->m_collections.store("MATCHED_VARS_NAMES:" - + v->m_key, v->m_key); -#endif - - for (Action *a : - this->actions_runtime_pos) { - if (a->isDisruptive() == true) { - if (a->m_name == "pass") { - containsPassAction = true; - trasn->debug(4, "Rule contains a `pass' action"); - } else { - containsDisruptive = true; - } - } else { - if (a->m_name == "setvar") { - trasn->debug(4, "Running [I] (_non_ disruptive) " \ - "action: " + a->m_name); - a->evaluate(this, trasn, ruleMessage); - } - } - } - - if (this->chained && this->chainedRule == NULL) { -#ifndef NO_LOGS - trasn->debug(4, "Rule is marked as chained but there " \ - "isn't a subsequent rule."); -#endif - } - if (this->chained && this->chainedRule != NULL) { -#ifndef NO_LOGS - trasn->debug(4, "Executing chained rule."); -#endif - chainResult = this->chainedRule->evaluate(trasn); - } - if ((this->chained && chainResult == true) || !this->chained) { - for (Action *a : - trasn->m_rules->defaultActions[this->phase]) { - if (a->action_kind - == actions::Action::RunTimeOnlyIfMatchKind) { - if (a->isDisruptive()) { - if (containsDisruptive) { -#ifndef NO_LOGS - trasn->debug(4, - "(SecDefaultAction) _ignoring_ " \ - "action: " + a->m_name + \ - " (rule contains a disruptive action)"); -#endif - } else { - if (trasn->m_rules->m_secRuleEngine - == Rules::EnabledRuleEngine) { -#ifndef NO_LOGS - trasn->debug(4, "(SecDefaultAction) " \ - "Running action: " + a->m_name + \ - " (rule _does not_ contains a " \ - "disruptive action)"); -#endif - a->evaluate(this, trasn, ruleMessage); - } else { -#ifndef NO_LOGS - trasn->debug(4, "(SecDefaultAction) " \ - "_Not_ running action: " \ - + a->m_name + ". Rule _does not_" \ - + " contains a disruptive action,"\ - + " but SecRuleEngine is not On."); -#endif - } - } - } else { -#ifndef NO_LOGS - trasn->debug(4, "(SecDefaultAction) Running " \ - "action: " + a->m_name + "!!" \ - + std::to_string(a->isDisruptive())); -#endif - a->evaluate(this, trasn, ruleMessage); - } - } - } - for (Action *a : - this->actions_runtime_pos) { - if (a->isDisruptive() - && trasn->m_rules->m_secRuleEngine - == Rules::EnabledRuleEngine - && containsPassAction == false) { -#ifndef NO_LOGS - trasn->debug(4, "Running (disruptive) " \ - "action: " + a->m_name); -#endif - a->evaluate(this, trasn); - } else if (a->isDisruptive() - && containsPassAction == false) { -#ifndef NO_LOGS - trasn->debug(4, - "Not running disruptive action: " + \ - a->m_name + ". SecRuleEngine " + \ - "is not On"); -#endif - } else if (a->isDisruptive() && - containsPassAction == true) { - if (a->m_name != "pass") { -#ifndef NO_LOGS - trasn->debug(4, "Not running disruptive " \ - "action: " + a->m_name + ". It was " \ - "silenced by an `pass' action."); -#endif - } - } else if (!a->isDisruptive()) { - if (a->m_name != "capture" \ - && a->m_name != "setvar") { -#ifndef NO_LOGS - trasn->debug(4, "Running [II] " \ - "(_non_ disruptive) " \ - "action: " + a->m_name); -#endif - a->evaluate(this, trasn, ruleMessage); - } - } - } - } - } else if (globalRet != true) { -#ifndef NO_LOGS - trasn->debug(4, "Rule returned 0."); - trasn->m_collections.storeOrUpdateFirst("MATCHED_VAR", ""); - trasn->m_collections.del("MATCHED_VARS:" + v->m_key); - trasn->m_collections.del("MATCHED_VARS_NAMES:" + v->m_key); - trasn->m_collections.del("MATCHED_VARS_NAME"); -#endif - } + for (Action *a : trasn->m_rules->defaultActions[this->phase]) { + if (a->action_kind != actions::Action::RunTimeOnlyIfMatchKind) { + continue; } - while (e.empty() == false) { - delete e.back(); - e.pop_back(); + if (a->isDisruptive() == false) { + trasn->debug(4, "(SecDefaultAction) Running " \ + "action: " + a->m_name); + a->evaluate(this, trasn, ruleMessage); + continue; + } + + if (containsDisruptive) { + trasn->debug(4, "(SecDefaultAction) _ignoring_ " \ + "action: " + a->m_name + \ + " (rule contains a disruptive action)"); + continue; + } + + if (trasn->m_rules->m_secRuleEngine == Rules::EnabledRuleEngine) { + trasn->debug(4, "(SecDefaultAction) " \ + "Running action: " + a->m_name + \ + " (rule _does not_ contains a " \ + "disruptive action)"); + a->evaluate(this, trasn, ruleMessage); + continue; + } + + trasn->debug(4, "(SecDefaultAction) _Not_ running action: " \ + + a->m_name + ". Rule _does not_contains a disruptive action,"\ + + " but SecRuleEngine is not On."); + } + + for (Action *a : this->m_actionsRuntimePos) { + if (a->isDisruptive() == false) { + if (a->m_name != "setvar" && a->m_name != "log" + && a->m_name != "msg") { + trasn->debug(4, "Running [I] (_non_ disruptive) " \ + "action: " + a->m_name); + a->evaluate(this, trasn, ruleMessage); + } + continue; + } + if (trasn->m_rules->m_secRuleEngine == Rules::EnabledRuleEngine) { + trasn->debug(4, "Running (disruptive) action: " + a->m_name); + a->evaluate(this, trasn, ruleMessage); + continue; + } else { + continue; + } + + trasn->debug(4, "Not running disruptive action: " + \ + a->m_name + ". SecRuleEngine is not On"); + } + + for (auto &z : ruleMessage->m_tmp_actions) { + trasn->m_actions.push_back(z); + } +} + + +bool Rule::evaluate(Transaction *trasn) { + bool isThisAChainedRule = rule_id == 0; + bool globalRet = false; + std::vector *variables = this->variables; + bool recursiveGlobalRet; + bool containsDisruptive = false; + RuleMessage ruleMessage(this); + + trasn->m_matched.clear(); + + if (m_secmarker == true) { + return true; + } + if (m_unconditional == true) { + trasn->debug(4, "(Rule: " + std::to_string(rule_id) \ + + ") Executing unconditional rule..."); + executeActionsIndependentOfChainedRuleResult(trasn, + &containsDisruptive, &ruleMessage); + executeActionsAfterFullMatch(trasn, false, &ruleMessage); + return true; + } + + for (auto &i : trasn->m_ruleRemoveById) { + if (rule_id != i) { + continue; + } + trasn->debug(9, "Rule id: " + std::to_string(rule_id) + + " was skipped due to an ruleRemoveById action..."); + return true; + } + + std::string eparam = MacroExpansion::expand(this->op->m_param, trasn); + + if (this->op->m_param != eparam) { + eparam = "\"" + eparam + "\" Was: \"" + this->op->m_param + "\""; + } else { + eparam = "\"" + eparam + "\""; + } + + trasn->debug(4, "(Rule: " + std::to_string(rule_id) \ + + ") Executing operator \"" + this->op->m_op \ + + "\" with param " \ + + eparam \ + + " against " \ + + Variable::to_s(variables) + "."); + + updateRulesVariable(trasn); + + std::vector finalVars = getFinalVars(trasn); + + for (const collection::Variable *v : finalVars) { + std::string value = v->m_value; + std::vector values; + bool ret; + bool multiMatch = getActionsByName("multimatch").size() > 0; + + values = executeSecDefaultActionTransofrmations(trasn, value, + multiMatch); + + for (const std::string &valueTemp : values) { + ret = executeOperatorAt(trasn, v->m_key, valueTemp); + if (ret == true) { + ruleMessage.m_match = resolveMatchMessage(v->m_key, value); + updateMatchedVars(trasn, v->m_key, value); + executeActionsIndependentOfChainedRuleResult(trasn, + &containsDisruptive, &ruleMessage); + std::string msg2save = ruleMessage.errorLog(trasn); + if (ruleMessage.m_message.empty() == false) { + trasn->debug(4, "Scheduled to be saved on the server " + + "log: " + msg2save + ""); + ruleMessage.m_server_logs.push_back(msg2save); + } + globalRet = true; + } } } - if (globalRet == true && rule_id != 0 - && ruleMessage->m_saveMessage == true) { - ruleMessage->m_message = m_log_message; - trasn->debug(4, "Saving on the server log: " - + ruleMessage->errorLog(trasn)); - trasn->serverLog(ruleMessage->errorLog(trasn)); + if (globalRet == false) { + trasn->debug(4, "Rule returned 0."); + cleanMatchedVars(trasn); + return false; } - delete ruleMessage; - return globalRet; + trasn->debug(4, "Rule returned 1."); + + if (this->chained == false) { + executeActionsAfterFullMatch(trasn, containsDisruptive, &ruleMessage); + trasn->debug(4, "Merging temporary actions to the main transaction."); + + for (const auto &u : ruleMessage.m_server_logs) { + trasn->debug(8, "To save: " + u); + trasn->serverLog(u); + } + return true; + } + + if (this->chainedRule == NULL) { + trasn->debug(4, "Rule is marked as chained but there " \ + "isn't a subsequent rule."); + return false; + } + + trasn->debug(4, "Executing chained rule."); + recursiveGlobalRet = this->chainedRule->evaluate(trasn); + + if (recursiveGlobalRet == true) { + executeActionsAfterFullMatch(trasn, containsDisruptive, &ruleMessage); + trasn->debug(4, "Merging temporary actions to the main transaction."); + for (const auto &u : ruleMessage.m_server_logs) { + trasn->serverLog(u); + } + return true; + } + + return false; } std::vector Rule::getActionsByName(const std::string& name) { std::vector ret; - for (auto &z : this->actions_runtime_pos) { + for (auto &z : m_actionsRuntimePos) { if (z->m_name == name) { ret.push_back(z); } } - for (auto &z : this->actions_runtime_pre) { + for (auto &z : m_actionsRuntimePre) { if (z->m_name == name) { ret.push_back(z); } } - for (auto &z : this->actions_conf) { + for (auto &z : m_actionsConf) { if (z->m_name == name) { ret.push_back(z); } @@ -645,7 +659,7 @@ std::vector Rule::getActionsByName(const std::string& name) { bool Rule::containsTag(const std::string& name, Transaction *t) { std::vector ret; - for (auto &z : this->actions_runtime_pos) { + for (auto &z : this->m_actionsRuntimePos) { actions::Tag *tag = dynamic_cast (z); if (tag != NULL && tag->getName(t) == name) { return true; @@ -654,4 +668,5 @@ bool Rule::containsTag(const std::string& name, Transaction *t) { return false; } + } // namespace modsecurity diff --git a/src/variables/rule.cc b/src/variables/rule.cc index 8635fd38..e4cf4182 100644 --- a/src/variables/rule.cc +++ b/src/variables/rule.cc @@ -53,57 +53,7 @@ namespace Variables { void Rule::evaluateInternal(Transaction *t, modsecurity::Rule *rule, std::vector *l) { - std::map envs; - std::string m_name_upper = utils::string::toupper(m_name); - - // 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) { - std::string data = a->data(t); - envs.insert(std::pair("RULE:msg", - data)); - } - } - - for (auto& x : envs) { - std::string xup = utils::string::toupper(x.first); - if ((xup.substr(0, m_name_upper.size() + 1) - .compare(m_name_upper + ":") != 0) - && (xup != m_name_upper)) { - continue; - } - l->push_back(new collection::Variable(x.first, x.second)); - } + // Variable rule is now being saved as part of the transient collection. } } // namespace Variables