Skip to content

Commit 6e70c37

Browse files
committed
Update Regex util to support match limits
If the rx or rxGlobal operator encounters a regex error, the RX_ERROR and RX_ERROR_RULE_ID variables are set. RX_ERROR contains a simple error code which can be either OTHER or MATCH_LIMIT. RX_ERROR_RULE_ID unsurprisingly contains the ID of the rule associated with the error. More than one rule may encounter regex errors, but only the first error is reflected in these variables.
1 parent 398e174 commit 6e70c37

17 files changed

+7482
-7109
lines changed

headers/modsecurity/rules_set_properties.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ class RulesSetProperties {
379379
from->m_responseBodyLimitAction,
380380
PropertyNotSetBodyLimitAction);
381381

382+
to->m_pcreMatchLimit.merge(&from->m_pcreMatchLimit);
382383
to->m_uploadFileLimit.merge(&from->m_uploadFileLimit);
383384
to->m_uploadFileMode.merge(&from->m_uploadFileMode);
384385
to->m_uploadDirectory.merge(&from->m_uploadDirectory);
@@ -470,6 +471,7 @@ class RulesSetProperties {
470471
ConfigDouble m_requestBodyLimit;
471472
ConfigDouble m_requestBodyNoFilesLimit;
472473
ConfigDouble m_responseBodyLimit;
474+
ConfigInt m_pcreMatchLimit;
473475
ConfigInt m_uploadFileLimit;
474476
ConfigInt m_uploadFileMode;
475477
DebugLog *m_debugLog;

headers/modsecurity/transaction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ class TransactionAnchoredVariables {
184184
m_variableUniqueID(t, "UNIQUE_ID"),
185185
m_variableUrlEncodedError(t, "URLENCODED_ERROR"),
186186
m_variableUserID(t, "USERID"),
187+
m_variableRxError(t, "RX_ERROR"),
188+
m_variableRxErrorRuleID(t, "RX_ERROR_RULE_ID"),
187189
m_variableArgs(t, "ARGS"),
188190
m_variableArgsGet(t, "ARGS_GET"),
189191
m_variableArgsPost(t, "ARGS_POST"),
@@ -265,6 +267,8 @@ class TransactionAnchoredVariables {
265267
AnchoredVariable m_variableUniqueID;
266268
AnchoredVariable m_variableUrlEncodedError;
267269
AnchoredVariable m_variableUserID;
270+
AnchoredVariable m_variableRxError;
271+
AnchoredVariable m_variableRxErrorRuleID;
268272

269273
AnchoredSetVariable m_variableArgs;
270274
AnchoredSetVariable m_variableArgsGet;

src/operators/rx.cc

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,41 @@ bool Rx::evaluate(Transaction *transaction, RuleWithActions *rule,
5151
re = m_re;
5252
}
5353

54-
std::vector<Utils::SMatchCapture> captures;
5554
if (re->hasError()) {
5655
ms_dbg_a(transaction, 3, "Error with regular expression: \"" + re->pattern + "\"");
5756
return false;
5857
}
59-
re->searchOneMatch(input, captures);
58+
59+
Utils::RegexResult regex_result;
60+
std::vector<Utils::SMatchCapture> captures;
61+
62+
if (transaction && transaction->m_rules->m_pcreMatchLimit.m_set) {
63+
unsigned long match_limit = transaction->m_rules->m_pcreMatchLimit.m_value;
64+
regex_result = re->searchOneMatch(input, captures, match_limit);
65+
} else {
66+
regex_result = re->searchOneMatch(input, captures);
67+
}
68+
69+
// FIXME: DRY regex error reporting. This logic is currently duplicated in other operators.
70+
if (regex_result != Utils::RegexResult::Ok) {
71+
std::string regex_error_str = "OTHER";
72+
if (regex_result == Utils::RegexResult::ErrorMatchLimit) {
73+
regex_error_str = "MATCH_LIMIT";
74+
}
75+
76+
ms_dbg_a(transaction, 1, "rx: regex error '" + regex_error_str + "' for pattern '" + re->pattern + "'");
77+
78+
// Only expose the first regex error to indicate there is an issue
79+
if (rule && transaction && transaction->m_variableRxError.m_value.empty()) {
80+
transaction->m_variableRxError.set(regex_error_str, transaction->m_variableOffset);
81+
transaction->m_variableRxErrorRuleID.set(
82+
std::to_string(rule->m_ruleId),
83+
transaction->m_variableOffset
84+
);
85+
}
86+
87+
return false;
88+
}
6089

6190
if (rule && rule->hasCaptureAction() && transaction) {
6291
for (const Utils::SMatchCapture& capture : captures) {

src/operators/rx_global.cc

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,36 @@ bool RxGlobal::evaluate(Transaction *transaction, RuleWithActions *rule,
5151
re = m_re;
5252
}
5353

54+
Utils::RegexResult regex_result;
5455
std::vector<Utils::SMatchCapture> captures;
55-
re->searchGlobal(input, captures);
56+
if (transaction && transaction->m_rules->m_pcreMatchLimit.m_set) {
57+
unsigned long match_limit = transaction->m_rules->m_pcreMatchLimit.m_value;
58+
regex_result = re->searchGlobal(input, captures, match_limit);
59+
} else {
60+
regex_result = re->searchGlobal(input, captures);
61+
}
62+
63+
// FIXME: DRY regex error reporting. This logic is currently duplicated in other operators.
64+
if (regex_result != Utils::RegexResult::Ok) {
65+
std::string regex_error_str = "OTHER";
66+
if (regex_result == Utils::RegexResult::ErrorMatchLimit) {
67+
regex_error_str = "MATCH_LIMIT";
68+
}
69+
70+
ms_dbg_a(transaction, 1, "rxGlobal: regex error '" + regex_error_str + "' for pattern '" + re->pattern + "'");
71+
72+
// Only expose the first regex error to indicate there is an issue
73+
if (rule && transaction && transaction->m_variableRxError.m_value.empty()) {
74+
transaction->m_variableRxError.set(regex_error_str, transaction->m_variableOffset);
75+
transaction->m_variableRxErrorRuleID.set(
76+
std::to_string(rule->m_ruleId),
77+
transaction->m_variableOffset
78+
);
79+
}
80+
81+
return false;
82+
}
83+
5684

5785
if (rule && rule->hasCaptureAction() && transaction) {
5886
for (const Utils::SMatchCapture& capture : captures) {

src/parser/location.hh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// A Bison parser, made by GNU Bison 3.7.6.
1+
// A Bison parser, made by GNU Bison 3.7.5.
22

33
// Locations for Bison parsers in C++
44

@@ -15,7 +15,7 @@
1515
// GNU General Public License for more details.
1616

1717
// You should have received a copy of the GNU General Public License
18-
// along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1919

2020
// As a special exception, you may create a larger work that contains
2121
// part or all of the Bison parser skeleton and distribute that work

src/parser/position.hh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// A Bison parser, made by GNU Bison 3.7.6.
1+
// A Bison parser, made by GNU Bison 3.7.5.
22

33
// Starting with Bison 3.2, this file is useless: the structure it
44
// used to define is now defined in "location.hh".

0 commit comments

Comments
 (0)