Repository navigation
docs: RULE can't be read from Lua in libModSecurity v3 - #6
Conversation
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
fzipi
left a comment
There was a problem hiding this comment.
I checked the note against the v3 code and #3730, and it's accurate: m.getvar()/m.getvars() resolve names through stringMatchResolve()/stringMatchResolveMulti(), which have no RULE; #3730 returns nil or an empty table and logs at level 2; and setvar runs in executeActionsIndependentOfChainedRuleResult() before exec runs in executeActionsAfterFullMatch(). Case 6 of the new regression test covers the workaround.
Approving on one condition: please merge this only after #3730 is merged, and check that the "later versions" wording matches the release #3730 lands in. Two small nits are inline.
The failing syntax-check job is lychee reporting a 404 on an existing web.archive.org link in this file, which this PR doesn't touch.
|
|
||
| {{% 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: |
There was a problem hiding this comment.
Nit: since this note sits in the SecRuleScript section, readers may try the workaround there. In a SecRuleScript rule, the script is the operator, so that rule's own setvar runs after the script. Maybe add one sentence: for SecRuleScript, set the TX variable in an earlier rule.
There was a problem hiding this comment.
Thanks @fzipi! I've applied the tostring() suggestion.
About SecRuleScript, I checked it with a regression test, and it turned out differently from both of our guesses:
- The order isn't the problem.
RuleScript::evaluate()(src/rule_script.cc) runs thesetvaractions beforem_lua.run(), and the debug log shows the same. - But
%{RULE.id}expands to an empty string there, so the script getsnil. The same happens in a SecAction. Only a SecRule works.
The cause is in RunTimeString::evaluate() (src/run_time_string.cc:62): it casts the rule to RuleWithOperator, and RuleScript and RuleUnconditional aren't one, so the RULE variable gets NULL. An earlier rule wouldn't help either, because %{RULE.id} there is the earlier rule's id. I've opened owasp-modsecurity/ModSecurity#3733 for it.
So I've added this to the note: the workaround works in a SecRule; in a SecAction or a SecRuleScript rule, set the value as a constant until #3733 is fixed, and in a SecRuleScript rule the constant can go in the rule itself, because its setvar runs before the script.
Co-authored-by: Felipe Zipitría <3012076+fzipi@users.noreply.github.com>
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
Proposed changes
Document that the RULE collection can't be read from Lua in libModSecurity v3, and how to pass a rule field to a script instead, as agreed in owasp-modsecurity/ModSecurity#2527.
m.getvar()andm.getvars()can't read RULE, because the Lua API has no access to the rule that runs the script;m.getvar()/m.getvars()with an unknown variable name aborts the process ModSecurity#3728); later versions returnnilor an empty table and write a debug log message;setvar:tx.rule_id=%{RULE.id}in the rule that runs the script withexec, andm.getvar("TX.rule_id")in the script, with an example of both.Closes owasp-modsecurity/ModSecurity#2527
Further comments
m.getvar()andm.getvars()resolve the name withVariable::stringMatchResolve()andstringMatchResolveMulti()(src/variables/variable.h), whose lists have no RULE.setvarinexecuteActionsIndependentOfChainedRuleResult(), beforeexecruns inexecuteActionsAfterFullMatch()(src/rule_with_operator.cc). The regression test added in the fix for #3728 checks this (misc-lua-getvar-unknown-variable.json, case 6).AI disclosure
Made with help of Claude Code, Opus 5.5