Repository navigation
fix: flag regex errors of @rx/@rxGlobal in TX:MSC_PCRE_ERROR - #3711
Conversation
Since owasp-modsecurity#3700, a negated @rx/@rxglobal no longer matches on a regex error. Exceeded match limits are still caught by the TX:/^MSC_/ rule (200005) of the recommended configuration through TX:MSC_PCRE_LIMITS_EXCEEDED. Compile errors and other regex errors only set the standalone MSC_PCRE_ERROR variable, though, which TX:/^MSC_/ does not see, so such a request passed both the negated rule and rule 200005. Set TX:MSC_PCRE_ERROR as well, the same way as MSC_PCRE_LIMITS_EXCEEDED. Rule 200005 now blocks on any regex error without a configuration change, as it does in ModSecurity v2 (owasp-modsecurity#3709). The standalone variable is kept.
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Shared regex error reporting src/operators/operator.h, src/operators/operator.cc |
Operator adds helpers that report regex compile and search errors. They set MSC_PCRE_ERROR in the transaction variable and TX collection. Search errors also set MSC_PCRE_LIMITS_EXCEEDED for match-limit errors and log the error category. |
Operator integration and regression tests src/operators/rx.cc, src/operators/rx_global.cc, test/test-cases/regression/operator-rx.json, test/test-cases/regression/operator-rxGlobal.json, modsecurity.conf-recommended, test/cppcheck_suppressions.txt |
Both operators call the shared helpers and retain their existing error return behavior. Regression tests check error flags for invalid macro-expanded patterns with negated operators. The recommended configuration documents MSC_PCRE_ERROR; cppcheck suppressions for the operators are removed. |
Priority:
Estimated code review effort: 3 (Moderate) | ~20 minutes
Change: Bug fix
Possibly related PRs
- owasp-modsecurity/ModSecurity#3700: Changes error returns for negated
@rxand@rxGlobal; this PR adds shared error reporting to those paths. - owasp-modsecurity/ModSecurity#3647: Covers invalid macro-expanded patterns in the same operators; this PR extends error reporting to the TX collection.
Suggested reviewers: fzipi, martinhsv
Merge Risk: 
Minimal · up to 286f8
-
The new regex error flags appear to reach the intended blocking rule. No issue identified here prevents merging after normal checks.
Pre-merge checks: 
4 · 
1
-
Failed checks: 1 warning Passed checks: 4 passedFull details: Docstring Coverage
-
Explanation
Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 7 functions across 4 files.
Resolution
Write docstrings for the functions missing them to satisfy the coverage threshold.
-
Finishing Touches
-
🧪 Generate unit tests (beta)
-
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands.
fzipi
left a comment
There was a problem hiding this comment.
Thanks @airween, LGTM. This closes the gap left after #3700: a negated @rx/@rxGlobal with an invalid macro-expanded pattern no longer matches, but until now rule 200005 couldn't see the error either. Setting the TX flag next to the standalone variable makes the recommended config fail closed on every regex error with no configuration change, the same as v2 after #3709.
I checked:
- Both error paths in
RxandRxGlobalset the flag: the invalid pattern case (re->hasError()) and a failed search (regex_result != Ok).storeOrUpdateFirstis the same call already used forTX:MSC_PCRE_LIMITS_EXCEEDED. - The new cases test what matters: a real rule 200005 (
TX:/^MSC_/ "!@streq 0") blocks the request with a 409. Without the change,!@rxdoesn't match after #3700, TX has noMSC_entry, and the request gets a 200. So both cases fail without the fix. - CI passes on every Linux, macOS and Windows job.
Some non-blocking comments:
-
Sonar duplication (100% of new code). The same four lines now sit in four places, next to the existing
// FIXME: DRY regex error reportingcomment. A small helper shared by both operators would clear the gate and resolve the FIXME. For example, a static function inoperator.ccor aTransactionmethod likeflagRegexError(RegexResult)that sets the standalone variable and the TX flag, plusMSC_PCRE_LIMITS_EXCEEDEDon a match limit. Fine as a follow-up if you'd rather keep this PR minimal. -
Match limit differs slightly from v2. On a match-limit error v3 now sets both
TX:MSC_PCRE_ERRORandTX:MSC_PCRE_LIMITS_EXCEEDED. That mirrors the standalone variables, which v3 already sets together. v2 (#3709) sets onlyTX:MSC_PCRE_LIMITS_EXCEEDEDfor limits and keepsTX:MSC_PCRE_ERRORfor other errors. Rule 200005 blocks either way. The visible difference is that its%{MATCHED_VAR_NAME}message may nameTX:MSC_PCRE_ERRORinstead of the more usefulTX:MSC_PCRE_LIMITS_EXCEEDED, depending on TX iteration order. I'm fine either way; just mentioning it so we decide on purpose. I'll document whichever behavior we keep. -
CHANGES. Rule 200005 now blocks requests it used to let through (an invalid pattern after macro expansion). That's the point of the change, but a CHANGES line that says so would help anyone upgrading with the recommended config.
I'll update the docs once this merges. owasp-modsecurity/documentation#4 currently says that v3 doesn't set TX:MSC_PCRE_ERROR.
owasp-modsecurity/ModSecurity#3711 makes @rx and @rxglobal set TX:MSC_PCRE_ERROR next to the standalone variable, including on match limit errors, so rule 200005 sees every regex error. Replace the statement that v3 does not set the TX flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RiUJuqXNvvpytiGRiDFyTy
Move the duplicated error handling of both operators into two static helpers in Operator: reportRegexCompileError() for an invalid pattern, and reportRegexSearchError() for a failed search. They set the standalone MSC_PCRE_* variables and the TX:MSC_PCRE_* flags, and keep the debug log messages unchanged. This resolves the "DRY regex error reporting" FIXME. The helpers are kept out of Utils::Regex, which doesn't know about transactions and is also used outside of operators, and out of Transaction, which is part of the public API. No behavior change. The log message is now built without a temporary variable, so the cppcheck suppressions for rx.cc and rx_global.cc, which only covered that variable in NO_LOGS builds, are no longer needed.
|
Thanks @fzipi for the review. On your points:
On
Thanks for taking the docs, I will review that and may be will ask some change. |
|
fzipi
left a comment
There was a problem hiding this comment.
Thanks @airween, the refactor looks good. Still LGTM.
What I checked on 286f87b:
- The helpers do the same thing as the old code.
reportRegexCompileError()andreportRegexSearchError()set the same standalone variables and TX flags, in the same order and on the same paths. The debug lines are unchanged apart from the operator name, which is now passed in. Both helpers do nothing when there is no transaction, as the oldif (transaction)blocks did. The return values inrx.cc/rx_global.ccand their comments are untouched. - The cppcheck suppressions are no longer needed. With the temporary
regex_error_strgone, cppcheck 2.22 reports nothing forrx.cc,rx_global.ccoroperator.ccwithout the fourvariableScope/unreadVariableentries, and the CI cppcheck job is green. - Forward declarations.
enum class RegexResult;inoperator.hmatches the definition inregex.h, which has no explicit underlying type, so they agree. - Tests. I built this locally (macOS, PCRE2).
operator-rx.jsonandoperator-rxGlobal.jsonpass, including the rule 200005 cases. The fullmake checkgives 5137 total, 5117 pass, 20 skip, 0 fail.
On your answers:
- Match limit. Agreed:
TX:MSC_PCRE_ERRORshould mean the same as the standalone variable, i.e. any regex failure, withMSC_PCRE_LIMITS_EXCEEDEDnaming the cause. I'll re-review #3712 once you push the v2 change, and update owasp-modsecurity/documentation#4 so both engines are described that way. - CHANGES at release time, together with #3700. Fine with me.
One optional nit: Sonar still fails at 3.6% duplication. That is probably the three lines that set MSC_PCRE_ERROR, now in both helpers. Having reportRegexCompileError() call a small shared flagRegexError(transaction), also used by the search helper, would likely clear the gate. Not a blocker for me.
|
Thanks @fzipi for checking the refactor. On the Sonar duplication: it isn't the
Both sit inside a block of 26 to 27 lines ( I'm going to merge this one - thank you for the help. |


what
@rx/@rxGlobal: on a regex error (compile error or a failed search), also setTX:MSC_PCRE_ERROR=1, next to the existing standaloneMSC_PCRE_ERRORvariablemodsecurity.conf-recommended: listMSC_PCRE_ERRORamong the flags checked by rule 200005operator-rx.jsonandoperator-rxGlobal.json: a negated rule with an invalid macro-expanded pattern is blocked by aTX:/^MSC_/rulewhy
After #3700, a negated
@rx/@rxGlobalrule no longer matches on a regex error. For exceeded match limits, rule 200005 (TX:/^MSC_/) still blocks, becauseTX:MSC_PCRE_LIMITS_EXCEEDEDis set. Compile errors and other regex errors only set the standaloneMSC_PCRE_ERRORvariable, whichTX:/^MSC_/does not see, so these requests passed. Setting the flag in TX too makes rule 200005 fail closed on every regex error, without any configuration change, and matches the behavior of ModSecurity v2.references
!@rx#3123AI disclosure: made with help of Claude Code, Opus 5.5
Summary by CodeRabbit