From 2068357af80196b98e7889c90d7793f310a9d571 Mon Sep 17 00:00:00 2001 From: ivanr Date: Fri, 21 Dec 2007 12:50:03 +0000 Subject: [PATCH] Added m.getvars() and finalised Lua support. --- CHANGES | 7 +- apache2/Makefile | 2 - apache2/apache2_config.c | 4 - apache2/msc_lua.c | 227 ++++++++++++++++---------- apache2/msc_lua.h | 3 - apache2/re.c | 4 - apache2/re.h | 9 +- apache2/re_actions.c | 111 ++++++++++++- doc/modsecurity2-apache-reference.xml | 90 +++++++--- 9 files changed, 322 insertions(+), 135 deletions(-) diff --git a/CHANGES b/CHANGES index c17b81e9..4d9147a0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,9 +1,14 @@ -20 Dec 2007 - 2.5.0-rc1 +21 Dec 2007 - 2.5.0-rc1 ----------------------- Changes since 2.5.0-dev2: + * Added support for Lua scripting in the following ways: SecRuleScript + can be used to specify a script to execute as a rule, the exec + action processes Lua scripts internally, and does the @inspectFile + operator. Refer to the documentation for more details. + * Updated included Core Ruleset to version 1.5.1. * Changed how allow works. Used on its own it now allows phases 1-4. Used diff --git a/apache2/Makefile b/apache2/Makefile index 30380d8e..dd9832c2 100644 --- a/apache2/Makefile +++ b/apache2/Makefile @@ -39,8 +39,6 @@ APACHECTL = apachectl INCLUDES = -I /usr/include/libxml2 -I /usr/include/lua5.1 # INCLUDES = -I /usr/include/libxml2 -I /path/to/httpd-x.y/srclib/pcre -DEFS = -DWITH_LUA -# DEFS = -DWITH_ICONV # DEFS = -DPERFORMANCE_MEASUREMENT # DEFS = -DNO_MODSEC_API # DEFS = -DDEBUG_CONF diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index bbdeb369..1a0904fa 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -1283,14 +1283,12 @@ static const char *cmd_rule_inheritance(cmd_parms *cmd, void *_dcfg, int flag) { return NULL; } -#ifdef WITH_LUA static const char *cmd_rule_script(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2) { const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1); return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_LUA, filename, p2, NULL); } -#endif static const char *cmd_rule_remove_by_id(cmd_parms *cmd, void *_dcfg, const char *p1) { directory_config *dcfg = (directory_config *)_dcfg; @@ -1887,7 +1885,6 @@ const command_rec module_directives[] = { "On or Off" ), - #ifdef WITH_LUA AP_INIT_TAKE12 ( "SecRuleScript", cmd_rule_script, @@ -1895,7 +1892,6 @@ const command_rec module_directives[] = { CMD_SCOPE_ANY, "" // TODO ), - #endif AP_INIT_ITERATE ( "SecRuleRemoveById", diff --git a/apache2/msc_lua.c b/apache2/msc_lua.c index 67fcb992..98b5ab9b 100644 --- a/apache2/msc_lua.c +++ b/apache2/msc_lua.c @@ -1,9 +1,6 @@ #include "msc_lua.h" - -#ifdef WITH_LUA - #include "apr_strings.h" typedef struct { @@ -123,18 +120,60 @@ static int l_log(lua_State *L) { return 0; } +/** + * + */ +static apr_array_header_t *resolve_tfns(lua_State *L, int idx, modsec_rec *msr, apr_pool_t *mp) { + apr_array_header_t *tfn_arr = NULL; + msre_tfn_metadata *tfn = NULL; + char *name = NULL; + + tfn_arr = apr_array_make(mp, 25, sizeof(msre_tfn_metadata *)); + if (tfn_arr == NULL) return NULL; + + if (lua_istable(L, idx)) { /* Is the second parameter an array? */ + int i, n = lua_objlen(L, idx); + + for(i = 1; i <= n; i++) { + lua_rawgeti(L, idx, i); + name = (char *)luaL_checkstring(L, -1); + tfn = msre_engine_tfn_resolve(msr->modsecurity->msre, name); + if (tfn == NULL) { + msr_log(msr, 1, "SecRuleScript: Invalid transformation function: %s", name); + } else { + *(msre_tfn_metadata **)apr_array_push(tfn_arr) = tfn; + } + } + } else if (lua_isstring(L, idx)) { /* The second parameter may be a simple string? */ + name = (char *)luaL_checkstring(L, idx); + tfn = msre_engine_tfn_resolve(msr->modsecurity->msre, name); + if (tfn == NULL) { + msr_log(msr, 1, "SecRuleScript: Invalid transformation function: %s", name); + } else { + *(msre_tfn_metadata **)apr_array_push(tfn_arr) = tfn; + } + } else { + // TODO Error + return NULL; + } + + return tfn_arr; +} + /** * */ static int l_getvar(lua_State *L) { - char *varname = NULL; - char *param = NULL; + char *varname = NULL, *param = NULL; modsec_rec *msr = NULL; msre_rule *rule = NULL; char *my_error_msg = NULL; + char *p1 = NULL; + apr_array_header_t *tfn_arr = NULL; + msre_var *vx = NULL; /* Retrieve parameters. */ - varname = (char *)luaL_checkstring(L, 1); + p1 = (char *)luaL_checkstring(L, 1); /* Retrieve msr. */ lua_getglobal(L, "__msr"); @@ -144,100 +183,123 @@ static int l_getvar(lua_State *L) { lua_getglobal(L, "__rule"); rule = (msre_rule *)lua_topointer(L, -1); - /* Resolve variable $varname. */ + /* Extract the variable name and its parameter from the script. */ + varname = apr_pstrdup(msr->msc_rule_mptmp, p1); param = strchr(varname, '.'); if (param != NULL) { *param = '\0'; param++; } + /* Resolve variable. */ msre_var *var = msre_create_var_ex(msr->msc_rule_mptmp, msr->modsecurity->msre, varname, param, msr, &my_error_msg); if (var == NULL) { - msr_log(msr, 1, "SecRuleScript: Failed to resolve variable: %s", varname); + msr_log(msr, 1, "%s", my_error_msg); + + lua_pushnil(L); + return 0; - } else { - msre_var *vx = NULL; - - vx = generate_single_var(msr, var, rule, msr->msc_rule_mptmp); - if (vx != NULL) { - char *rval = NULL; - long int rval_len = -1; - - /* Transform the variable if a list of transformation - * functions has been supplied. - */ - if (lua_istable(L, 2)) { /* Is the second parameter an array? */ - int i, n = lua_objlen(L, 2); - - /* Make a copy so that we don't ruin the original value. */ - vx->value = apr_pstrmemdup(msr->msc_rule_mptmp, vx->value, vx->value_len); - - for(i = 1; i <= n; i++) { - msre_tfn_metadata *tfn = NULL; - char *name = NULL; - int rc = 0; - - lua_rawgeti(L, 2, i); - name = (char *)luaL_checkstring(L, -1); - tfn = msre_engine_tfn_resolve(msr->modsecurity->msre, name); - if (tfn == NULL) { - msr_log(msr, 1, "SecRuleScript: Invalid transformation function in getvar() call: %s", name); - return 0; - } - - rc = tfn->execute(msr->msc_rule_mptmp, (unsigned char*)vx->value, - vx->value_len, &rval, &rval_len); - - vx->value = rval; - vx->value_len = rval_len; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "T (%d) %s: \"%s\"", rc, tfn->name, - log_escape_nq_ex(msr->msc_rule_mptmp, vx->value, vx->value_len)); - } - } - } - else if (lua_isstring(L, 2)) { /* The second parameter may be a simple string? */ - msre_tfn_metadata *tfn = NULL; - char *name = NULL; - int rc = 0; - - /* Make a copy so that we don't ruin the original value. */ - vx->value = apr_pstrmemdup(msr->msc_rule_mptmp, vx->value, vx->value_len); - - name = (char *)luaL_checkstring(L, 2); - tfn = msre_engine_tfn_resolve(msr->modsecurity->msre, name); - if (tfn == NULL) { - msr_log(msr, 1, "SecRuleScript: Invalid transformation function in getvar() call: %s", name); - return 0; - } - - rc = tfn->execute(msr->msc_rule_mptmp, (unsigned char *)vx->value, - vx->value_len, &rval, &rval_len); - - vx->value = rval; - vx->value_len = rval_len; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "T (%d) %s: \"%s\"", rc, tfn->name, - log_escape_nq_ex(msr->msc_rule_mptmp, vx->value, vx->value_len)); - } - } - - lua_pushlstring(L, vx->value, vx->value_len); - - return 1; - } } - return 0; + /* Resolve transformation functions. */ + tfn_arr = resolve_tfns(L, 2, msr, msr->msc_rule_mptmp); + + /* Generate variable. */ + vx = generate_single_var(msr, var, tfn_arr, rule, msr->msc_rule_mptmp); + if (vx == NULL) { + lua_pushnil(L); + + return 0; + } + + /* Return variable value. */ + lua_pushlstring(L, vx->value, vx->value_len); + + return 1; +} + +/** + * + */ +static int l_getvars(lua_State *L) { + const apr_array_header_t *tarr; + const apr_table_entry_t *telts; + apr_table_t *vartable = NULL; + apr_array_header_t *tfn_arr = NULL; + char *varname = NULL, *param = NULL; + modsec_rec *msr = NULL; + msre_rule *rule = NULL; + msre_var *vartemplate = NULL; + char *my_error_msg = NULL; + char *p1 = NULL; + int i; + + /* Retrieve parameters. */ + p1 = (char *)luaL_checkstring(L, 1); + + /* Retrieve msr. */ + lua_getglobal(L, "__msr"); + msr = (modsec_rec *)lua_topointer(L, -1); + + /* Retrieve rule. */ + lua_getglobal(L, "__rule"); + rule = (msre_rule *)lua_topointer(L, -1); + + /* Extract the variable name and its parameter from the script. */ + varname = apr_pstrdup(msr->msc_rule_mptmp, p1); + param = strchr(varname, '.'); + if (param != NULL) { + *param = '\0'; + param++; + } + + /* Resolve transformation functions. */ + tfn_arr = resolve_tfns(L, 2, msr, msr->msc_rule_mptmp); + + lua_newtable(L); + + /* Resolve variable. */ + vartemplate = msre_create_var_ex(msr->msc_rule_mptmp, msr->modsecurity->msre, + varname, param, msr, &my_error_msg); + + if (vartemplate == NULL) { + msr_log(msr, 1, "%s", my_error_msg); + + /* Returning empty table. */ + return 1; + } + + vartable = generate_multi_var(msr, vartemplate, tfn_arr, rule, msr->msc_rule_mptmp); + + tarr = apr_table_elts(vartable); + telts = (const apr_table_entry_t*)tarr->elts; + for (i = 0; i < tarr->nelts; i++) { + msre_var *var = (msre_var *)telts[i].val; + + lua_pushnumber(L, i + 1); /* Index is not zero-based. */ + + lua_newtable(L); /* Per-parameter table. */ + + lua_pushstring(L, "name"); + lua_pushlstring(L, var->name, strlen(var->name)); + lua_settable(L, -3); + + lua_pushstring(L, "value"); + lua_pushlstring(L, var->value, var->value_len); + lua_settable(L, -3); + + lua_settable(L, -3); /* Push one parameter into the results table. */ + } + + return 1; } static const struct luaL_Reg mylib[] = { { "log", l_log }, { "getvar", l_getvar }, + { "getvars", l_getvars }, { NULL, NULL } }; @@ -317,4 +379,3 @@ int lua_execute(msc_script *script, char *param, modsec_rec *msr, msre_rule *rul return ((*error_msg != NULL) ? RULE_MATCH : RULE_NO_MATCH); } -#endif diff --git a/apache2/msc_lua.h b/apache2/msc_lua.h index b0d28891..a2684dda 100644 --- a/apache2/msc_lua.h +++ b/apache2/msc_lua.h @@ -11,8 +11,6 @@ #ifndef _MSC_LUA_H_ #define _MSC_LUA_H_ -#if defined(WITH_LUA) - typedef struct msc_script msc_script; typedef struct msc_script_part msc_script_part; @@ -39,4 +37,3 @@ char DSOLOCAL *lua_compile(msc_script **script, const char *filename, apr_pool_t int DSOLOCAL lua_execute(msc_script *script, char *param, modsec_rec *msr, msre_rule *rule, char **error_msg); #endif -#endif diff --git a/apache2/re.c b/apache2/re.c index 4418d6f3..586d0cfe 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1314,7 +1314,6 @@ msre_rule *msre_rule_create(msre_ruleset *ruleset, return rule; } -#ifdef WITH_LUA /** * */ @@ -1362,7 +1361,6 @@ msre_rule *msre_rule_lua_create(msre_ruleset *ruleset, return rule; } -#endif /** * Perform non-disruptive actions associated with the provided actionset. @@ -2001,11 +1999,9 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { apr_pool_clear(msr->msc_rule_mptmp); } - #if defined(WITH_LUA) if (rule->type == RULE_TYPE_LUA) { return msre_rule_process_lua(rule, msr); } - #endif return msre_rule_process_normal(rule, msr); } diff --git a/apache2/re.h b/apache2/re.h index d0c6bda0..80edc252 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -65,9 +65,11 @@ int DSOLOCAL msre_parse_generic(apr_pool_t *pool, const char *text, apr_table_t int DSOLOCAL rule_id_in_range(int ruleid, const char *range); -msre_var DSOLOCAL *generate_single_var(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_pool_t *mptmp); +msre_var DSOLOCAL *generate_single_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr, + msre_rule *rule, apr_pool_t *mptmp); +apr_table_t DSOLOCAL *generate_multi_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr, + msre_rule *rule, apr_pool_t *mptmp); /* Structures with the corresponding functions */ @@ -143,9 +145,8 @@ struct msre_rule { unsigned int execution_time; #endif - #if defined(WITH_LUA) + /* Compiled Lua script. */ msc_script *script; - #endif }; msre_rule DSOLOCAL *msre_rule_create(msre_ruleset *ruleset, diff --git a/apache2/re_actions.c b/apache2/re_actions.c index 29d08779..b826c642 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -39,21 +39,118 @@ static void msre_engine_action_register(msre_engine *engine, const char *name, u /** * Generates a single variable (from the supplied metadata). */ -msre_var *generate_single_var(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_pool_t *mptmp) +msre_var *generate_single_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr, + msre_rule *rule, apr_pool_t *mptmp) { apr_table_t *vartab = NULL; const apr_table_entry_t *te = NULL; const apr_array_header_t *arr = NULL; + msre_var *rvar = NULL; + int i; + + /* Sanity check. */ + if ((var == NULL)||(var->metadata == NULL)||(var->metadata->generate == NULL)) return NULL; - if (var->metadata->generate == NULL) return NULL; vartab = apr_table_make(mptmp, 16); var->metadata->generate(msr, var, rule, vartab, mptmp); + arr = apr_table_elts(vartab); if (arr->nelts == 0) return NULL; - te = (apr_table_entry_t *)arr->elts; + te = (apr_table_entry_t *)arr->elts; - return (msre_var *)te[0].val; + rvar = (msre_var *)te[0].val; + + /* Return straight away if there were no + * transformation functions supplied. + */ + if ((tfn_arr == NULL)||(tfn_arr->nelts == 0)) { + return rvar; + } + + /* Copy the value so that we can transform it in piece. */ + rvar->value = apr_pstrndup(mptmp, rvar->value, rvar->value_len); + + /* Transform rvar in a loop. */ + for (i = 0; i < tfn_arr->nelts; i++) { + msre_tfn_metadata *tfn = ((msre_tfn_metadata **)tfn_arr->elts)[i]; + char *rval; + int rc; + long int rval_len; + + rc = tfn->execute(mptmp, (unsigned char *)rvar->value, + rvar->value_len, &rval, &rval_len); + + rvar->value = rval; + rvar->value_len = rval_len; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "T (%d) %s: \"%s\"", rc, tfn->name, + log_escape_nq_ex(mptmp, rvar->value, rvar->value_len)); + } + } + + return rvar; +} + +/** + * + */ +apr_table_t *generate_multi_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr, + msre_rule *rule, apr_pool_t *mptmp) +{ + const apr_array_header_t *tarr; + const apr_table_entry_t *telts; + apr_table_t *vartab = NULL, *tvartab = NULL; + msre_var *rvar = NULL; + int i, j; + + /* Sanity check. */ + if ((var == NULL)||(var->metadata == NULL)||(var->metadata->generate == NULL)) return NULL; + + /* Generate variables. */ + vartab = apr_table_make(mptmp, 16); + var->metadata->generate(msr, var, rule, vartab, mptmp); + + /* Return straight away if there were no + * transformation functions supplied. + */ + if ((tfn_arr == NULL)||(tfn_arr->nelts == 0)) { + return vartab; + } + + tvartab = apr_table_make(mptmp, 16); + + tarr = apr_table_elts(vartab); + telts = (const apr_table_entry_t*)tarr->elts; + for (j = 0; j < tarr->nelts; j++) { + rvar = (msre_var *)telts[j].val; + + /* Copy the value so that we can transform it in piece. */ + rvar->value = apr_pstrndup(mptmp, rvar->value, rvar->value_len); + + /* Transform rvar in a loop. */ + for (i = 0; i < tfn_arr->nelts; i++) { + msre_tfn_metadata *tfn = ((msre_tfn_metadata **)tfn_arr->elts)[i]; + char *rval; + int rc; + long int rval_len; + + rc = tfn->execute(mptmp, (unsigned char *)rvar->value, + rvar->value_len, &rval, &rval_len); + + rvar->value = rval; + rvar->value_len = rval_len; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "T (%d) %s: \"%s\"", rc, tfn->name, + log_escape_nq_ex(mptmp, rvar->value, rvar->value_len)); + } + } + + apr_table_addn(tvartab, rvar->name, (void *)rvar); + } + + return tvartab; } /** @@ -137,7 +234,7 @@ int expand_macros(modsec_rec *msr, msc_string *var, msre_rule *rule, apr_pool_t var_resolved = msre_create_var_ex(mptmp, msr->modsecurity->msre, var_name, var_value, msr, &my_error_msg); if (var_resolved != NULL) { - var_generated = generate_single_var(msr, var_resolved, rule, mptmp); + var_generated = generate_single_var(msr, var_resolved, NULL, rule, mptmp); if (var_generated != NULL) { part = (msc_string *)apr_pcalloc(mptmp, sizeof(msc_string)); if (part == NULL) return -1; @@ -1457,7 +1554,6 @@ static char *msre_action_exec_validate(msre_engine *engine, msre_action *action) /* TODO Support relative filenames. */ - #ifdef WITH_LUA /* Process Lua scripts internally. */ if (strlen(filename) > 4) { char *p = filename + strlen(filename) - 4; @@ -1472,7 +1568,6 @@ static char *msre_action_exec_validate(msre_engine *engine, msre_action *action) action->param_data = script; } } - #endif return NULL; } diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 893b05ce..bee3a803 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -3,7 +3,7 @@ ModSecurity Reference Manual - Version 2.5.0-rc1/ (December 19, 2007) + Version 2.5.0-rc1/ (December 21, 2007) 2004-2007 @@ -300,6 +300,11 @@ installed on the server. + + Install the latest version of Lua in the 5.1.x branch, if it + isn't already installed on the server. + + Unpack the ModSecurity archive @@ -315,14 +320,13 @@ Edit Makefile to configure the correct include path for libxml - (for example: INCLUDES=-I/usr/include/libxml2) + (for example -I + /usr/include/libxml2) - (Optional) If you want to use Lua scripting add - -DWITH_LUA and configure the path to Lua include - files (for example -I /usr/include/lua5.1). + Edit Makefile to configure the correct incpude path for Lua (for + example -I /usr/include/lua5.1). @@ -339,19 +343,18 @@ - Add one line to your configuration to load libxml2: Load libxml2 before ModSecurity: LoadFile /usr/lib/libxml2.so - (Optional) Add one line to load Lua before ModSecurity: - LoadFile /usr/lib/liblua5.1.so. + Load Lua before ModSecurity: LoadFile + /usr/lib/liblua5.1.so. - Add one line to your configuration to load ModSecurity: LoadModule security2_module - modules/mod_security2.so + Load ModSecurity itself: LoadModule + security2_module modules/mod_security2.so @@ -1805,6 +1808,12 @@ ServerAlias www.app2.com SecRuleScript resides. This allows you to place your script in the same folder as the configuration files using them. + + All Lua scripts are compiled at configuration time and cached in + memory. To reload scripts you must reload the entire ModSecurity + configuration by restarting Apache. + + Example script: -- Your script must define the main entry @@ -1816,33 +1825,65 @@ function main() m.log(1, "Hello world!"); -- Retrieve one variable. - var1 = m.getvar("REMOTE_ADDR"); + local var1 = m.getvar("REMOTE_ADDR"); -- Retrieve one variable, applying one transformation function. -- The second parameter is a string. - var2 = m.getvar("REQUEST_URI", "normalisePath"); + local var2 = m.getvar("REQUEST_URI", "normalisePath"); -- Retrieve one variable, applying several transformation functions. -- The second parameter is now a list. You should note that m.getvar() -- requires the use of comma to separate collection names from -- variable names. This is because only one variable is returned. - var3 = m.getvar("ARGS.p", { "lowercase", "compressWhitespace" } ); + local var3 = m.getvar("ARGS.p", { "lowercase", "compressWhitespace" } ); -- If you want this rule to match return a string - -- containing the error message. It is a good idea to mention - -- where the problem is located. + -- containing the error message. The message must contain the name + -- of the variable where the problem is located. -- return "Variable ARGS:p looks suspicious!" -- Otherwise, simply return null. return null; end + In this first example we were only retrieving one variable at the + time. In this case the name of the variable is known to you. In many + cases, however, you will want to examine variables whose names you won't + know in advance, for example script parameters. + + Example showing use of m.getvars() to retrieve + many variables at once: + + function main() + -- Retrieve script parameters. + local d = m.getvars("ARGS", { "lowercase", "htmlEntityDecode" } ); + + -- Loop through the paramters. + for i = 1, #d do + -- Examine parameter value. + if (string.find(d[i].value, "<script")) then + -- Always specify the name of the variable where the + -- problem is located in the error message. + return ("Suspected XSS in variable " .. d[i].name .. "."); + end + end + + -- Nothing wrong found. + return null; +end + Go to http://www.lua.org to find more about the Lua programming language. The reference manual too is available online, at http://www.lua.org/manual/5.1/. + + + Lua support is marked as experimental + because the way the scripts are written and function names may still + change while we are working for the best implementation style. +
@@ -3926,9 +3967,8 @@ SecRule IP:AUTH_ATTEMPT "@gt 25" \ <literal>exec</literal> Description: Executes an external - script/binary supplied as parameter. As of v2.5, when the support for - Lua scripting is enabled, and the parameter supplied to - exec is a Lua script (detected by the + script/binary supplied as parameter. As of v2.5, if tge parameter + supplied to exec is a Lua script (detected by the .lua extension) the script will be processed internally. This means you will get direct access to the internal request context from the script. Please read the @@ -3945,8 +3985,7 @@ SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ "log,exec:/usr/local/apache/bin/test.sh" # The following is going to process /usr/local/apache/conf/exec.lua -# internally as a Lua script on rule match, provided ModSecurity was -# compiled with Lua support enabled. +# internally as a Lua script on rule match. SecRule ARGS:p attack log,exec:/usr/local/apache/conf/exec.lua @@ -4878,10 +4917,9 @@ SecRule ARGS:route "!@endsWith %{REQUEST_ADDR}" t:none,deny extracted from the request. As of v2.5, if the supplied filename is not absolute it is treated as relative to the directory in which the configuration file resides. Also as of v2.5 if the filename is - determined to be a Lua script (based on its extension) and the Lua - support is compiled in, the script will be processed by the internal - engine. As such it will have full access to the ModSecurity - context. + determined to be a Lua script (based on its extension) the script will + be processed by the internal engine. As such it will have full access to + the ModSecurity context. Example of using an external binary/script: