Repository navigation
fix: handle regex errors of @validateHash like @rx - #3712
Conversation
Follow-up to owasp-modsecurity#3709, as promised in its review: - @validateHash returned 0 on a compile error of the macro-expanded pattern and on exceeded PCRE limits, which a negated rule turned into a match. A compile error was also not logged and not flagged, so a non-negated rule skipped the hash check silently. Both paths now return what re.c turns into "no match", and a compile error is logged and sets TX:MSC_PCRE_ERROR. Other execution errors set TX:MSC_PCRE_ERROR too, as in @rx. - With legacy PCRE and JIT, pcre_exec() returns PCRE_ERROR_JITSTACKLIMIT without retrying without JIT, as the PCRE2 path does. Handle it like the match limit in @rx and @validateHash. - Replace the TX flag blocks in re_operators.c with one helper that takes the flag name.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the
|
fzipi
left a comment
There was a problem hiding this comment.
Thanks @airween, this covers the follow-ups from #3709 and fixes the @validateHash part of #3710. LGTM.
I checked:
- Compile error path: it now logs at level 3, sets
TX:MSC_PCRE_ERRORand returnsrule->op_negated, the same as@rxafter #3709. - Limits path: it now returns
rule->op_negatedinstead of0. With legacy PCRE,PCRE_ERROR_JITSTACKLIMITis guarded by#ifdef, so older PCRE builds still compile. With PCRE2,msc_regexec_ex()already retries without JIT onPCRE2_ERROR_JIT_STACKLIMIT(msc_pcre.c:257), so the asymmetry is correct. - Helper:
msre_op_set_tx_flag()is a straight merge of the three copies; behaviour is unchanged. - Tests: CI ran both new cases on the PCRE2/JIT regression job (
10-pcre-negated.t, 7/7 passed). Both would fail without the fix: the compile error and the limit case would return0, the negation would turn that into a match, and the request would get a 403. Both assert the debug line, so they can't pass by skipping the path.SecHashEngine Onsets bothhash_is_enabledandhash_enforcement, so the early return doesn't hide anything.
A few non-blocking comments:
- Link #3710. Please add
Refs #3710to the description. I'd rather not auto-close it yet because of point 2. - The
SecHashEngine Offearly return is still open.msre_op_validateHash_execute()still doesreturn 0;when the hash engine is disabled, so!@validateHashmatches every request in that configuration. This isn't a regex error, so leaving it out of this PR makes sense. Either we decide on #3710 that a disabled operator should returnrule->op_negatedtoo (a no-op in both directions), or we document that!@validateHashneedsSecHashEngine On. I can take it from there once this merges. - Sonar quality gate. It fails on duplication (63.6% of new code). Most of that is the identical limits block, with its
#ifdefand error message, that now lives in both@rxand@validateHash. A small helper, e.g.static int msre_op_pcre_limits_exceeded(int rc)for the condition, would clear it. Optional; I'm fine merging as is. - Legacy PCRE + JIT path. It isn't covered by a test, as you noted. The only regression job runs PCRE2, so this path won't be exercised in CI. Not something to fix here, but worth keeping in mind if we ever add a
--with-pcreregression job.
Once this is merged, I'll open the docs PR for MSC_PCRE_ERROR on v2 (3-3-variables.md still says it's v3-only).
TX:MSC_PCRE_ERROR now means a regex failed for any reason, as the standalone variable of the same name in v3 does, and TX:MSC_PCRE_LIMITS_EXCEEDED names one of the causes. On exceeded limits, @rx and @validateHash set both flags, MSC_PCRE_LIMITS_EXCEEDED first. tx_vars is iterated in insertion order, so a TX:/^MSC_/ rule names the more specific flag. Move the limit handling, which was identical in both operators, into two helpers: msre_op_pcre_limits_exceeded() for the condition and msre_op_pcre_limits_error() for the flags and the log message.
|
|
Thanks @fzipi for the review. I pushed a second commit with the change we agreed on in #3711, plus your Sonar point:
On your points:
|




what
@validateHash: on a compile error of the macro-expanded pattern and on exceeded PCRE limits, returnrule->op_negatedinstead of0, so a negated rule doesn't match on a regex error. A compile error is now logged (level 3) and setsTX:MSC_PCRE_ERROR; other execution errors (rc < -1) setTX:MSC_PCRE_ERRORtoo@rxand@validateHashwith legacy PCRE (--with-pcre): handlePCRE_ERROR_JITSTACKLIMITlike the match limit (setsTX:MSC_PCRE_LIMITS_EXCEEDED)re_operators.cwith one helper,msre_op_set_tx_flag(), which takes the flag nametests/regression/misc/10-pcre-negated.tfor a negated@validateHashwith an invalid expanded pattern and with exceeded PCRE limitswhy
These are the follow-ups promised in the review of #3709:
@validateHashhad the same problem as@rxbefore fix: negated @rx must not match when the regex fails #3709: the error paths returned a fixed0, which the negation inre.cturned into a match. A compile error was also neither logged nor flagged, so a non-negated rule skipped the hash check without any trace, and rule 200005 (TX:/^MSC_/) of the recommended configuration couldn't catch it.msc_regexec_ex()reruns the match without JIT onPCRE2_ERROR_JIT_STACKLIMIT. With legacy PCRE,pcre_exec()returnsPCRE_ERROR_JITSTACKLIMITinstead, so the rule took therc < -1path and skipped its remaining targets. It is now treated like an exceeded match limit. This path isn't covered by a test, since it needs a legacy PCRE build with JIT.ModSecurity v3 is not affected: its
@validateHashis a stub without a regex, and the hash engine directives are rejected by the parser.references
!@rx#3123AI disclosure
Made with help of Claude Code, Opus 5.5