diff --git a/src/operators/ip_match.h b/src/operators/ip_match.h index 217d82da..a7417882 100644 --- a/src/operators/ip_match.h +++ b/src/operators/ip_match.h @@ -33,9 +33,9 @@ class IpMatch : public Operator { bool evaluate(Assay *assay, const std::string &input); - bool init(const char **error); + virtual bool init(const char **error); - private: + protected: Utils::IpTree m_tree; }; diff --git a/src/operators/ip_match_f.cc b/src/operators/ip_match_f.cc index c432edda..c2377746 100644 --- a/src/operators/ip_match_f.cc +++ b/src/operators/ip_match_f.cc @@ -22,21 +22,6 @@ namespace ModSecurity { namespace operators { -bool IpMatchF::evaluate(Assay *assay) { - /** - * @todo Implement the operator IpMatchF. - * Reference: https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#ipmatchf - */ - return true; -} - - -IpMatchF::IpMatchF(std::string op, std::string param, - bool negation) - : Operator() { - this->op = op; - this->param = param; -} } // namespace operators } // namespace ModSecurity diff --git a/src/operators/ip_match_f.h b/src/operators/ip_match_f.h index c9b58589..ead427be 100644 --- a/src/operators/ip_match_f.h +++ b/src/operators/ip_match_f.h @@ -18,17 +18,16 @@ #include -#include "operators/operator.h" +#include "operators/ip_match_from_file.h" #ifdef __cplusplus namespace ModSecurity { namespace operators { -class IpMatchF : public Operator { +class IpMatchF : public IpMatchFromFile { public: - /** @ingroup ModSecurity_Operator */ - IpMatchF(std::string p, std::string o, bool i); - bool evaluate(Assay *assay); + IpMatchF(std::string op, std::string param, bool negation) + : IpMatchFromFile(op, param, negation) { } }; } // namespace operators diff --git a/src/operators/ip_match_from_file.cc b/src/operators/ip_match_from_file.cc index 11c50397..faea0c7b 100644 --- a/src/operators/ip_match_from_file.cc +++ b/src/operators/ip_match_from_file.cc @@ -22,21 +22,24 @@ namespace ModSecurity { namespace operators { -bool IpMatchFromFile::evaluate(Assay *assay) { - /** - * @todo Implement the operator IpMatchFromFile. - * Reference: https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#ipmatchfromfile - */ - return true; + +bool IpMatchFromFile::init(const char **error) { + std::string e(""); + bool res = false; + + if (param.compare(0, 8, "https://") == 0) { + res = m_tree.addFromUrl(param, &e); + } else { + res = m_tree.addFromFile(param, &e); + } + + if (res == false) { + *error = e.c_str(); + } + + return res; } -IpMatchFromFile::IpMatchFromFile(std::string op, std::string param, - bool negation) - : Operator() { - this->op = op; - this->param = param; -} - } // namespace operators } // namespace ModSecurity diff --git a/src/operators/ip_match_from_file.h b/src/operators/ip_match_from_file.h index 04553fff..61649f54 100644 --- a/src/operators/ip_match_from_file.h +++ b/src/operators/ip_match_from_file.h @@ -17,17 +17,19 @@ #include -#include "operators/operator.h" +#include "operators/ip_match.h" #ifdef __cplusplus namespace ModSecurity { namespace operators { -class IpMatchFromFile : public Operator { +class IpMatchFromFile : public IpMatch { public: /** @ingroup ModSecurity_Operator */ - IpMatchFromFile(std::string o, std::string p, bool i); - bool evaluate(Assay *assay); + IpMatchFromFile(std::string op, std::string param, bool negation) + : IpMatch(op, param, negation) { } + + bool init(const char **error) override; }; } // namespace operators diff --git a/src/utils/ip_tree.cc b/src/utils/ip_tree.cc index bbdc50c8..9dc79b1d 100644 --- a/src/utils/ip_tree.cc +++ b/src/utils/ip_tree.cc @@ -25,6 +25,7 @@ #include #include "utils/geo_lookup.h" +#include "utils/https_client.h" namespace ModSecurity { namespace Utils { @@ -77,16 +78,12 @@ IpTree::~IpTree() { } } - -bool IpTree::addFromBuffer(const std::string& buffer, std::string *error) { +bool IpTree::addFromBuffer(std::istream *ss, std::string *error) { char *error_msg = NULL; - std::stringstream ss; - std::string line; - ss << buffer; int res = 0; - for (std::string line; std::getline(ss, line); ) { - res = ip_tree_from_param(buffer.c_str(), &m_tree, &error_msg); + for (std::string line; std::getline(*ss, line); ) { + res = ip_tree_from_param(line.c_str(), &m_tree, &error_msg); if (res != 0) { if (error_msg != NULL) { error->assign(error_msg); @@ -99,6 +96,40 @@ bool IpTree::addFromBuffer(const std::string& buffer, std::string *error) { } +bool IpTree::addFromBuffer(const std::string& buffer, std::string *error) { + std::stringstream ss; + ss << buffer; + + return addFromBuffer(&ss, error); +} + + +bool IpTree::addFromFile(const std::string& file, std::string *error) { + std::ifstream myfile(file, std::ios::in); + + if (myfile.is_open() == false) { + error->assign("Failed to open file: " + file); + return false; + } + + return addFromBuffer(&myfile, error); +} + + +bool IpTree::addFromUrl(const std::string& url, std::string *error) { + HttpsClient c; + bool ret = c.download(url); + + if (ret == false) { + error->assign(c.error); + } else { + ret = addFromBuffer(c.content, error); + } + + return ret; +} + + bool IpTree::contains(const std::string& ip) { int res = 0; char *error_msg = NULL; diff --git a/src/utils/ip_tree.h b/src/utils/ip_tree.h index fc67dfdf..5c17f8bc 100644 --- a/src/utils/ip_tree.h +++ b/src/utils/ip_tree.h @@ -37,8 +37,11 @@ class IpTree { ~IpTree(); bool contains(const std::string &ip); - bool addFromBuffer(const std::string& buffer, std::string *error); void postOrderTraversal(TreeNode *node); + bool addFromBuffer(std::istream *ss, std::string *error); + bool addFromBuffer(const std::string& buffer, std::string *error); + bool addFromFile(const std::string& file, std::string *error); + bool addFromUrl(const std::string& url, std::string *error); private: TreeRoot *m_tree; }; diff --git a/test/test-cases/data/ipMatchFromFile.txt b/test/test-cases/data/ipMatchFromFile.txt new file mode 100644 index 00000000..d55f6085 --- /dev/null +++ b/test/test-cases/data/ipMatchFromFile.txt @@ -0,0 +1,4 @@ +127.0.0.1 +10.10.10.1 +::1 +200.249.12.31 diff --git a/test/test-cases/regression/operator-ipMatchFromFile.json b/test/test-cases/regression/operator-ipMatchFromFile.json new file mode 100644 index 00000000..e872fcfe --- /dev/null +++ b/test/test-cases/regression/operator-ipMatchFromFile.json @@ -0,0 +1,140 @@ +[ + { + "enabled":1, + "version_min":300000, + "title":"Testing Operator :: @ipMatchFromFile", + "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":"/", + "protocol":"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":"Rule returned 1" + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecDebugLogLevel 9", + "SecRule REMOTE_ADDR \"@ipMatchFromFile test-cases\/data\/ipMatchFromFile.txt\" \"phase:3,pass,t:trim\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing Operator :: @ipMatchFromFile - file not found", + "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":"/", + "protocol":"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":{ + "parser_error":"Failed to open file: file-not-found.txt" + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecDebugLogLevel 9", + "SecRule REMOTE_ADDR \"@ipMatchFromFile file-not-found.txt\" \"phase:3,pass,t:trim\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing Operator :: @ipMatchFromFile - https", + "client":{ + "ip":"8.8.4.4", + "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":"/", + "protocol":"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":"Rule returned 1." + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecDebugLogLevel 9", + "SecRule REMOTE_ADDR \"@ipMatchFromFile https://www.modsecurity.org/modsecurity-regression-test.txt\" \"phase:3,pass,t:trim\"" + ] + } +] \ No newline at end of file