From be7cb0339f5e22cafc8d5bf21b283d42f5e1e547 Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Sun, 1 Nov 2026 20:20:37 +0100 Subject: [PATCH 1/4] Document that RULE can't be read from Lua in libModSecurity v3 m.getvar() and m.getvars() resolve names without access to the rule that runs the script, so the RULE collection isn't available to them. Up to 3.0.17, such a call, or one with any other unknown name, aborts the process (owasp-modsecurity/ModSecurity#3728); later versions return nil or an empty table. Add a note to the Lua section of SecRuleScript with the workaround: copy the rule field into TX with setvar in the rule that runs the script with exec, and read it from TX. libModSecurity v3 runs setvar before exec, so the value is set when the script starts. Link the RULE entry to the note. Refs owasp-modsecurity/ModSecurity#2527 --- .../3-2-configuration-directives.md | 23 +++++++++++++++++++ content/3-reference-manual/3-3-variables.md | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/content/3-reference-manual/3-2-configuration-directives.md b/content/3-reference-manual/3-2-configuration-directives.md index 9b0808a..a788e65 100644 --- a/content/3-reference-manual/3-2-configuration-directives.md +++ b/content/3-reference-manual/3-2-configuration-directives.md @@ -1172,6 +1172,29 @@ end {{% notice note %}} +In libModSecurity v3, m.getvar() and m.getvars() can't read the [RULE]({{% relref "3-reference-manual/3-3-variables#rule" %}}) collection, because the Lua API has no access to the rule that runs the script. In libModSecurity 3.0.17 and earlier, calling them with RULE, or with any other variable name the Lua API doesn't know, aborts the process ([#3728](https://github.com/owasp-modsecurity/ModSecurity/issues/3728)); later versions return nil from m.getvar() and an empty table from m.getvars(), and write a debug log message. If a script started with the exec action needs a field of its rule, copy it into a transaction variable with setvar in the same rule (libModSecurity v3 runs setvar before exec), and read that variable from the script: + +``` seclang +SecRule ARGS "@rx attack" \ + "id:100,\ + phase:2,\ + pass,\ + setvar:tx.rule_id=%{RULE.id},\ + exec:/path/to/script.lua" +``` + +``` lua +function main() + local rule_id = m.getvar("TX.rule_id"); + m.log(4, "Called from rule " .. rule_id); + return nil; +end +``` + +{{% /notice %}} + +{{% notice note %}} + Go to to find more about the Lua programming language. The reference manual too is available online, at (ModSecurity v2) and (libModSecurity v3). {{% /notice %}} diff --git a/content/3-reference-manual/3-3-variables.md b/content/3-reference-manual/3-3-variables.md index 5b32be6..3b047f3 100644 --- a/content/3-reference-manual/3-3-variables.md +++ b/content/3-reference-manual/3-3-variables.md @@ -1050,7 +1050,7 @@ This variable may not work as expected in embedded mode, as Apache sometimes han This is a special collection that provides access to the id, rev, severity, msg, logdata, ver, maturity, and accuracy fields of the rule that triggered the action. It can be used to refer to only the same rule in which it resides; a field name is mandatory (RULE:id, or %{RULE.id} in macros), and inside a chain the fields are those of the chain starter. -libModSecurity v3 exposes only id, rev, severity, msg and logdata (ver, maturity and accuracy are not available); it also accepts a bare RULE, which returns all five, and a regular-expression selector. +libModSecurity v3 exposes only id, rev, severity, msg and logdata (ver, maturity and accuracy are not available); it also accepts a bare RULE, which returns all five, and a regular-expression selector. In libModSecurity v3, RULE can't be read from Lua; see [SecRuleScript]({{% relref "3-reference-manual/3-2-configuration-directives#secrulescript" %}}) for a workaround. `SecRule &REQUEST_HEADERS:Host "@eq 0" "log,deny,id:59,setvar:tx.varname=%{RULE.id}"` From 59e887b7082c6de453144fca5e76df1c6262d270 Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Sun, 1 Nov 2026 21:33:46 +0100 Subject: [PATCH 2/4] Update content/3-reference-manual/3-2-configuration-directives.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Felipe Zipitría <3012076+fzipi@users.noreply.github.com> --- content/3-reference-manual/3-2-configuration-directives.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/3-reference-manual/3-2-configuration-directives.md b/content/3-reference-manual/3-2-configuration-directives.md index a788e65..4cf3388 100644 --- a/content/3-reference-manual/3-2-configuration-directives.md +++ b/content/3-reference-manual/3-2-configuration-directives.md @@ -1186,7 +1186,7 @@ SecRule ARGS "@rx attack" \ ``` lua function main() local rule_id = m.getvar("TX.rule_id"); - m.log(4, "Called from rule " .. rule_id); + m.log(4, "Called from rule " .. tostring(rule_id)); return nil; end ``` From ddd7cab8b516bf76ffc443feec002923413b2476 Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Sun, 1 Nov 2026 21:42:21 +0100 Subject: [PATCH 3/4] Describe the RULE workaround in SecAction and SecRuleScript rules In a SecAction or a SecRuleScript rule, libModSecurity v3 expands %{RULE.id} to an empty string, so the setvar workaround only works in a SecRule. Say so, and suggest a constant instead. A SecRuleScript rule runs its setvar actions before the script, so the constant can be set in that rule. Refs owasp-modsecurity/ModSecurity#2527 --- content/3-reference-manual/3-2-configuration-directives.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/3-reference-manual/3-2-configuration-directives.md b/content/3-reference-manual/3-2-configuration-directives.md index 4cf3388..925a222 100644 --- a/content/3-reference-manual/3-2-configuration-directives.md +++ b/content/3-reference-manual/3-2-configuration-directives.md @@ -1191,6 +1191,8 @@ function main() end ``` +This works in a SecRule. In a SecAction or a SecRuleScript rule, libModSecurity v3 currently expands %{RULE.id} to an empty string, so set the value as a constant there, for example setvar:tx.rule_id=100. libModSecurity v3 runs the setvar actions of a SecRuleScript rule before the script, so the constant can be set in the SecRuleScript rule itself. + {{% /notice %}} {{% notice note %}} From 2511690cd9ccc61c8127489cd35f5bb7826a3193 Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Sun, 1 Nov 2026 21:43:47 +0100 Subject: [PATCH 4/4] Fix created issue link. --- content/3-reference-manual/3-2-configuration-directives.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/3-reference-manual/3-2-configuration-directives.md b/content/3-reference-manual/3-2-configuration-directives.md index 925a222..437517e 100644 --- a/content/3-reference-manual/3-2-configuration-directives.md +++ b/content/3-reference-manual/3-2-configuration-directives.md @@ -1191,7 +1191,7 @@ function main() end ``` -This works in a SecRule. In a SecAction or a SecRuleScript rule, libModSecurity v3 currently expands %{RULE.id} to an empty string, so set the value as a constant there, for example setvar:tx.rule_id=100. libModSecurity v3 runs the setvar actions of a SecRuleScript rule before the script, so the constant can be set in the SecRuleScript rule itself. +This works in a SecRule. In a SecAction or a SecRuleScript rule, libModSecurity v3 currently expands %{RULE.id} to an empty string ([#3733](https://github.com/owasp-modsecurity/ModSecurity/issues/3733)), so set the value as a constant there, for example setvar:tx.rule_id=100. libModSecurity v3 runs the setvar actions of a SecRuleScript rule before the script, so the constant can be set in the SecRuleScript rule itself. {{% /notice %}}