diff --git a/src/operators/pm_from_file.cc b/src/operators/pm_from_file.cc index f70677cdc6..8016c9cb5f 100644 --- a/src/operators/pm_from_file.cc +++ b/src/operators/pm_from_file.cc @@ -20,7 +20,9 @@ #include "src/operators/operator.h" #include "src/utils/https_client.h" #include "src/utils/system.h" +#include "src/utils/string.h" +using namespace modsecurity::utils::string; namespace modsecurity { namespace operators { @@ -44,39 +46,47 @@ bool PmFromFile::isComment(const std::string &s) { } bool PmFromFile::init(const std::string &config, std::string *error) { - std::istream *iss; - - if (m_param.compare(0, 8, "https://") == 0) { - Utils::HttpsClient client; - bool ret = client.download(m_param); - if (ret == false) { - error->assign(client.error); - return false; - } - iss = new std::stringstream(client.content); - } else { - std::string err; - std::string resource = utils::find_resource(m_param, config, &err); - iss = new std::ifstream(resource, std::ios::in); + std::vector tokens = split(m_param, ' '); + + for (const auto& token : tokens) { + if (! token.empty()) { + + std::istream *iss; + + if (token.compare(0, 8, "https://") == 0) { + Utils::HttpsClient client; + bool ret = client.download(token); + if (ret == false) { + error->assign(client.error); + return false; + } + iss = new std::stringstream(client.content); + } else { + std::string err; + std::string resource = utils::find_resource(token, config, &err); + iss = new std::ifstream(resource, std::ios::in); + + if (((std::ifstream *)iss)->is_open() == false) { + error->assign("Failed to open file: '" + token + "'. " + err); + delete iss; + return false; + } + } + + for (std::string line; std::getline(*iss, line); ) { + if (isComment(line) == false) { + acmp_add_pattern(m_p, line.c_str(), NULL, NULL, line.length()); + } + } - if (((std::ifstream *)iss)->is_open() == false) { - error->assign("Failed to open file: " + m_param + ". " + err); delete iss; - return false; } } - for (std::string line; std::getline(*iss, line); ) { - if (isComment(line) == false) { - acmp_add_pattern(m_p, line.c_str(), NULL, NULL, line.length()); - } - } - while (m_p->is_failtree_done == 0) { acmp_prepare(m_p); } - delete iss; return true; } diff --git a/test/test-cases/data/pattern-file1.data b/test/test-cases/data/pattern-file1.data new file mode 100644 index 0000000000..f6533b3ddb --- /dev/null +++ b/test/test-cases/data/pattern-file1.data @@ -0,0 +1,2 @@ +# comment +pattern1 \ No newline at end of file diff --git a/test/test-cases/data/pattern-file2.data b/test/test-cases/data/pattern-file2.data new file mode 100644 index 0000000000..be3c95d1d4 --- /dev/null +++ b/test/test-cases/data/pattern-file2.data @@ -0,0 +1,2 @@ +# comment +pattern2 diff --git a/test/test-cases/regression/operator-pmfromfile.json b/test/test-cases/regression/operator-pmfromfile.json new file mode 100644 index 0000000000..13b92b6ce5 --- /dev/null +++ b/test/test-cases/regression/operator-pmfromfile.json @@ -0,0 +1,39 @@ +[ + { + "enabled": 1, + "version_min": 300000, + "version_max": 0, + "title": "pmFromFile operator test", + "client": { + "ip": "10.20.30.40", + "port": 2313 + }, + "server": { + "ip": "1.2.3.4", + "port": 80 + }, + "request": { + "headers": { + "Host": "foobar.com" + }, + "uri": "\/test.php?param1=pattern2", + "method": "GET", + "http_version": 1.1, + "body": "" + }, + "response": { + "headers": { + "Content-Type": "text\/html; charset=utf-8\n\r", + "Content-Length": "10\n\r" + } + }, + "expected": { + "debug_log": "Rule returned 1", + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRule ARGS \"@pmFromFile test-cases/data/pattern-file1.data test-cases/data/pattern-file2.data\" \"phase:1,id:999,deny\"" + ] + } +] \ No newline at end of file diff --git a/test/test-suite.in b/test/test-suite.in index f74834dd27..6e8754254b 100644 --- a/test/test-suite.in +++ b/test/test-suite.in @@ -86,6 +86,7 @@ TESTS+=test/test-cases/regression/operator-fuzzyhash.json TESTS+=test/test-cases/regression/operator-inpectFile.json TESTS+=test/test-cases/regression/operator-ipMatchFromFile.json TESTS+=test/test-cases/regression/operator-pm.json +TESTS+=test/test-cases/regression/operator-pmfromfile.json TESTS+=test/test-cases/regression/operator-rx.json TESTS+=test/test-cases/regression/operator-rxGlobal.json TESTS+=test/test-cases/regression/operator-UnconditionalMatch.json