diff --git a/src/operators/contains_word.cc b/src/operators/contains_word.cc index 6c9eb296..1988f4df 100644 --- a/src/operators/contains_word.cc +++ b/src/operators/contains_word.cc @@ -18,47 +18,57 @@ #include #include "operators/operator.h" +#include "src/macro_expansion.h" namespace ModSecurity { namespace operators { +bool ContainsWord::acceptableChar(const std::string& a, size_t pos) { + if (a.size() - 1 < pos) { + return false; + } + + if ((a.at(pos) >= 65 && a.at(pos) <= 90) || + (a.at(pos) >= 97 && a.at(pos) <= 122)) { + return false; + } + + return true; +} + bool ContainsWord::evaluate(Assay *assay, - std::string input) { - /** - * @todo Implement the operator ContainsWord in a performative way. - */ + const std::string& input) { + std::string paramTarget = MacroExpansion::expand(param, assay); - // FIXME: This is odd logic and should be removed in a future version - if (this->param == "") { - return 1; + if (paramTarget.empty()) { + return true; } - // If our length is too long we will never match - if (this->param.length() > input.length()) { - return 0; + if (input.empty()) { + return false; } - // If they are exact matches shortcut - if (this->param == input) { - return 1; + if (input == paramTarget) { + return true; } - // std::regex r("\\b" + this->param + "\\b"); - // std::smatch m; - // if (std::regex_search(input, m, r)) { - // this won't find anything because 'spoons' is not - // the word you're searching for - // return 1; - // } + size_t pos = input.find(paramTarget); + while (pos != std::string::npos) { + if (pos == 0 && acceptableChar(input, paramTarget.size())) { + return true; + } + if (pos + paramTarget.size() == input.size() && + acceptableChar(input, pos - 1)) { + return true; + } + if (acceptableChar(input, pos - 1) && + acceptableChar(input, pos + paramTarget.size())) { + return true; + } + pos = input.find(paramTarget, pos + 1); + } - return 0; + return false; } -ContainsWord::ContainsWord(std::string op, - std::string param, bool negation) - : Operator() { - this->op = op; - this->param = param; -} - } // namespace operators } // namespace ModSecurity diff --git a/src/operators/contains_word.h b/src/operators/contains_word.h index b366f352..d615fd4a 100644 --- a/src/operators/contains_word.h +++ b/src/operators/contains_word.h @@ -27,8 +27,12 @@ namespace operators { class ContainsWord : public Operator { public: /** @ingroup ModSecurity_Operator */ - ContainsWord(std::string o, std::string p, bool i); - bool evaluate(Assay *assay, std::string exp); + ContainsWord(std::string op, std::string param, bool negation) + : Operator(op, param, negation) { } + + bool evaluate(Assay *assay, const std::string &str); + + bool acceptableChar(const std::string& a, size_t pos); }; } // namespace operators diff --git a/src/operators/operator.cc b/src/operators/operator.cc index c238a935..7984c6ba 100644 --- a/src/operators/operator.cc +++ b/src/operators/operator.cc @@ -59,7 +59,7 @@ #include "operators/begins_with.h" #define IF_MATCH(a) \ - if (op.compare(1, std::strlen(#a), #a) == 0) + if (op.compare(1, op.length() - 2, #a) == 0) namespace ModSecurity { namespace operators { @@ -75,6 +75,9 @@ bool Operator::evaluate(Assay *assay) { if (assay) { assay->debug(2, "Operator: " + this->op + \ " is not implemented or malfunctioning."); + } else { + std::cerr << "Operator: " + this->op + \ + " is not implemented or malfunctioning."; } return true; } @@ -84,6 +87,9 @@ bool Operator::evaluate(Assay *assay, const std::string& a) { if (assay) { assay->debug(2, "Operator: " + this->op + \ " is not implemented or malfunctioning."); + } else { + std::cerr << "Operator: " + this->op + \ + " is not implemented or malfunctioning."; } return true; diff --git a/test/test-cases/secrules-language-tests b/test/test-cases/secrules-language-tests index a65639f9..93eddeff 160000 --- a/test/test-cases/secrules-language-tests +++ b/test/test-cases/secrules-language-tests @@ -1 +1 @@ -Subproject commit a65639f93590edc93ee1c78647cf4b8147e2025d +Subproject commit 93eddefff48e295179d6884691ecb24c362735be diff --git a/test/unit/unit_test.cc b/test/unit/unit_test.cc index 011aca5c..a1fdae80 100644 --- a/test/unit/unit_test.cc +++ b/test/unit/unit_test.cc @@ -93,7 +93,7 @@ UnitTest *UnitTest::from_yajl_node(yajl_val &node) { u->param = YAJL_GET_STRING(val); } else if (strcmp(key, "input") == 0) { u->input = YAJL_GET_STRING(val); - replaceAll(&(u->input), "\\0", '\0'); + replaceAll(&(u->input), "\\0", '\u0000'); replaceAll(&(u->input), "\\xe4", '\xe4'); replaceAll(&(u->input), "\\x03", '\x03'); replaceAll(&(u->input), "\\xbf", '\xbf');