From a676f313c3f9277ae007b09d79fa698f61947ce6 Mon Sep 17 00:00:00 2001 From: Felipe Zimmerle Date: Sun, 5 Nov 2017 10:42:40 -0300 Subject: [PATCH] Initial support for Lua script engine --- CHANGES | 2 + src/Makefile.am | 2 +- src/engine/lua.cc | 377 ++++++++++++++ src/engine/lua.h | 101 ++++ src/engines/lua.cc | 32 -- src/operators/inspect_file.cc | 53 +- src/operators/inspect_file.h | 9 +- src/parser/seclang-parser.cc | 2 +- src/parser/seclang-parser.yy | 2 +- src/variables/variable.h | 481 ++++++++++++++++++ test/Makefile.am | 3 + .../data/match-getvar-transformation.lua | 20 + test/test-cases/data/match-getvar.lua | 19 + test/test-cases/data/match-getvars.lua | 21 + test/test-cases/data/match-log.lua | 4 + test/test-cases/data/match-set.lua | 5 + test/test-cases/data/match.lua | 3 + .../lua.h => test/test-cases/data/test.lua | 0 .../regression/operator-inpectFile.json | 193 +++++++ 19 files changed, 1270 insertions(+), 59 deletions(-) create mode 100644 src/engine/lua.cc create mode 100644 src/engine/lua.h delete mode 100644 src/engines/lua.cc create mode 100644 test/test-cases/data/match-getvar-transformation.lua create mode 100644 test/test-cases/data/match-getvar.lua create mode 100644 test/test-cases/data/match-getvars.lua create mode 100644 test/test-cases/data/match-log.lua create mode 100644 test/test-cases/data/match-set.lua create mode 100644 test/test-cases/data/match.lua rename src/engines/lua.h => test/test-cases/data/test.lua (100%) diff --git a/CHANGES b/CHANGES index a18310e3..df74dbe8 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,8 @@ v3.0.????? - ? --------------------------- + - Adds initial support for Lua engine. + [Issue #994 - @zimmerle] - Adds support for @inspectFile operator. [Issue #999 - @zimmerle, @victorhora] - Adds support for RESOURCE variable collection. diff --git a/src/Makefile.am b/src/Makefile.am index 1d48167c..f7c4809f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -79,7 +79,7 @@ noinst_HEADERS = \ ENGINES = \ - engines/lua.cc + engine/lua.cc VARIABLES = \ diff --git a/src/engine/lua.cc b/src/engine/lua.cc new file mode 100644 index 00000000..183933a0 --- /dev/null +++ b/src/engine/lua.cc @@ -0,0 +1,377 @@ +/* + * ModSecurity, http://www.modsecurity.org/ + * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/) + * + * You may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Trustwave Holdings, Inc. + * directly using the email address security@modsecurity.org. + * + */ + + +#include "modsecurity/modsecurity.h" +#include "src/engine/lua.h" +#include "src/utils/string.h" +#include "src/macro_expansion.h" +#include "modsecurity/transaction.h" +#include "modsecurity/collection/variable.h" +#include "src/variables/variable.h" +#include "src/variables/highest_severity.h" +#include "src/utils/string.h" +#include "src/actions/transformations/transformation.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + + +namespace modsecurity { +namespace engine { + + +bool Lua::isCompatible(std::string script, Lua *l, std::string *error) { + std::string lua(".lua"); + std::string err; + + if (!(script.size() >= lua.size() && + script.compare(script.size() - lua.size(), lua.size(), lua) == 0)) + { + error->assign("Expecting a Lua script: " + script); + return false; + } + + if (l->load(script, &err) == false) { + error->assign("Problems load script: " + err); + return false; + } + + return true; +} + + +bool Lua::load(std::string script, std::string *err) { + lua_State *L = NULL; + L = luaL_newstate(); + luaL_openlibs(L); + + m_scriptName = script; + if (luaL_loadfile(L, script.c_str())) { + const char *luaerr = lua_tostring(L, -1); + err->assign("Failed to compile script '" + script + ""); + if (luaerr) { + err->append(": " + *luaerr); + } + err->append("."); + lua_close(L); + + return false; + } + + if (lua_dump(L, Lua::blob_keeper, (void *)&m_blob, 0)) { + const char *luaerr = lua_tostring(L, -1); + err->assign("Failed to compile script '" + script + ""); + if (luaerr) { + err->append(": " + *luaerr); + } + err->append("."); + lua_close(L); + + return false; + } + + + lua_close(L); + return true; +} + + +int Lua::blob_keeper(lua_State *L, const void *p, size_t sz, void *ud) { + LuaScriptBlob *lsb = static_cast(ud); + lsb->write(p, sz); + return 0; +} + + +const char *Lua::blob_reader(lua_State *L, void *ud, size_t *size) { + LuaScriptBlob *lsb = static_cast(ud); + const char *data = lsb->read(size); + return data; +} + + +int Lua::run(Transaction *t) { + std::string luaRet; + lua_State *L = luaL_newstate(); + luaL_openlibs(L); + + luaL_newmetatable(L, "luaL_msc"); + lua_newtable(L); + + lua_pushlightuserdata(L, (void *)t); + lua_setglobal(L, "__transaction"); + + luaL_setfuncs(L, mscLuaLib, 0); + lua_setglobal(L, "m"); + + int rc = lua_load(L, Lua::blob_reader, &m_blob, m_scriptName.c_str(), + NULL); + if (rc != LUA_OK) { + std::string e; + e.assign("Failed to execute lua script: " + m_scriptName + ". "); + switch (rc) { + case LUA_ERRSYNTAX: + e.assign("Syntax error. "); + break; + case LUA_ERRMEM: + e.assign("Memory error. "); + break; + case LUA_ERRGCMM: + e.assign("Garbage Collector error. "); + break; + } + e.append(lua_tostring(L, -1)); + t->debug(2, e); + return false; + } + + if (lua_pcall(L, 0, 0, 0)) { + std::string e; + const char *luaerr = lua_tostring(L, -1); + e.assign("Failed to execute lua script: " + m_scriptName \ + + " (before main)"); + if (luaerr != NULL) { + e.append(" - "); + e.append(luaerr); + } + t->debug(2, e); + return false; + } + + lua_setglobal(L, "modsec"); + + lua_getglobal(L, "main"); + if (lua_pcall(L, 0, 1, 0)) { + std::string e; + const char *luaerr = lua_tostring(L, -1); + e.assign("Failed to execute lua script: " + m_scriptName + " (main)"); + if (luaerr != NULL) { + e.append(" - "); + e.append(luaerr); + } + t->debug(2, e); + return false; + } + + char *a = (char *)lua_tostring(L, -1); + if (a != NULL) { + luaRet.assign(a); + } + + t->debug(9, "Returning from lua script: " + luaRet); + + lua_pop(L, 1); + lua_close(L); + + if (luaRet.size() == 0) { + return false; + } + + return true; +} + + +int Lua::log(lua_State *L) { + Transaction *t = NULL; + const char *text; + int level; + + /* Retrieve parameters. */ + level = luaL_checknumber(L, 1); + text = luaL_checkstring(L, 2); + + /* Retrieve msr. */ + lua_getglobal(L, "__transaction"); + t = (Transaction *)lua_topointer(L, -1); + + /* Log message. */ + if (t != NULL) { + t->debug(level, text); + } + + return 0; +} + + +int Lua::getvar(lua_State *L) { + char *varname = NULL; + Transaction *t = NULL; + + /* Retrieve parameters. */ + varname = (char *)luaL_checkstring(L, 1); + + lua_getglobal(L, "__transaction"); + t = (Transaction *)lua_topointer(L, -1); + + std::string var = Variables::Variable::stringMatchResolve(t, varname); + var = applyTransformations(L, t, 2, var); + + if (var.size() == 0) { + lua_pushnil(L); + return 0; + } + + lua_pushlstring(L, var.c_str(), var.size()); + + return 1; +} + + +int Lua::getvars(lua_State *L) { + char *varname = NULL; + Transaction *t = NULL; + std::vector l; + int idx = 1; + + /* Retrieve parameters. */ + varname = (char *)luaL_checkstring(L, 1); + + lua_getglobal(L, "__transaction"); + t = (Transaction *)lua_topointer(L, -1); + + Variables::Variable::stringMatchResolveMulti(t, varname, &l); + + lua_newtable(L); + for (auto i : l) { + lua_pushnumber(L, idx); + lua_newtable(L); + + lua_pushstring(L, "name"); + lua_pushlstring(L, i->m_key.c_str(), i->m_key.size()); + lua_settable(L, -3); + + lua_pushstring(L, "value"); + lua_pushlstring(L, i->m_value.c_str(), i->m_value.size()); + lua_settable(L, -3); + + lua_settable(L, -3); + idx++; + } + + return 1; +} + + +int Lua::setvar(lua_State *L) { + Transaction *t = NULL; + const char *var_value = NULL; + const char *var_name = NULL; + std::string vname; + std::string collection; + std::string variableName; + int nargs = lua_gettop(L); + char *chr = NULL; + size_t pos; + + lua_getglobal(L, "__transaction"); + t = (Transaction *)lua_topointer(L, -1); + + if (nargs != 2) { + t->debug(8, "m.setvar: Failed m.setvar funtion must has 2 arguments"); + return -1; + } + var_value = luaL_checkstring(L, 2); + var_name = luaL_checkstring(L, 1); + + lua_pop(L, 2); + + if (var_value == NULL || var_name == NULL) { + return -1; + } + + vname.assign(var_name); + pos = vname.find("."); + if (pos != std::string::npos) { + collection = std::string(vname, 0, pos); + collection = utils::string::toupper(collection); + variableName = std::string(vname, pos + 1, + std::string::npos); + + } else { + t->debug(8, "m.setvar: Must specify a collection using dot character" \ + " - ie m.setvar(tx.myvar,mydata)"); + return -1; + } + + t->m_collections.storeOrUpdateFirst(collection, + variableName, var_value); +} + + +std::string Lua::applyTransformations(lua_State *L, Transaction *t, int idx, std::string var) { + std::string newVar = var; + + if (lua_isuserdata(L, idx) || lua_isnoneornil(L, idx)) { + return var; + } + + if (lua_istable(L, idx)) { + char *name = NULL; + int i, n = lua_rawlen(L, idx); + + for (i = 1; i <= n; i++) { + lua_rawgeti(L, idx, i); + name = (char *)luaL_checkstring(L, -1); + + /* A "none" means start over */ + if (strcmp("none", name) == 0) { + newVar = var; + continue; + } + + actions::transformations::Transformation *tfn = actions::transformations::Transformation::instantiate("t:" + std::string(name)); + // FIXME: transformation is not yet returning null. + if (tfn) { + newVar = tfn->evaluate(newVar, t); + } else { + t->debug(1, "SecRuleScript: Invalid transformation function: " + std::string(name)); + } + } + + return newVar; + } + + if (lua_isstring(L, idx)) { + char *name = (char *)luaL_checkstring(L, idx); + + actions::transformations::Transformation *tfn = actions::transformations::Transformation::instantiate("t:" + std::string(name)); + // FIXME: transformation is not yet returning null. + if (tfn) { + newVar = tfn->evaluate(newVar, t); + } else { + t->debug(1, "SecRuleScript: Invalid transformation function: " + std::string(name)); + } + return newVar; + } + + t->debug(8, "SecRuleScript: Transformation parameter must be a " \ + "transformation name or array of transformation names, but found " \ + "" + std::string(lua_typename(L, idx)) + " (type " \ + + std::to_string(lua_type(L, idx)) + ")"); + + return newVar; +} + + +} // namespace engines +} // namespace modsecurity diff --git a/src/engine/lua.h b/src/engine/lua.h new file mode 100644 index 00000000..1191c70c --- /dev/null +++ b/src/engine/lua.h @@ -0,0 +1,101 @@ +/* + * ModSecurity, http://www.modsecurity.org/ + * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/) + * + * You may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Trustwave Holdings, Inc. + * directly using the email address security@modsecurity.org. + * + */ + +#ifdef WITH_LUA +#endif +#include + +#include +#include +#include + + +#ifndef SRC_ENGINES_LUA_H_ +#define SRC_ENGINES_LUA_H_ + +namespace modsecurity { +class Transaction; +namespace engine { + +class LuaScriptBlob { + public: + LuaScriptBlob() : + m_data(NULL), + m_len(0) { }; + + ~LuaScriptBlob() { + if (m_data) { + free(m_data); + m_data = NULL; + } + } + + + void write(const void *data, size_t len) { + unsigned char *d = NULL; + d = (unsigned char *)realloc((unsigned char *)m_data, len + m_len); + std::memcpy(d + m_len, data, len); + m_len = m_len + len; + m_data = d; + } + + + const char *read(size_t *len) { + *len = m_len; + return (const char *)m_data; + } + + + unsigned char *m_data; + size_t m_len; +}; + + +class Lua { + public: + Lua() { }; + + bool load(std::string script, std::string *err); + int run(Transaction *t); + static bool isCompatible(std::string script, Lua *l, std::string *error); + + static int blob_keeper(lua_State *L, const void *p, size_t sz, void *ud); + static const char *blob_reader(lua_State *L, void *us, size_t *size); + + static int log(lua_State *L); + static int getvar(lua_State *L); + static int getvars(lua_State *L); + static int setvar(lua_State *L); + static std::string applyTransformations(lua_State *L, Transaction *t, int idx, + std::string var); + + LuaScriptBlob m_blob; + std::string m_scriptName; +}; + + +static const struct luaL_Reg mscLuaLib[] = { + { "log", Lua::log }, + { "getvar", Lua::getvar }, + { "getvars", Lua::getvars }, + { "setvar", Lua::setvar }, + { NULL, NULL } +}; + + +} // namespace engines +} // namespace modsecurity + +#endif // SRC_ENGINES_LUA_H_ diff --git a/src/engines/lua.cc b/src/engines/lua.cc deleted file mode 100644 index 9df08c07..00000000 --- a/src/engines/lua.cc +++ /dev/null @@ -1,32 +0,0 @@ -/* - * ModSecurity, http://www.modsecurity.org/ - * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/) - * - * You may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * If any of the files related to licensing are missing or if you have any - * other questions related to licensing please contact Trustwave Holdings, Inc. - * directly using the email address security@modsecurity.org. - * - */ - - -#include "modsecurity/modsecurity.h" -#include "src/engines/lua.h" - - -#ifdef WITH_LUA -#include - - -#endif - -#ifndef SRC_ENGINES_LUA_H_ -#define SRC_ENGINES_LUA_H_ - - - -#endif // SRC_ENGINES_LUA_H_ diff --git a/src/operators/inspect_file.cc b/src/operators/inspect_file.cc index c3f16af0..7ec8be74 100644 --- a/src/operators/inspect_file.cc +++ b/src/operators/inspect_file.cc @@ -28,6 +28,7 @@ namespace operators { bool InspectFile::init(const std::string ¶m2, std::string *error) { std::istream *iss; std::string err; + std::string err_lua; m_file = utils::find_resource(m_param, param2, &err); iss = new std::ifstream(m_file, std::ios::in); @@ -38,37 +39,45 @@ bool InspectFile::init(const std::string ¶m2, std::string *error) { return false; } + if (engine::Lua::isCompatible(m_file, &m_lua, &err_lua) == true) { + m_isScript = true; + } + delete iss; return true; } + bool InspectFile::evaluate(Transaction *transaction, const std::string &str) { - FILE *in; - char buff[512]; - std::stringstream s; - std::string res; - std::string openstr; + if (m_isScript) { + return m_lua.run(transaction); + } else { + FILE *in; + char buff[512]; + std::stringstream s; + std::string res; + std::string openstr; - openstr.append(m_param); - openstr.append(" "); - openstr.append(str); + openstr.append(m_param); + openstr.append(" "); + openstr.append(str); + if (!(in = popen(openstr.c_str(), "r"))){ + return false; + } + + while (fgets(buff, sizeof(buff), in) != NULL) { + s << buff; + } + + pclose(in); + + res.append(s.str()); + if (res.size() > 1 && res.at(0) == '1') { + return true; + } - if (!(in = popen(openstr.c_str(), "r"))){ return false; } - - while (fgets(buff, sizeof(buff), in) != NULL) { - s << buff; - } - - pclose(in); - - res.append(s.str()); - if (res.size() > 1 && res.at(0) == '1') { - return true; - } - - return false; } diff --git a/src/operators/inspect_file.h b/src/operators/inspect_file.h index f9ca2e6a..28476bf2 100644 --- a/src/operators/inspect_file.h +++ b/src/operators/inspect_file.h @@ -19,6 +19,7 @@ #include #include "src/operators/operator.h" +#include "src/engine/lua.h" namespace modsecurity { @@ -29,15 +30,19 @@ class InspectFile : public Operator { /** @ingroup ModSecurity_Operator */ InspectFile(std::string o, std::string p, bool n) : Operator(o, p, n), - m_file("") { } + m_file(""), + m_isScript(false) { } explicit InspectFile(std::string param) : Operator("InspectFile", param), - m_file("") { } + m_file(""), + m_isScript(false) { } bool init(const std::string ¶m, std::string *error) override; bool evaluate(Transaction *transaction, const std::string &str) override; private: std::string m_file; + bool m_isScript; + engine::Lua m_lua; }; } // namespace operators diff --git a/src/parser/seclang-parser.cc b/src/parser/seclang-parser.cc index fb141188..88e1822c 100644 --- a/src/parser/seclang-parser.cc +++ b/src/parser/seclang-parser.cc @@ -3937,7 +3937,7 @@ namespace yy { case 310: #line 2298 "seclang-parser.yy" // lalr1.cc:859 { - ACTION_NOT_SUPPORTED("Exec", yystack_[1].location); + //ACTION_CONTAINER($$, new actions::Exec($1)); } #line 3943 "seclang-parser.cc" // lalr1.cc:859 break; diff --git a/src/parser/seclang-parser.yy b/src/parser/seclang-parser.yy index 7f95e09b..d1f8df0a 100644 --- a/src/parser/seclang-parser.yy +++ b/src/parser/seclang-parser.yy @@ -2296,7 +2296,7 @@ act: } | ACTION_EXEC { - ACTION_NOT_SUPPORTED("Exec", @0); + //ACTION_CONTAINER($$, new actions::Exec($1)); } | ACTION_EXPIRE_VAR { diff --git a/src/variables/variable.h b/src/variables/variable.h index f8f1f369..bba7fe55 100644 --- a/src/variables/variable.h +++ b/src/variables/variable.h @@ -86,6 +86,487 @@ class Variable { static std::string to_s(std::vector *variables); + static inline bool compareStrNoCase(const std::string &a, const std::string &b) { + return a.size() == b.size() + && std::equal(a.begin(), a.end(), b.begin(), + [](char aa, char bb) { + return toupper(aa) == bb; + }); + }; + + static void stringMatchResolveMulti(Transaction *transaction, const std::string &variable, + std::vector *l) { + size_t collection = variable.find("."); + if (collection == std::string::npos) { + collection = variable.find(":"); + } + if (collection == std::string::npos) { + if (compareStrNoCase(variable, "RESPONSE_CONTENT_TYPE")) { + transaction->m_variableResponseContentType.evaluate(l); + } + else if (compareStrNoCase(variable, "ARGS_COMBINED_SIZE")) { + transaction->m_variableARGScombinedSize.evaluate(l); + } + else if (compareStrNoCase(variable, "AUTH_TYPE")) { + transaction->m_variableAuthType.evaluate(l); + } + else if (compareStrNoCase(variable, "FILES_COMBINED_SIZE")) { + transaction->m_variableFilesCombinedSize.evaluate(l); + } + else if (compareStrNoCase(variable, "FULL_REQUEST")) { + transaction->m_variableFullRequest.evaluate(l); + } + else if (compareStrNoCase(variable, "FULL_REQUEST_LENGTH")) { + transaction->m_variableFullRequestLength.evaluate(l); + } + else if (compareStrNoCase(variable, "INBOUND_DATA_ERROR")) { + transaction->m_variableInboundDataError.evaluate(l); + } + else if (compareStrNoCase(variable, "MATCHED_VAR")) { + transaction->m_variableMatchedVar.evaluate(l); + } + else if (compareStrNoCase(variable, "MATCHED_VAR_NAME")) { + transaction->m_variableMatchedVarName.evaluate(l); + } + else if (compareStrNoCase(variable, "MULTIPART_CRLF_LF_LINES")) { + transaction->m_variableMultipartCrlfLFLines.evaluate(l); + } + else if (compareStrNoCase(variable, "MULTIPART_DATA_AFTER")) { + transaction->m_variableMultipartDataAfter.evaluate(l); + } + else if (compareStrNoCase(variable, "MULTIPART_FILE_LIMIT_EXCEEDED")) { + transaction->m_variableMultipartFileLimitExceeded.evaluate(l); + } + else if (compareStrNoCase(variable, "MULTIPART_STRICT_ERROR")) { + transaction->m_variableMultipartStrictError.evaluate(l); + } + else if (compareStrNoCase(variable, "MULTIPART_HEADER_FOLDING")) { + transaction->m_variableMultipartHeaderFolding.evaluate(l); + } + else if (compareStrNoCase(variable, "MULTIPART_INVALID_QUOTING")) { + transaction->m_variableMultipartInvalidQuoting.evaluate(l); + } + else if (compareStrNoCase(variable, "MULTIPART_INVALID_HEADER_FOLDING")) { + transaction->m_variableMultipartInvalidHeaderFolding.evaluate(l); + } + else if (compareStrNoCase(variable, "MULTIPART_UNMATCHED_BOUNDARY")) { + transaction->m_variableMultipartUnmatchedBoundary.evaluate(l); + } + else if (compareStrNoCase(variable, "OUTBOUND_DATA_ERROR")) { + transaction->m_variableOutboundDataError.evaluate(l); + } + else if (compareStrNoCase(variable, "PATH_INFO")) { + transaction->m_variablePathInfo.evaluate(l); + } + else if (compareStrNoCase(variable, "QUERY_STRING")) { + transaction->m_variableQueryString.evaluate(l); + } + else if (compareStrNoCase(variable, "REMOTE_ADDR")) { + transaction->m_variableRemoteAddr.evaluate(l); + } + else if (compareStrNoCase(variable, "REMOTE_HOST")) { + transaction->m_variableRemoteHost.evaluate(l); + } + else if (compareStrNoCase(variable, "REMOTE_PORT")) { + transaction->m_variableRemotePort.evaluate(l); + } + else if (compareStrNoCase(variable, "REQBODY_ERROR")) { + transaction->m_variableReqbodyError.evaluate(l); + } + else if (compareStrNoCase(variable, "REQBODY_ERROR_MSG")) { + transaction->m_variableReqbodyErrorMsg.evaluate(l); + } + else if (compareStrNoCase(variable, "REQBODY_PROCESSOR_ERROR_MSG")) { + transaction->m_variableReqbodyProcessorErrorMsg.evaluate(l); + } + else if (compareStrNoCase(variable, "REQBODY_PROCESSOR_ERROR")) { + transaction->m_variableReqbodyProcessorError.evaluate(l); + } + else if (compareStrNoCase(variable, "REQBODY_PROCESSOR")) { + transaction->m_variableReqbodyProcessor.evaluate(l); + } + else if (compareStrNoCase(variable, "REQUEST_BASENAME")) { + transaction->m_variableRequestBasename.evaluate(l); + } + else if (compareStrNoCase(variable, "REQUEST_BODY")) { + transaction->m_variableRequestBody.evaluate(l); + } + else if (compareStrNoCase(variable, "REQUEST_BODY_LENGTH")) { + transaction->m_variableRequestBodyLength.evaluate(l); + } + else if (compareStrNoCase(variable, "REQUEST_FILENAME")) { + transaction->m_variableRequestFilename.evaluate(l); + } + else if (compareStrNoCase(variable, "REQUEST_LINE")) { + transaction->m_variableRequestLine.evaluate(l); + } + else if (compareStrNoCase(variable, "REQUEST_METHOD")) { + transaction->m_variableRequestMethod.evaluate(l); + } + else if (compareStrNoCase(variable, "REQUEST_PROTOCOL")) { + transaction->m_variableRequestProtocol.evaluate(l); + } + else if (compareStrNoCase(variable, "REQUEST_URI")) { + transaction->m_variableRequestURI.evaluate(l); + } + else if (compareStrNoCase(variable, "REQUEST_URI_RAW")) { + transaction->m_variableRequestURIRaw.evaluate(l); + } + else if (compareStrNoCase(variable, "RESOURCE")) { + transaction->m_variableResource.evaluate(l); + } + else if (compareStrNoCase(variable, "RESPONSE_BODY")) { + transaction->m_variableResponseBody.evaluate(l); + } + else if (compareStrNoCase(variable, "RESPONSE_CONTENT_LENGTH")) { + transaction->m_variableResponseContentLength.evaluate(l); + } + else if (compareStrNoCase(variable, "RESPONSE_PROTOCOL")) { + transaction->m_variableResponseProtocol.evaluate(l); + } + else if (compareStrNoCase(variable, "RESPONSE_STATUS")) { + transaction->m_variableResponseStatus.evaluate(l); + } + else if (compareStrNoCase(variable, "SERVER_ADDR")) { + transaction->m_variableServerAddr.evaluate(l); + } + else if (compareStrNoCase(variable, "SERVER_NAME")) { + transaction->m_variableServerName.evaluate(l); + } + else if (compareStrNoCase(variable, "SERVER_PORT")) { + transaction->m_variableServerPort.evaluate(l); + } + else if (compareStrNoCase(variable, "SESSIONID")) { + transaction->m_variableSessionID.evaluate(l); + } + else if (compareStrNoCase(variable, "UNIQUE_ID")) { + transaction->m_variableUniqueID.evaluate(l); + } + else if (compareStrNoCase(variable, "URLENCODED_ERROR")) { + transaction->m_variableUrlEncodedError.evaluate(l); + } + else if (compareStrNoCase(variable, "USERID")) { + transaction->m_variableUserID.evaluate(l); + } else { + transaction->m_collections.resolveMultiMatches( + variable, l); + } + } else { + std::string col = std::string(variable, 0, collection); + std::string var = std::string(variable, collection + 1, + variable.length() - (collection + 1)); + if (compareStrNoCase(col, "ARGS")) { + transaction->m_variableArgs.resolve(var, l); + } + else if (compareStrNoCase(variable, "ARGS_NAMES")) { + transaction->m_variableArgsNames.resolve(var, l); + } + else if (compareStrNoCase(variable, "ARGS_GET_NAMES")) { + transaction->m_variableArgsGetNames.resolve(var, l); + } + else if (compareStrNoCase(variable, "ARGS_POST_NAMES")) { + transaction->m_variableArgsPostNames.resolve(var, l); + } + else if (compareStrNoCase(col, "RULE")) { + transaction->m_variableRule.resolve(var, l); + } + else if (compareStrNoCase(col, "ARGS_GET")) { + transaction->m_variableArgsGet.resolve(var, l); + } + else if (compareStrNoCase(col, "ARGS_POST")) { + transaction->m_variableArgsPost.resolve(var, l); + } + else if (compareStrNoCase(col, "FILES_SIZES")) { + transaction->m_variableFilesSizes.resolve(var, l); + } + else if (compareStrNoCase(col, "FILES_NAMES")) { + transaction->m_variableFilesNames.resolve(var, l); + } + else if (compareStrNoCase(col, "FILES_TMP_CONTENT")) { + transaction->m_variableFilesTmpContent.resolve(var, l); + } + else if (compareStrNoCase(col, "MULTIPART_FILENAME")) { + transaction->m_variableMultiPartFileName.resolve(var, l); + } + else if (compareStrNoCase(col, "MULTIPART_NAME")) { + transaction->m_variableMultiPartName.resolve(var, l); + } + else if (compareStrNoCase(col, "MATCHED_VARS_NAMES")) { + transaction->m_variableMatchedVarsNames.resolve(var, l); + } + else if (compareStrNoCase(col, "MATCHED_VARS")) { + transaction->m_variableMatchedVars.resolve(var, l); + } + else if (compareStrNoCase(col, "FILES")) { + transaction->m_variableFiles.resolve(var, l); + } + else if (compareStrNoCase(col, "REQUEST_COOKIES")) { + transaction->m_variableRequestCookies.resolve(var, l); + } + else if (compareStrNoCase(col, "REQUEST_HEADERS")) { + transaction->m_variableRequestHeaders.resolve(var, l); + } + else if (compareStrNoCase(variable, "REQUEST_HEADERS_NAMES")) { + transaction->m_variableRequestHeadersNames.resolve(var, l); + } + else if (compareStrNoCase(col, "RESPONSE_HEADERS")) { + transaction->m_variableResponseHeaders.resolve(var, l); + } + else if (compareStrNoCase(variable, "RESPONSE_HEADERS_NAMES")) { + transaction->m_variableResponseHeadersNames.resolve(var, l); + } + else if (compareStrNoCase(col, "GEO")) { + transaction->m_variableGeo.resolve(var, l); + } + else if (compareStrNoCase(col, "REQUEST_COOKIES_NAMES")) { + transaction->m_variableRequestCookiesNames.resolve(var, l); + } + else if (compareStrNoCase(col, "FILES_TMPNAMES")) { + transaction->m_variableFilesTmpNames.resolve(var, l); + } else { + transaction->m_collections.resolveMultiMatches(col, + var, l); + } + } + } + + static std::string stringMatchResolve(Transaction *transaction, const std::string &variable) { + std::unique_ptr variableValue = nullptr; + size_t collection = variable.find("."); + if (collection == std::string::npos) { + collection = variable.find(":"); + } + if (collection == std::string::npos) { + if (compareStrNoCase(variable, "RESPONSE_CONTENT_TYPE")) { + variableValue = transaction->m_variableResponseContentType.resolveFirst(); + } + else if (compareStrNoCase(variable, "ARGS_COMBINED_SIZE")) { + variableValue = transaction->m_variableARGScombinedSize.resolveFirst(); + } + else if (compareStrNoCase(variable, "AUTH_TYPE")) { + variableValue = transaction->m_variableAuthType.resolveFirst(); + } + else if (compareStrNoCase(variable, "FILES_COMBINED_SIZE")) { + variableValue = transaction->m_variableFilesCombinedSize.resolveFirst(); + } + else if (compareStrNoCase(variable, "FULL_REQUEST")) { + variableValue = transaction->m_variableFullRequest.resolveFirst(); + } + else if (compareStrNoCase(variable, "FULL_REQUEST_LENGTH")) { + variableValue = transaction->m_variableFullRequestLength.resolveFirst(); + } + else if (compareStrNoCase(variable, "INBOUND_DATA_ERROR")) { + variableValue = transaction->m_variableInboundDataError.resolveFirst(); + } + else if (compareStrNoCase(variable, "MATCHED_VAR")) { + variableValue = transaction->m_variableMatchedVar.resolveFirst(); + } + else if (compareStrNoCase(variable, "MATCHED_VAR_NAME")) { + variableValue = transaction->m_variableMatchedVarName.resolveFirst(); + } + else if (compareStrNoCase(variable, "MULTIPART_CRLF_LF_LINES")) { + variableValue = transaction->m_variableMultipartCrlfLFLines.resolveFirst(); + } + else if (compareStrNoCase(variable, "MULTIPART_DATA_AFTER")) { + variableValue = transaction->m_variableMultipartDataAfter.resolveFirst(); + } + else if (compareStrNoCase(variable, "MULTIPART_FILE_LIMIT_EXCEEDED")) { + variableValue = transaction->m_variableMultipartFileLimitExceeded.resolveFirst(); + } + else if (compareStrNoCase(variable, "MULTIPART_STRICT_ERROR")) { + variableValue = transaction->m_variableMultipartStrictError.resolveFirst(); + } + else if (compareStrNoCase(variable, "MULTIPART_HEADER_FOLDING")) { + variableValue = transaction->m_variableMultipartHeaderFolding.resolveFirst(); + } + else if (compareStrNoCase(variable, "MULTIPART_INVALID_QUOTING")) { + variableValue = transaction->m_variableMultipartInvalidQuoting.resolveFirst(); + } + else if (compareStrNoCase(variable, "MULTIPART_INVALID_HEADER_FOLDING")) { + variableValue = transaction->m_variableMultipartInvalidHeaderFolding.resolveFirst(); + } + else if (compareStrNoCase(variable, "MULTIPART_UNMATCHED_BOUNDARY")) { + variableValue = transaction->m_variableMultipartUnmatchedBoundary.resolveFirst(); + } + else if (compareStrNoCase(variable, "OUTBOUND_DATA_ERROR")) { + variableValue = transaction->m_variableOutboundDataError.resolveFirst(); + } + else if (compareStrNoCase(variable, "PATH_INFO")) { + variableValue = transaction->m_variablePathInfo.resolveFirst(); + } + else if (compareStrNoCase(variable, "QUERY_STRING")) { + variableValue = transaction->m_variableQueryString.resolveFirst(); + } + else if (compareStrNoCase(variable, "REMOTE_ADDR")) { + variableValue = transaction->m_variableRemoteAddr.resolveFirst(); + } + else if (compareStrNoCase(variable, "REMOTE_HOST")) { + variableValue = transaction->m_variableRemoteHost.resolveFirst(); + } + else if (compareStrNoCase(variable, "REMOTE_PORT")) { + variableValue = transaction->m_variableRemotePort.resolveFirst(); + } + else if (compareStrNoCase(variable, "REQBODY_ERROR")) { + variableValue = transaction->m_variableReqbodyError.resolveFirst(); + } + else if (compareStrNoCase(variable, "REQBODY_ERROR_MSG")) { + variableValue = transaction->m_variableReqbodyErrorMsg.resolveFirst(); + } + else if (compareStrNoCase(variable, "REQBODY_PROCESSOR_ERROR_MSG")) { + variableValue = transaction->m_variableReqbodyProcessorErrorMsg.resolveFirst(); + } + else if (compareStrNoCase(variable, "REQBODY_PROCESSOR_ERROR")) { + variableValue = transaction->m_variableReqbodyProcessorError.resolveFirst(); + } + else if (compareStrNoCase(variable, "REQBODY_PROCESSOR")) { + variableValue = transaction->m_variableReqbodyProcessor.resolveFirst(); + } + else if (compareStrNoCase(variable, "REQUEST_BASENAME")) { + variableValue = transaction->m_variableRequestBasename.resolveFirst(); + } + else if (compareStrNoCase(variable, "REQUEST_BODY")) { + variableValue = transaction->m_variableRequestBody.resolveFirst(); + } + else if (compareStrNoCase(variable, "REQUEST_BODY_LENGTH")) { + variableValue = transaction->m_variableRequestBodyLength.resolveFirst(); + } + else if (compareStrNoCase(variable, "REQUEST_FILENAME")) { + variableValue = transaction->m_variableRequestFilename.resolveFirst(); + } + else if (compareStrNoCase(variable, "REQUEST_LINE")) { + variableValue = transaction->m_variableRequestLine.resolveFirst(); + } + else if (compareStrNoCase(variable, "REQUEST_METHOD")) { + variableValue = transaction->m_variableRequestMethod.resolveFirst(); + } + else if (compareStrNoCase(variable, "REQUEST_PROTOCOL")) { + variableValue = transaction->m_variableRequestProtocol.resolveFirst(); + } + else if (compareStrNoCase(variable, "REQUEST_URI")) { + variableValue = transaction->m_variableRequestURI.resolveFirst(); + } + else if (compareStrNoCase(variable, "REQUEST_URI_RAW")) { + variableValue = transaction->m_variableRequestURIRaw.resolveFirst(); + } + else if (compareStrNoCase(variable, "RESOURCE")) { + variableValue = transaction->m_variableResource.resolveFirst(); + } + else if (compareStrNoCase(variable, "RESPONSE_BODY")) { + variableValue = transaction->m_variableResponseBody.resolveFirst(); + } + else if (compareStrNoCase(variable, "RESPONSE_CONTENT_LENGTH")) { + variableValue = transaction->m_variableResponseContentLength.resolveFirst(); + } + else if (compareStrNoCase(variable, "RESPONSE_PROTOCOL")) { + variableValue = transaction->m_variableResponseProtocol.resolveFirst(); + } + else if (compareStrNoCase(variable, "RESPONSE_STATUS")) { + variableValue = transaction->m_variableResponseStatus.resolveFirst(); + } + else if (compareStrNoCase(variable, "SERVER_ADDR")) { + variableValue = transaction->m_variableServerAddr.resolveFirst(); + } + else if (compareStrNoCase(variable, "SERVER_NAME")) { + variableValue = transaction->m_variableServerName.resolveFirst(); + } + else if (compareStrNoCase(variable, "SERVER_PORT")) { + variableValue = transaction->m_variableServerPort.resolveFirst(); + } + else if (compareStrNoCase(variable, "SESSIONID")) { + variableValue = transaction->m_variableSessionID.resolveFirst(); + } + else if (compareStrNoCase(variable, "UNIQUE_ID")) { + variableValue = transaction->m_variableUniqueID.resolveFirst(); + } + else if (compareStrNoCase(variable, "URLENCODED_ERROR")) { + variableValue = transaction->m_variableUrlEncodedError.resolveFirst(); + } + else if (compareStrNoCase(variable, "USERID")) { + variableValue = transaction->m_variableUserID.resolveFirst(); + } else { + variableValue = transaction->m_collections.resolveFirst( + variable); + } + } else { + std::string col = std::string(variable, 0, collection); + std::string var = std::string(variable, collection + 1, + variable.length() - (collection + 1)); + if (compareStrNoCase(col, "ARGS")) { + variableValue = transaction->m_variableArgs.resolveFirst(var); + } + else if (compareStrNoCase(variable, "ARGS_NAMES")) { + variableValue = transaction->m_variableArgsNames.resolveFirst(var); + } + else if (compareStrNoCase(variable, "ARGS_GET_NAMES")) { + variableValue = transaction->m_variableArgsGetNames.resolveFirst(var); + } + else if (compareStrNoCase(variable, "ARGS_POST_NAMES")) { + variableValue = transaction->m_variableArgsPostNames.resolveFirst(var); + } + else if (compareStrNoCase(col, "RULE")) { + variableValue = transaction->m_variableRule.resolveFirst(var); + } + else if (compareStrNoCase(col, "ARGS_GET")) { + variableValue = transaction->m_variableArgsGet.resolveFirst(var); + } + else if (compareStrNoCase(col, "ARGS_POST")) { + variableValue = transaction->m_variableArgsPost.resolveFirst(var); + } + else if (compareStrNoCase(col, "FILES_SIZES")) { + variableValue = transaction->m_variableFilesSizes.resolveFirst(var); + } + else if (compareStrNoCase(col, "FILES_NAMES")) { + variableValue = transaction->m_variableFilesNames.resolveFirst(var); + } + else if (compareStrNoCase(col, "FILES_TMP_CONTENT")) { + variableValue = transaction->m_variableFilesTmpContent.resolveFirst(var); + } + else if (compareStrNoCase(col, "MULTIPART_FILENAME")) { + variableValue = transaction->m_variableMultiPartFileName.resolveFirst(var); + } + else if (compareStrNoCase(col, "MULTIPART_NAME")) { + variableValue = transaction->m_variableMultiPartName.resolveFirst(var); + } + else if (compareStrNoCase(col, "MATCHED_VARS_NAMES")) { + variableValue = transaction->m_variableMatchedVarsNames.resolveFirst(var); + } + else if (compareStrNoCase(col, "MATCHED_VARS")) { + variableValue = transaction->m_variableMatchedVars.resolveFirst(var); + } + else if (compareStrNoCase(col, "FILES")) { + variableValue = transaction->m_variableFiles.resolveFirst(var); + } + else if (compareStrNoCase(col, "REQUEST_COOKIES")) { + variableValue = transaction->m_variableRequestCookies.resolveFirst(var); + } + else if (compareStrNoCase(col, "REQUEST_HEADERS")) { + variableValue = transaction->m_variableRequestHeaders.resolveFirst(var); + } + else if (compareStrNoCase(variable, "REQUEST_HEADERS_NAMES")) { + variableValue = transaction->m_variableRequestHeadersNames.resolveFirst(var); + } + else if (compareStrNoCase(col, "RESPONSE_HEADERS")) { + variableValue = transaction->m_variableResponseHeaders.resolveFirst(var); + } + else if (compareStrNoCase(variable, "RESPONSE_HEADERS_NAMES")) { + variableValue = transaction->m_variableResponseHeadersNames.resolveFirst(var); + } + else if (compareStrNoCase(col, "GEO")) { + variableValue = transaction->m_variableGeo.resolveFirst(var); + } + else if (compareStrNoCase(col, "REQUEST_COOKIES_NAMES")) { + variableValue = transaction->m_variableRequestCookiesNames.resolveFirst(var); + } + else if (compareStrNoCase(col, "FILES_TMPNAMES")) { + variableValue = transaction->m_variableFilesTmpNames.resolveFirst(var); + } else { + variableValue = transaction->m_collections.resolveFirst(col, + var); + } + } + return std::string(*variableValue.get()); + } + std::string m_name; std::string m_collectionName; diff --git a/test/Makefile.am b/test/Makefile.am index a8e3f54d..a45e4706 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -45,6 +45,7 @@ unit_tests_LDADD = \ $(YAJL_LDFLAGS) $(YAJL_LDADD) \ $(LMDB_LDFLAGS) $(LMDB_LDADD) \ $(SSDEEP_LDFLAGS) $(SSDEEP_LDADD) \ + $(LUA_LDFLAGS) $(LUA_LDADD) \ $(LIBXML2_LDADD) \ $(GLOBAL_LDADD) @@ -82,6 +83,7 @@ regression_tests_LDADD = \ $(YAJL_LDFLAGS) $(YAJL_LDADD) \ $(LMDB_LDFLAGS) $(LMDB_LDADD) \ $(SSDEEP_LDFLAGS) $(SSDEEP_LDADD) \ + $(LUA_LDFLAGS) $(LUA_LDADD) \ $(LIBXML2_LDADD) \ $(GLOBAL_LDADD) @@ -118,6 +120,7 @@ rules_optimization_LDADD = \ $(YAJL_LDFLAGS) $(YAJL_LDADD) \ $(LMDB_LDFLAGS) $(LMDB_LDADD) \ $(SSDEEP_LDFLAGS) $(SSDEEP_LDADD) \ + $(LUA_LDFLAGS) $(LUA_LDADD) \ $(LIBXML2_LDADD) \ $(GLOBAL_LDADD) diff --git a/test/test-cases/data/match-getvar-transformation.lua b/test/test-cases/data/match-getvar-transformation.lua new file mode 100644 index 00000000..202987af --- /dev/null +++ b/test/test-cases/data/match-getvar-transformation.lua @@ -0,0 +1,20 @@ +function main() + ret = nil + + var = m.getvar("tx.test"); + if var == nil then + m.log(9, "Don't know what to say..."); + return ret + end + + if var == "FELIPE" + m.log(9, "Ops."); + elseif var == "felipe" + m.log(9, "Just fine."); + ret ="ok"; + else + m.log(9, "Really?"); + end + + return ret +end diff --git a/test/test-cases/data/match-getvar.lua b/test/test-cases/data/match-getvar.lua new file mode 100644 index 00000000..b13e2aca --- /dev/null +++ b/test/test-cases/data/match-getvar.lua @@ -0,0 +1,19 @@ +function main() + ret = nil + + num = m.getvar("tx.test"); + if num == nil then + m.log(9, "Don't know what to say about this so called number."); + return ret + end + num = tonumber(num) + + if num > 1 then + m.log(9, "Number is bigger than one."); + ret = "Whee :)" + else + m.log(9, "Really?"); + end + + return ret +end diff --git a/test/test-cases/data/match-getvars.lua b/test/test-cases/data/match-getvars.lua new file mode 100644 index 00000000..ab840b67 --- /dev/null +++ b/test/test-cases/data/match-getvars.lua @@ -0,0 +1,21 @@ +function dump(o) + if type(o) == 'table' then + local s = '{ ' + for k,v in pairs(o) do + if type(k) ~= 'number' then k = '"'..k..'"' end + s = s .. '['..k..'] = ' .. dump(v) .. ',' + end + return s .. '} ' + else + return tostring(o) + end +end + +function main() + ret = nil + m.log(9, "Here I am"); + z = m.getvars("QUERY_STRING"); + m.log(9, "Z: " .. dump(z)) + + return ret +end diff --git a/test/test-cases/data/match-log.lua b/test/test-cases/data/match-log.lua new file mode 100644 index 00000000..d5fda39d --- /dev/null +++ b/test/test-cases/data/match-log.lua @@ -0,0 +1,4 @@ +function main() + m.log(9, "echo 123"); + return "Lua script matched."; +end diff --git a/test/test-cases/data/match-set.lua b/test/test-cases/data/match-set.lua new file mode 100644 index 00000000..10665608 --- /dev/null +++ b/test/test-cases/data/match-set.lua @@ -0,0 +1,5 @@ +function main() + m.log(9, "echo 123"); + m.setvar("tx.test", "whee"); + return "Lua script matched."; +end diff --git a/test/test-cases/data/match.lua b/test/test-cases/data/match.lua new file mode 100644 index 00000000..4f7bb879 --- /dev/null +++ b/test/test-cases/data/match.lua @@ -0,0 +1,3 @@ +function main() + return "Lua script matched."; +end diff --git a/src/engines/lua.h b/test/test-cases/data/test.lua similarity index 100% rename from src/engines/lua.h rename to test/test-cases/data/test.lua diff --git a/test/test-cases/regression/operator-inpectFile.json b/test/test-cases/regression/operator-inpectFile.json index bd9ea897..7e7958c1 100644 --- a/test/test-cases/regression/operator-inpectFile.json +++ b/test/test-cases/regression/operator-inpectFile.json @@ -112,5 +112,198 @@ "SecRuleEngine On", "SecRule ARGS:res \"@inspectFile /bin/echo\" \"id:1,phase:2,pass,t:trim\"" ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing Operator :: @inspectFile - lua (1/1)", + "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":"/whee?res=whee", + "method":"GET", + "body": [ ] + }, + "response":{ + "headers":{}, + "body":[ + "no need." + ] + }, + "expected":{ + "debug_log":"Rule returned 1." + }, + "rules":[ + "SecRuleEngine On", + "SecRule ARGS:res \"@inspectFile test-cases/data/match.lua\" \"id:1,phase:2,pass,t:trim\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing Operator :: @inspectFile - lua (2/2)", + "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":"/whee?res=whee", + "method":"GET", + "body": [ ] + }, + "response":{ + "headers":{}, + "body":[ + "no need." + ] + }, + "expected":{ + "debug_log":"echo 123" + }, + "rules":[ + "SecRuleEngine On", + "SecRule ARGS:res \"@inspectFile test-cases/data/match-log.lua\" \"id:1,phase:2,pass,t:trim\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing Operator :: @inspectFile - lua (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":"*/*", + "Content-Length": "27", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri":"/whee?res=whee", + "method":"GET", + "body": [ ] + }, + "response":{ + "headers":{}, + "body":[ + "no need." + ] + }, + "expected":{ + "debug_log":"Target value: \"whee\" " + }, + "rules":[ + "SecRuleEngine On", + "SecRule ARGS:res \"@inspectFile test-cases/data/match-set.lua\" \"id:1,phase:2,pass,t:trim\"", + "SecRule TX:test \"whee\" \"id:2,phase:2,pass,t:trim\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing Operator :: @inspectFile - lua (4/4)", + "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":"/whee?res=whee", + "method":"GET", + "body": [ ] + }, + "response":{ + "headers":{}, + "body":[ + "no need." + ] + }, + "expected":{ + "debug_log":"Number is bigger than one." + }, + "rules":[ + "SecRuleEngine On", + "SecRule ARGS \".\" \"id:2,phase:2,setvar:tx.test=2\"", + "SecRule ARGS:res \"@inspectFile test-cases/data/match-getvar.lua\" \"id:1,phase:2,pass,t:trim\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing Operator :: @inspectFile - lua (5/5)", + "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":"/whee?res=whee&z=z&d=e", + "method":"GET", + "body": [ ] + }, + "response":{ + "headers":{}, + "body":[ + "no need." + ] + }, + "expected":{ + "debug_log":"Z: \\{ \\[1\\] = \\{ \\[\"value\"\\] = res=whee&z=z&d=e,\\[\"name\"\\] = QUERY_STRING,\\} ,\\}" + }, + "rules":[ + "SecRuleEngine On", + "SecRule QUERY_STRING \".\" \"id:2,phase:2,setvar:tx.test=2\"", + "SecRule ARGS:res \"@inspectFile test-cases/data/match-getvars.lua\" \"id:1,phase:2,pass,t:trim\"" + ] } ]