Skip to content

Commit 370f93b

Browse files
feat: regex key support for ctl:ruleRemoveTargetById and ctl:ruleRemoveTargetByTag
Add regex pattern matching in the variable-key position of ctl:ruleRemoveTargetById and ctl:ruleRemoveTargetByTag, enabling exclusions like: ctl:ruleRemoveTargetById=932125;ARGS:/^json\.\d+\.JobDescription$/ ctl:ruleRemoveTargetByTag=XSS;ARGS:/^json\.\d+\.JobDescription$/ JSON body processing generates argument names with dynamic array indices (json.0.Field, json.1.Field, ...). Without regex keys, operators cannot scope exclusions to specific keys without listing every possible index or disabling rules entirely. Design: - Regex detected by /pattern/ delimiter in COLLECTION:/pattern/ - Compiled once at config load via Utils::Regex (PCRE2/PCRE1) - Stored as shared_ptr - zero per-request compilation - Literal targets continue to work unchanged (no breaking change) - Shared RuleRemoveTargetSpec struct used by both ById and ByTag - Lexer REMOVE_RULE_TARGET_VALUE class shared by both actions Aligns ModSecurity v3 with Coraza (corazawaf/coraza#1561). Fixes owasp-modsecurity#3505
1 parent c34ec48 commit 370f93b

File tree

11 files changed

+3996
-3735
lines changed

11 files changed

+3996
-3735
lines changed

headers/modsecurity/rule_remove_target_entry.h

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,31 +24,23 @@
2424
namespace modsecurity {
2525

2626
/**
27-
* Entry for ctl:ruleRemoveTargetById exclusion.
27+
* Shared target-matching logic for ctl:ruleRemoveTarget{ById,ByTag}.
2828
* Supports literal target (e.g. ARGS:pwd) or regex (e.g. ARGS:/^json\.\d+\.JobDescription$/).
29-
* Regex is compiled at config load (maintainer's approach).
29+
* Regex is compiled at config load time.
3030
*/
31-
struct RuleRemoveTargetByIdEntry {
32-
int id;
31+
struct RuleRemoveTargetSpec {
3332
std::string literal;
34-
std::shared_ptr<Utils::Regex> regex; // shared: same compiled regex reused per request
33+
std::shared_ptr<Utils::Regex> regex;
3534

36-
/**
37-
* Match VariableValue. For regex: match against key (dict element).
38-
* For literal: match against keyWithCollection (e.g. ARGS:mixpanel).
39-
*/
40-
bool matches(const std::string &key, const std::string &keyWithCollection) const {
35+
bool matchesKeyWithCollection(const std::string &key,
36+
const std::string &keyWithCollection) const {
4137
if (regex) {
4238
return regex->searchAll(key).size() > 0;
4339
}
4440
return literal == keyWithCollection;
4541
}
4642

47-
/**
48-
* Match Variable (for getFinalVars). Uses case-insensitive literal match.
49-
* Regex uses key from variable's fullName (extract part after colon).
50-
*/
51-
bool matchesVariable(const std::string &fullName) const {
43+
bool matchesFullName(const std::string &fullName) const {
5244
if (regex) {
5345
size_t colon = fullName.find(':');
5446
std::string keyPart = (colon != std::string::npos && colon + 1 < fullName.size())
@@ -66,6 +58,18 @@ struct RuleRemoveTargetByIdEntry {
6658
}
6759
};
6860

61+
62+
struct RuleRemoveTargetByIdEntry {
63+
int id;
64+
RuleRemoveTargetSpec target;
65+
};
66+
67+
68+
struct RuleRemoveTargetByTagEntry {
69+
std::string tag;
70+
RuleRemoveTargetSpec target;
71+
};
72+
6973
} // namespace modsecurity
7074

7175
#endif // HEADERS_MODSECURITY_RULE_REMOVE_TARGET_ENTRY_H_

headers/modsecurity/transaction.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ class Transaction : public TransactionAnchoredVariables, public TransactionSecMa
523523
/**
524524
*
525525
*/
526-
std::list< std::pair<std::string, std::string> > m_ruleRemoveTargetByTag;
526+
std::list<RuleRemoveTargetByTagEntry> m_ruleRemoveTargetByTag;
527527

528528
/**
529529
*

src/actions/ctl/rule_remove_target_by_id.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ bool RuleRemoveTargetById::init(std::string *error) {
7777
bool RuleRemoveTargetById::evaluate(RuleWithActions *rule, Transaction *transaction) {
7878
RuleRemoveTargetByIdEntry entry;
7979
entry.id = m_id;
80-
entry.literal = m_target;
81-
entry.regex = m_regex; // shared_ptr: reuse pre-compiled regex
80+
entry.target.literal = m_target;
81+
entry.target.regex = m_regex;
8282
transaction->m_ruleRemoveTargetById.push_back(std::move(entry));
8383
return true;
8484
}

src/actions/ctl/rule_remove_target_by_tag.cc

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@
1919
#include <string>
2020
#include <vector>
2121
#include <utility>
22+
#include <memory>
2223

2324
#include "modsecurity/transaction.h"
25+
#include "modsecurity/rule_remove_target_entry.h"
2426
#include "src/utils/string.h"
27+
#include "src/utils/regex.h"
2528

2629

2730
namespace modsecurity {
@@ -41,12 +44,34 @@ bool RuleRemoveTargetByTag::init(std::string *error) {
4144
m_tag = param[0];
4245
m_target = param[1];
4346

47+
if (m_target.size() >= 4) {
48+
size_t colon = m_target.find(':');
49+
if (colon != std::string::npos && colon + 2 < m_target.size() &&
50+
m_target[colon + 1] == '/' && m_target[m_target.size() - 1] == '/') {
51+
size_t pattern_start = colon + 2;
52+
size_t pattern_end = m_target.size() - 1;
53+
if (pattern_end > pattern_start) {
54+
std::string pattern = m_target.substr(pattern_start,
55+
pattern_end - pattern_start);
56+
m_regex = std::make_unique<Utils::Regex>(pattern, true);
57+
if (m_regex->hasError()) {
58+
error->assign("Invalid regex in ctl:ruleRemoveTargetByTag: " +
59+
m_target);
60+
return false;
61+
}
62+
}
63+
}
64+
}
65+
4466
return true;
4567
}
4668

4769
bool RuleRemoveTargetByTag::evaluate(RuleWithActions *rule, Transaction *transaction) {
48-
transaction->m_ruleRemoveTargetByTag.push_back(
49-
std::make_pair(m_tag, m_target));
70+
RuleRemoveTargetByTagEntry entry;
71+
entry.tag = m_tag;
72+
entry.target.literal = m_target;
73+
entry.target.regex = m_regex;
74+
transaction->m_ruleRemoveTargetByTag.push_back(std::move(entry));
5075
return true;
5176
}
5277

src/actions/ctl/rule_remove_target_by_tag.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313
*
1414
*/
1515

16+
#include <memory>
1617
#include <string>
1718

1819
#include "modsecurity/actions/action.h"
1920
#include "modsecurity/transaction.h"
21+
#include "src/utils/regex.h"
2022

2123

2224
#ifndef SRC_ACTIONS_CTL_RULE_REMOVE_TARGET_BY_TAG_H_
@@ -37,6 +39,7 @@ class RuleRemoveTargetByTag : public Action {
3739

3840
std::string m_tag;
3941
std::string m_target;
42+
std::shared_ptr<Utils::Regex> m_regex;
4043
};
4144

4245

0 commit comments

Comments
 (0)