Skip to content

Commit 95f16ea

Browse files
committedSep 5, 2020
Add proper error handling to @rx operator
1 parent 5437b44 commit 95f16ea

File tree

7 files changed

+102
-7
lines changed

7 files changed

+102
-7
lines changed
 

‎src/operators/rx.cc

+13
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@ namespace operators {
2929

3030
bool Rx::init(const std::string &arg, std::string *error) {
3131
if (m_string->m_containsMacro == false) {
32+
std::string regex_error;
3233
m_re = new Regex(m_param);
34+
if (!m_re->ok(&regex_error)) {
35+
*error = "Failed to compile regular expression " + m_re->getPattern() + ": " + regex_error;
36+
return false;
37+
}
3338
}
3439

3540
return true;
@@ -47,6 +52,14 @@ bool Rx::evaluate(Transaction *transaction, RuleWithActions *rule,
4752
if (m_string->m_containsMacro) {
4853
std::string eparam(m_string->evaluate(transaction));
4954
re = new Regex(eparam);
55+
std::string regex_error;
56+
if (!re->ok(&regex_error)) {
57+
ms_dbg_a(transaction, 2,
58+
"Failed to compile regular expression with macro "
59+
+ re->getPattern() + ": " + regex_error);
60+
delete re;
61+
return false;
62+
}
5063
} else {
5164
re = m_re;
5265
}

‎src/regex/backend/backend.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class Backend {
2929
public:
3030
virtual ~Backend() {}
3131

32-
virtual bool ok() const = 0;
32+
virtual bool ok(std::string *error = nullptr) const = 0;
3333

3434
virtual std::list<RegexMatch> searchAll(const std::string& s) const = 0;
3535
virtual bool searchOneMatch(const std::string& s, std::vector<RegexMatchCapture>& captures) const = 0;

‎src/regex/backend/pcre.cc

+10
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,18 @@ Pcre::Pcre(const std::string& pattern_)
4444

4545
m_pc = pcre_compile(pattern.c_str(), PCRE_DOTALL|PCRE_MULTILINE,
4646
&errptr, &erroffset, NULL);
47+
if (m_pc == NULL) {
48+
m_error = "pcre_compile error at offset " + std::to_string(erroffset) + ": " + std::string(errptr);
49+
return;
50+
}
4751

4852
m_pce = pcre_study(m_pc, pcre_study_opt, &errptr);
53+
if (m_pce == NULL) {
54+
m_error = "pcre_study error: " + std::string(errptr);
55+
pcre_free(m_pc);
56+
m_pc = nullptr;
57+
return;
58+
}
4959
}
5060

5161

‎src/regex/backend/pcre.h

+11-2
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,15 @@ class Pcre : public Backend {
5252
int search(const std::string &s, RegexMatch *m) const override;
5353
int search(const std::string &s) const override;
5454

55-
virtual bool ok() const override {
56-
return m_pc != NULL;
55+
virtual bool ok(std::string *error = nullptr) const override {
56+
if (m_pc != NULL) {
57+
return true;
58+
}
59+
if (error != nullptr) {
60+
*error= m_error;
61+
}
62+
63+
return false;
5764
}
5865

5966
virtual const std::string& getPattern() const override {
@@ -64,6 +71,8 @@ class Pcre : public Backend {
6471

6572
pcre *m_pc = NULL;
6673
pcre_extra *m_pce = NULL;
74+
75+
std::string m_error;
6776
};
6877

6978
#endif

‎src/regex/backend/re2.h

+8-2
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,14 @@ class Re2 : public Backend {
4545
bool searchOneMatch(const std::string& s, std::vector<RegexMatchCapture>& captures) const override;
4646
int search(const std::string &s, RegexMatch *m) const override;
4747
int search(const std::string &s) const override;
48-
virtual bool ok() const override {
49-
return re.ok();
48+
virtual bool ok(std::string *error = nullptr) const override {
49+
if (re.ok()) {
50+
return true;
51+
}
52+
if (error != nullptr) {
53+
*error = re.error();
54+
}
55+
return false;
5056
}
5157

5258
virtual const std::string& getPattern() const override {

‎src/regex/backend_fallback.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ class BackendFallback : public backend::Backend {
4545
: backend(compile_regex_fallback<Args...>(pattern))
4646
{}
4747

48-
virtual bool ok() const override {
49-
return backend->ok();
48+
virtual bool ok(std::string *error = nullptr) const override {
49+
return backend->ok(error);
5050
}
5151

5252
std::list<RegexMatch> searchAll(const std::string& s) const override {

‎test/test-cases/regression/operator-rx.json

+57
Original file line numberDiff line numberDiff line change
@@ -85,5 +85,62 @@
8585
"SecRuleEngine On",
8686
"SecRule REQUEST_HEADERS:Content-Length \"!^0$\" \"id:1,phase:2,pass,t:trim,block\""
8787
]
88+
},
89+
{
90+
"enabled":1,
91+
"version_min":300000,
92+
"version_max":0,
93+
"title":"Testing Operator :: @rx with invalid regular expression",
94+
"expected":{
95+
"parser_error":"Rules error.*Failed to compile regular expression \\(\\(value1\\):"
96+
},
97+
"rules":[
98+
"SecRuleEngine On",
99+
"SecRule ARGS \"@rx ((value1)\" \"id:1,phase:2,pass,t:trim\""
100+
]
101+
},
102+
{
103+
"enabled":1,
104+
"version_min":300000,
105+
"title":"Testing Operator :: @rx with invalid regular expression after macro expansion",
106+
"client":{
107+
"ip":"200.249.12.31",
108+
"port":123
109+
},
110+
"server":{
111+
"ip":"200.249.12.31",
112+
"port":80
113+
},
114+
"request":{
115+
"headers":{
116+
"Host":"localhost",
117+
"User-Agent":"curl/7.38.0",
118+
"Accept":"*/*",
119+
"Content-Length": "27",
120+
"Content-Type": "application/x-www-form-urlencoded"
121+
},
122+
"uri":"/",
123+
"method":"POST",
124+
"body": [
125+
"param1=value1&param2=value2"
126+
]
127+
},
128+
"response":{
129+
"headers":{
130+
"Date":"Mon, 13 Jul 2015 20:02:41 GMT",
131+
"Last-Modified":"Sun, 26 Oct 2014 22:33:37 GMT",
132+
"Content-Type":"text/html"
133+
},
134+
"body":[
135+
"no need."
136+
]
137+
},
138+
"expected":{
139+
"debug_log":"Failed to compile regular expression with macro \\(\\(\\):"
140+
},
141+
"rules":[
142+
"SecRuleEngine On",
143+
"SecRule ARGS \"@rx ((%{TX.DOESNT_NEED_TO_EXIST_IT_WILL_BE_AN_EMPTY_STRING})\" \"id:1,phase:2,pass,t:trim\""
144+
]
88145
}
89146
]

0 commit comments

Comments
 (0)
Failed to load comments.