Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions modsecurity.conf-recommended
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ SecPcreMatchLimitRecursion 1000
# All of these are prefixed with "MSC_". The following flags currently exist:
#
# MSC_PCRE_LIMITS_EXCEEDED: PCRE match limits were exceeded.
# MSC_PCRE_ERROR: a regular expression failed to compile or execute.
#
SecRule TX:/^MSC_/ "!@streq 0" \
"id:'200005',phase:2,t:none,log,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'"
Expand Down
38 changes: 38 additions & 0 deletions src/operators/operator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include "modsecurity/transaction.h"
#include "src/run_time_string.h"
#include "src/utils/regex.h"
#include "src/utils/string.h"
#include "src/operators/begins_with.h"
#include "src/operators/contains.h"
Expand Down Expand Up @@ -70,6 +71,43 @@
namespace operators {


void Operator::reportRegexCompileError(Transaction *transaction,
const Utils::Regex &re) {
if (transaction == nullptr) {
return;
}

ms_dbg_a(transaction, 1, "Error with regular expression: \"" + re.pattern + "\"");
transaction->m_variableMscPcreError.set("1", transaction->m_variableOffset);
transaction->m_collections.m_tx_collection->storeOrUpdateFirst("MSC_PCRE_ERROR", "1");
ms_dbg_a(transaction, 7, "Set TX.MSC_PCRE_ERROR to 1");
}


void Operator::reportRegexSearchError(Transaction *transaction,
const Utils::Regex &re, Utils::RegexResult result,
const std::string &opName) {
if (transaction == nullptr) {
return;
}

transaction->m_variableMscPcreError.set("1", transaction->m_variableOffset);
transaction->m_collections.m_tx_collection->storeOrUpdateFirst("MSC_PCRE_ERROR", "1");
ms_dbg_a(transaction, 7, "Set TX.MSC_PCRE_ERROR to 1");

const bool match_limit = (result == Utils::RegexResult::ErrorMatchLimit);
if (match_limit) {
transaction->m_variableMscPcreLimitsExceeded.set("1", transaction->m_variableOffset);
transaction->m_collections.m_tx_collection->storeOrUpdateFirst("MSC_PCRE_LIMITS_EXCEEDED", "1");
ms_dbg_a(transaction, 7, "Set TX.MSC_PCRE_LIMITS_EXCEEDED to 1");

Check warning on line 102 in src/operators/operator.cc

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Modify the macro definition so that it needs to be followed by a semicolon, or remove this empty statement.

See more on https://sonarcloud.io/project/issues?id=owasp-modsecurity_ModSecurity&issues=AaF9my139M1MPg9ZzTZR&open=AaF9my139M1MPg9ZzTZR&pullRequest=3711
}

ms_dbg_a(transaction, 1, opName + ": regex error '"
+ (match_limit ? "MATCH_LIMIT" : "OTHER")
+ "' for pattern '" + re.pattern + "'");
}


bool Operator::evaluateInternal(Transaction *transaction,
RuleWithActions *rule, const std::string& a, RuleMessage &ruleMessage) {
bool res = evaluate(transaction, rule, a, ruleMessage);
Expand Down
14 changes: 14 additions & 0 deletions src/operators/operator.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
#include "src/run_time_string.h"

namespace modsecurity {
namespace Utils {
class Regex;
enum class RegexResult;
} // namespace Utils

namespace operators {

class Operator {
Expand Down Expand Up @@ -140,6 +145,15 @@ class Operator {
std::string m_param;
std::unique_ptr<RunTimeString> m_string;
bool m_couldContainsMacro;

protected:
/* Regex error reporting shared by @rx and @rxGlobal: sets the
* standalone MSC_PCRE_* variables and the TX:MSC_PCRE_* flags. */
static void reportRegexCompileError(Transaction *transaction,
const Utils::Regex &re);
static void reportRegexSearchError(Transaction *transaction,
const Utils::Regex &re, Utils::RegexResult result,
const std::string &opName);
};

} // namespace operators
Expand Down
20 changes: 2 additions & 18 deletions src/operators/rx.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,7 @@ bool Rx::evaluate(Transaction *transaction, RuleWithActions *rule,
}

if (re->hasError()) {
if (transaction) {
ms_dbg_a(transaction, 1, "Error with regular expression: \"" + re->pattern + "\"");
transaction->m_variableMscPcreError.set("1", transaction->m_variableOffset);
}
reportRegexCompileError(transaction, *re);
/* A regex failure is neither a match nor a mismatch. Return the
* value that the negation in Operator::evaluateInternal turns
* into "no match", so that the negated form does not fire on
Expand All @@ -86,21 +83,8 @@ bool Rx::evaluate(Transaction *transaction, RuleWithActions *rule,
regex_result = re->searchOneMatch(input, captures);
}

// FIXME: DRY regex error reporting. This logic is currently duplicated in other operators.
if (regex_result != Utils::RegexResult::Ok) {
if (transaction) {
transaction->m_variableMscPcreError.set("1", transaction->m_variableOffset);

std::string regex_error_str = "OTHER";
if (regex_result == Utils::RegexResult::ErrorMatchLimit) {
regex_error_str = "MATCH_LIMIT";
transaction->m_variableMscPcreLimitsExceeded.set("1", transaction->m_variableOffset);
transaction->m_collections.m_tx_collection->storeOrUpdateFirst("MSC_PCRE_LIMITS_EXCEEDED", "1");
ms_dbg_a(transaction, 7, "Set TX.MSC_PCRE_LIMITS_EXCEEDED to 1");
}

ms_dbg_a(transaction, 1, "rx: regex error '" + regex_error_str + "' for pattern '" + re->pattern + "'");
}
reportRegexSearchError(transaction, *re, regex_result, "rx");

/* A regex failure is neither a match nor a mismatch. Return the
* value that the negation in Operator::evaluateInternal turns
Expand Down
20 changes: 2 additions & 18 deletions src/operators/rx_global.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,7 @@ bool RxGlobal::evaluate(Transaction *transaction, RuleWithActions *rule,
}

if (re->hasError()) {
if (transaction) {
ms_dbg_a(transaction, 1, "Error with regular expression: \"" + re->pattern + "\"");
transaction->m_variableMscPcreError.set("1", transaction->m_variableOffset);
}
reportRegexCompileError(transaction, *re);
/* A regex failure is neither a match nor a mismatch. Return the
* value that the negation in Operator::evaluateInternal turns
* into "no match", so that the negated form does not fire on
Expand All @@ -85,21 +82,8 @@ bool RxGlobal::evaluate(Transaction *transaction, RuleWithActions *rule,
regex_result = re->searchGlobal(input, captures);
}

// FIXME: DRY regex error reporting. This logic is currently duplicated in other operators.
if (regex_result != Utils::RegexResult::Ok) {
if (transaction) {
transaction->m_variableMscPcreError.set("1", transaction->m_variableOffset);

std::string regex_error_str = "OTHER";
if (regex_result == Utils::RegexResult::ErrorMatchLimit) {
regex_error_str = "MATCH_LIMIT";
transaction->m_variableMscPcreLimitsExceeded.set("1", transaction->m_variableOffset);
transaction->m_collections.m_tx_collection->storeOrUpdateFirst("MSC_PCRE_LIMITS_EXCEEDED", "1");
ms_dbg_a(transaction, 7, "Set TX.MSC_PCRE_LIMITS_EXCEEDED to 1");
}

ms_dbg_a(transaction, 1, "rxGlobal: regex error '" + regex_error_str + "' for pattern '" + re->pattern + "'");
}
reportRegexSearchError(transaction, *re, regex_result, "rxGlobal");

/* A regex failure is neither a match nor a mismatch. Return the
* value that the negation in Operator::evaluateInternal turns
Expand Down
6 changes: 0 additions & 6 deletions test/cppcheck_suppressions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ shiftNegative:src/utils/msc_tree.cc
//
// ModSecurity v3 code...
//
variableScope:src/operators/rx.cc
variableScope:src/operators/rx_global.cc

noExplicitConstructor:seclang-parser.hh
constParameter:seclang-parser.hh
accessMoved:seclang-parser.hh
Expand All @@ -34,9 +31,6 @@ constVariableReference:seclang-parser.hh
uninitMemberVar:seclang-parser.hh


unreadVariable:src/operators/rx.cc
unreadVariable:src/operators/rx_global.cc

noExplicitConstructor:src/collection/backend/collection_data.h
stlIfStrFind:src/collection/backend/collection_data.cc

Expand Down
48 changes: 48 additions & 0 deletions test/test-cases/regression/operator-rx.json
Original file line number Diff line number Diff line change
Expand Up @@ -565,5 +565,53 @@
"SecRule ARGS:subject \"!@rx %{ARGS:patt}\" \"id:4001,phase:2,deny,status:403\"",
"SecRule MSC_PCRE_ERROR \"@streq 1\" \"id:4002,phase:2,pass,log\""
]
},
{
"enabled": 1,
"version_min": 300000,
"title": "Testing Operator :: !@rx with invalid macro-expanded pattern is flagged in TX for TX:/^MSC_/",
"client": {
"ip": "200.249.12.31",
"port": 123
},
"server": {
"ip": "200.249.12.31",
"port": 80
},
"request": {
"headers": {
"Host": "localhost",
"User-Agent": "curl/7.38.0",
"Accept": "*/*",
"Content-Length": "27",
"Content-Type": "application/x-www-form-urlencoded"
},
"uri": "/?patt=%28",
"method": "POST",
"body": [
"subject=wwww"
]
},
"response": {
"headers": {
"Date": "Mon, 13 Jul 2015 20:02:41 GMT",
"Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT",
"Content-Type": "text/html",
"Content-Length": "8"
},
"body": [
"no need."
]
},
"expected": {
"debug_log": "Set TX.MSC_PCRE_ERROR to 1",
"error_log": "ModSecurity internal error flagged: TX:MSC_PCRE_ERROR",
"http_code": 409
},
"rules": [
"SecRuleEngine On",
"SecRule ARGS:subject \"!@rx %{ARGS:patt}\" \"id:4001,phase:2,deny,status:403\"",
"SecRule TX:/^MSC_/ \"!@streq 0\" \"id:4002,phase:2,t:none,log,deny,status:409,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'\""
]
}
]
48 changes: 48 additions & 0 deletions test/test-cases/regression/operator-rxGlobal.json
Original file line number Diff line number Diff line change
Expand Up @@ -371,5 +371,53 @@
"SecRule ARGS:subject \"!@rxGlobal %{ARGS:patt}\" \"id:4001,phase:2,deny,status:403\"",
"SecRule MSC_PCRE_ERROR \"@streq 1\" \"id:4002,phase:2,pass,log\""
]
},
{
"enabled": 1,
"version_min": 300000,
"title": "Testing Operator :: !@rxGlobal with invalid macro-expanded pattern is flagged in TX for TX:/^MSC_/",
"client": {
"ip": "200.249.12.31",
"port": 123
},
"server": {
"ip": "200.249.12.31",
"port": 80
},
"request": {
"headers": {
"Host": "localhost",
"User-Agent": "curl/7.38.0",
"Accept": "*/*",
"Content-Length": "27",
"Content-Type": "application/x-www-form-urlencoded"
},
"uri": "/?patt=%28",
"method": "POST",
"body": [
"subject=wwww"
]
},
"response": {
"headers": {
"Date": "Mon, 13 Jul 2015 20:02:41 GMT",
"Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT",
"Content-Type": "text/html",
"Content-Length": "8"
},
"body": [
"no need."
]
},
"expected": {
"debug_log": "Set TX.MSC_PCRE_ERROR to 1",
"error_log": "ModSecurity internal error flagged: TX:MSC_PCRE_ERROR",
"http_code": 409
},
"rules": [
"SecRuleEngine On",
"SecRule ARGS:subject \"!@rxGlobal %{ARGS:patt}\" \"id:4001,phase:2,deny,status:403\"",
"SecRule TX:/^MSC_/ \"!@streq 0\" \"id:4002,phase:2,t:none,log,deny,status:409,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'\""
]
}
]
Loading