mirror of
https://github.com/owasp-modsecurity/ModSecurity.git
synced 2025-08-13 21:36:00 +03:00
Merge pull request #2736 from brandonpayton/add-regex-match-limits-and-error-reporting
Add isolated PCRE match limits as a layer of ReDoS defense
This commit is contained in:
commit
09a135baab
@ -379,6 +379,7 @@ class RulesSetProperties {
|
||||
from->m_responseBodyLimitAction,
|
||||
PropertyNotSetBodyLimitAction);
|
||||
|
||||
to->m_pcreMatchLimit.merge(&from->m_pcreMatchLimit);
|
||||
to->m_uploadFileLimit.merge(&from->m_uploadFileLimit);
|
||||
to->m_uploadFileMode.merge(&from->m_uploadFileMode);
|
||||
to->m_uploadDirectory.merge(&from->m_uploadDirectory);
|
||||
@ -470,6 +471,7 @@ class RulesSetProperties {
|
||||
ConfigDouble m_requestBodyLimit;
|
||||
ConfigDouble m_requestBodyNoFilesLimit;
|
||||
ConfigDouble m_responseBodyLimit;
|
||||
ConfigInt m_pcreMatchLimit;
|
||||
ConfigInt m_uploadFileLimit;
|
||||
ConfigInt m_uploadFileMode;
|
||||
DebugLog *m_debugLog;
|
||||
|
@ -134,6 +134,8 @@ class TransactionAnchoredVariables {
|
||||
m_variableInboundDataError(t, "INBOUND_DATA_ERROR"),
|
||||
m_variableMatchedVar(t, "MATCHED_VAR"),
|
||||
m_variableMatchedVarName(t, "MATCHED_VAR_NAME"),
|
||||
m_variableMscPcreError(t, "MSC_PCRE_ERROR"),
|
||||
m_variableMscPcreLimitsExceeded(t, "MSC_PCRE_LIMITS_EXCEEDED"),
|
||||
m_variableMultipartBoundaryQuoted(t, "MULTIPART_BOUNDARY_QUOTED"),
|
||||
m_variableMultipartBoundaryWhiteSpace(t,
|
||||
"MULTIPART_BOUNDARY_WHITESPACE"),
|
||||
@ -219,6 +221,8 @@ class TransactionAnchoredVariables {
|
||||
AnchoredVariable m_variableInboundDataError;
|
||||
AnchoredVariable m_variableMatchedVar;
|
||||
AnchoredVariable m_variableMatchedVarName;
|
||||
AnchoredVariable m_variableMscPcreError;
|
||||
AnchoredVariable m_variableMscPcreLimitsExceeded;
|
||||
AnchoredVariable m_variableMultipartBoundaryQuoted;
|
||||
AnchoredVariable m_variableMultipartBoundaryWhiteSpace;
|
||||
AnchoredVariable m_variableMultipartCrlfLFLines;
|
||||
|
@ -51,12 +51,36 @@ bool Rx::evaluate(Transaction *transaction, RuleWithActions *rule,
|
||||
re = m_re;
|
||||
}
|
||||
|
||||
std::vector<Utils::SMatchCapture> captures;
|
||||
if (re->hasError()) {
|
||||
ms_dbg_a(transaction, 3, "Error with regular expression: \"" + re->pattern + "\"");
|
||||
return false;
|
||||
}
|
||||
re->searchOneMatch(input, captures);
|
||||
|
||||
Utils::RegexResult regex_result;
|
||||
std::vector<Utils::SMatchCapture> captures;
|
||||
|
||||
if (transaction && transaction->m_rules->m_pcreMatchLimit.m_set) {
|
||||
unsigned long match_limit = transaction->m_rules->m_pcreMatchLimit.m_value;
|
||||
regex_result = re->searchOneMatch(input, captures, match_limit);
|
||||
} else {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
ms_dbg_a(transaction, 1, "rx: regex error '" + regex_error_str + "' for pattern '" + re->pattern + "'");
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rule && rule->hasCaptureAction() && transaction) {
|
||||
for (const Utils::SMatchCapture& capture : captures) {
|
||||
|
@ -51,8 +51,30 @@ bool RxGlobal::evaluate(Transaction *transaction, RuleWithActions *rule,
|
||||
re = m_re;
|
||||
}
|
||||
|
||||
Utils::RegexResult regex_result;
|
||||
std::vector<Utils::SMatchCapture> captures;
|
||||
re->searchGlobal(input, captures);
|
||||
if (transaction && transaction->m_rules->m_pcreMatchLimit.m_set) {
|
||||
unsigned long match_limit = transaction->m_rules->m_pcreMatchLimit.m_value;
|
||||
regex_result = re->searchGlobal(input, captures, match_limit);
|
||||
} else {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
ms_dbg_a(transaction, 1, "rxGlobal: regex error '" + regex_error_str + "' for pattern '" + re->pattern + "'");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (rule && rule->hasCaptureAction() && transaction) {
|
||||
for (const Utils::SMatchCapture& capture : captures) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// A Bison parser, made by GNU Bison 3.7.6.
|
||||
// A Bison parser, made by GNU Bison 3.8.2.
|
||||
|
||||
// Locations for Bison parsers in C++
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// A Bison parser, made by GNU Bison 3.7.6.
|
||||
// A Bison parser, made by GNU Bison 3.8.2.
|
||||
|
||||
// Starting with Bison 3.2, this file is useless: the structure it
|
||||
// used to define is now defined in "location.hh".
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -184,6 +184,8 @@ class Driver;
|
||||
#include "src/variables/matched_vars.h"
|
||||
#include "src/variables/matched_vars_names.h"
|
||||
#include "src/variables/modsec_build.h"
|
||||
#include "src/variables/msc_pcre_error.h"
|
||||
#include "src/variables/msc_pcre_limits_exceeded.h"
|
||||
#include "src/variables/multipart_boundary_quoted.h"
|
||||
#include "src/variables/multipart_boundary_whitespace.h"
|
||||
#include "src/variables/multipart_crlf_lf_lines.h"
|
||||
@ -368,6 +370,8 @@ using namespace modsecurity::operators;
|
||||
VARIABLE_INBOUND_DATA_ERROR "INBOUND_DATA_ERROR"
|
||||
VARIABLE_MATCHED_VAR "MATCHED_VAR"
|
||||
VARIABLE_MATCHED_VAR_NAME "MATCHED_VAR_NAME"
|
||||
VARIABLE_MSC_PCRE_ERROR "MSC_PCRE_ERROR"
|
||||
VARIABLE_MSC_PCRE_LIMITS_EXCEEDED "MSC_PCRE_LIMITS_EXCEEDED"
|
||||
VARIABLE_MULTIPART_BOUNDARY_QUOTED
|
||||
VARIABLE_MULTIPART_BOUNDARY_WHITESPACE
|
||||
VARIABLE_MULTIPART_CRLF_LF_LINES "MULTIPART_CRLF_LF_LINES"
|
||||
@ -1648,10 +1652,10 @@ expression:
|
||||
YYERROR;
|
||||
*/
|
||||
| CONFIG_DIR_PCRE_MATCH_LIMIT
|
||||
/* Parser error disabled to avoid breaking default installations with modsecurity.conf-recommended
|
||||
driver.error(@0, "SecPcreMatchLimit is not currently supported. Default PCRE values are being used for now");
|
||||
YYERROR;
|
||||
*/
|
||||
{
|
||||
driver.m_pcreMatchLimit.m_set = true;
|
||||
driver.m_pcreMatchLimit.m_value = atoi($1.c_str());
|
||||
}
|
||||
| CONGIG_DIR_RESPONSE_BODY_MP
|
||||
{
|
||||
std::istringstream buf($1);
|
||||
@ -2321,6 +2325,14 @@ var:
|
||||
{
|
||||
VARIABLE_CONTAINER($$, new variables::MatchedVarName());
|
||||
}
|
||||
| VARIABLE_MSC_PCRE_ERROR
|
||||
{
|
||||
VARIABLE_CONTAINER($$, new variables::MscPcreError());
|
||||
}
|
||||
| VARIABLE_MSC_PCRE_LIMITS_EXCEEDED
|
||||
{
|
||||
VARIABLE_CONTAINER($$, new variables::MscPcreLimitsExceeded());
|
||||
}
|
||||
| VARIABLE_MULTIPART_BOUNDARY_QUOTED
|
||||
{
|
||||
VARIABLE_CONTAINER($$, new variables::MultipartBoundaryQuoted());
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -186,6 +186,8 @@ VARIABLE_GLOBAL (?i:GLOBAL)
|
||||
VARIABLE_INBOUND_DATA_ERROR (?i:INBOUND_DATA_ERROR)
|
||||
VARIABLE_MATCHED_VAR (?i:MATCHED_VAR)
|
||||
VARIABLE_MATCHED_VAR_NAME (?i:MATCHED_VAR_NAME)
|
||||
VARIABLE_MSC_PCRE_ERROR (?i:MSC_PCRE_ERROR)
|
||||
VARIABLE_MSC_PCRE_LIMITS_EXCEEDED (?i:MSC_PCRE_LIMITS_EXCEEDED)
|
||||
VARIABLE_MULTIPART_BOUNDARY_QUOTED (?i:MULTIPART_BOUNDARY_QUOTED)
|
||||
VARIABLE_MULTIPART_BOUNDARY_WHITESPACE (?i:MULTIPART_BOUNDARY_WHITESPACE)
|
||||
VARIABLE_MULTIPART_CRLF_LF_LINES (?i:MULTIPART_CRLF_LF_LINES)
|
||||
@ -910,6 +912,8 @@ EQUALS_MINUS (?i:=\-)
|
||||
{VARIABLE_INBOUND_DATA_ERROR} { return p::make_VARIABLE_INBOUND_DATA_ERROR(*driver.loc.back()); }
|
||||
{VARIABLE_MATCHED_VAR_NAME} { return p::make_VARIABLE_MATCHED_VAR_NAME(*driver.loc.back()); }
|
||||
{VARIABLE_MATCHED_VAR} { return p::make_VARIABLE_MATCHED_VAR(*driver.loc.back()); }
|
||||
{VARIABLE_MSC_PCRE_ERROR} { return p::make_VARIABLE_MSC_PCRE_ERROR(*driver.loc.back()); }
|
||||
{VARIABLE_MSC_PCRE_LIMITS_EXCEEDED} { return p::make_VARIABLE_MSC_PCRE_LIMITS_EXCEEDED(*driver.loc.back()); }
|
||||
{VARIABLE_MULTIPART_BOUNDARY_QUOTED} { return p::make_VARIABLE_MULTIPART_BOUNDARY_QUOTED(*driver.loc.back()); }
|
||||
{VARIABLE_MULTIPART_BOUNDARY_WHITESPACE} { return p::make_VARIABLE_MULTIPART_BOUNDARY_WHITESPACE(*driver.loc.back()); }
|
||||
{VARIABLE_MULTIPART_CRLF_LF_LINES} { return p::make_VARIABLE_MULTIPART_CRLF_LF_LINES(*driver.loc.back()); }
|
||||
|
@ -1,4 +1,4 @@
|
||||
// A Bison parser, made by GNU Bison 3.7.6.
|
||||
// A Bison parser, made by GNU Bison 3.8.2.
|
||||
|
||||
// Starting with Bison 3.2, this file is useless: the structure it
|
||||
// used to define is now defined with the parser itself.
|
||||
|
@ -167,6 +167,8 @@ Transaction::Transaction(ModSecurity *ms, RulesSet *rules, void *logCbData)
|
||||
+ std::to_string(modsecurity::utils::generate_transaction_unique_id())));
|
||||
|
||||
m_variableUrlEncodedError.set("0", 0);
|
||||
m_variableMscPcreError.set("0", 0);
|
||||
m_variableMscPcreLimitsExceeded.set("0", 0);
|
||||
|
||||
ms_dbg(4, "Initializing transaction");
|
||||
|
||||
@ -238,6 +240,8 @@ Transaction::Transaction(ModSecurity *ms, RulesSet *rules, char *id, void *logCb
|
||||
TransactionAnchoredVariables(this) {
|
||||
|
||||
m_variableUrlEncodedError.set("0", 0);
|
||||
m_variableMscPcreError.set("0", 0);
|
||||
m_variableMscPcreLimitsExceeded.set("0", 0);
|
||||
|
||||
ms_dbg(4, "Initializing transaction");
|
||||
|
||||
|
@ -25,12 +25,38 @@
|
||||
|
||||
#ifndef WITH_PCRE2
|
||||
#if PCRE_HAVE_JIT
|
||||
#define pcre_study_opt PCRE_STUDY_JIT_COMPILE
|
||||
// NOTE: Add PCRE_STUDY_EXTRA_NEEDED so studying always yields a pcre_extra strucure
|
||||
// and we can selectively override match limits using a copy of that structure at runtime.
|
||||
#define pcre_study_opt PCRE_STUDY_JIT_COMPILE | PCRE_STUDY_EXTRA_NEEDED
|
||||
#else
|
||||
#define pcre_study_opt 0
|
||||
// NOTE: Add PCRE_STUDY_EXTRA_NEEDED so studying always yields a pcre_extra strucure
|
||||
// and we can selectively override match limits using a copy of that structure at runtime.
|
||||
#define pcre_study_opt PCRE_STUDY_EXTRA_NEEDED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef WITH_PCRE2
|
||||
class Pcre2MatchContextPtr {
|
||||
public:
|
||||
Pcre2MatchContextPtr()
|
||||
: m_match_context(pcre2_match_context_create(NULL)) {}
|
||||
|
||||
Pcre2MatchContextPtr(const Pcre2MatchContextPtr&) = delete;
|
||||
Pcre2MatchContextPtr& operator=(const Pcre2MatchContextPtr&) = delete;
|
||||
|
||||
~Pcre2MatchContextPtr() {
|
||||
pcre2_match_context_free(m_match_context);
|
||||
}
|
||||
|
||||
operator pcre2_match_context*() const {
|
||||
return m_match_context;
|
||||
}
|
||||
|
||||
private:
|
||||
pcre2_match_context *m_match_context;
|
||||
};
|
||||
#endif
|
||||
|
||||
namespace modsecurity {
|
||||
namespace Utils {
|
||||
|
||||
@ -163,24 +189,39 @@ std::list<SMatch> Regex::searchAll(const std::string& s) const {
|
||||
return retList;
|
||||
}
|
||||
|
||||
bool Regex::searchOneMatch(const std::string& s, std::vector<SMatchCapture>& captures) const {
|
||||
RegexResult Regex::searchOneMatch(const std::string& s, std::vector<SMatchCapture>& captures, unsigned long match_limit) const {
|
||||
#ifdef WITH_PCRE2
|
||||
Pcre2MatchContextPtr match_context;
|
||||
if (match_limit > 0) {
|
||||
// TODO: What if setting the match limit fails?
|
||||
pcre2_set_match_limit(match_context, match_limit);
|
||||
}
|
||||
|
||||
PCRE2_SPTR pcre2_s = reinterpret_cast<PCRE2_SPTR>(s.c_str());
|
||||
pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(m_pc, NULL);
|
||||
int rc = 0;
|
||||
if (m_pcje == 0) {
|
||||
rc = pcre2_jit_match(m_pc, pcre2_s, s.length(), 0, 0, match_data, NULL);
|
||||
rc = pcre2_jit_match(m_pc, pcre2_s, s.length(), 0, 0, match_data, match_context);
|
||||
}
|
||||
|
||||
if (m_pcje != 0 || rc == PCRE2_ERROR_JIT_STACKLIMIT) {
|
||||
rc = pcre2_match(m_pc, pcre2_s, s.length(), 0, PCRE2_NO_JIT, match_data, NULL);
|
||||
rc = pcre2_match(m_pc, pcre2_s, s.length(), 0, PCRE2_NO_JIT, match_data, match_context);
|
||||
}
|
||||
PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
|
||||
#else
|
||||
const char *subject = s.c_str();
|
||||
int ovector[OVECCOUNT];
|
||||
pcre_extra local_pce;
|
||||
pcre_extra *pce = m_pce;
|
||||
|
||||
int rc = pcre_exec(m_pc, m_pce, subject, s.size(), 0, 0, ovector, OVECCOUNT);
|
||||
if (m_pce != NULL && match_limit > 0) {
|
||||
local_pce = *m_pce;
|
||||
local_pce.match_limit = match_limit;
|
||||
local_pce.flags |= PCRE_EXTRA_MATCH_LIMIT;
|
||||
pce = &local_pce;
|
||||
}
|
||||
|
||||
int rc = pcre_exec(m_pc, pce, subject, s.size(), 0, 0, ovector, OVECCOUNT);
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < rc; i++) {
|
||||
@ -197,12 +238,18 @@ bool Regex::searchOneMatch(const std::string& s, std::vector<SMatchCapture>& cap
|
||||
#ifdef WITH_PCRE2
|
||||
pcre2_match_data_free(match_data);
|
||||
#endif
|
||||
return (rc > 0);
|
||||
return to_regex_result(rc);
|
||||
}
|
||||
|
||||
bool Regex::searchGlobal(const std::string& s, std::vector<SMatchCapture>& captures) const {
|
||||
RegexResult Regex::searchGlobal(const std::string& s, std::vector<SMatchCapture>& captures, unsigned long match_limit) const {
|
||||
bool prev_match_zero_length = false;
|
||||
#ifdef WITH_PCRE2
|
||||
Pcre2MatchContextPtr match_context;
|
||||
if (match_limit > 0) {
|
||||
// TODO: What if setting the match limit fails?
|
||||
pcre2_set_match_limit(match_context, match_limit);
|
||||
}
|
||||
|
||||
PCRE2_SPTR pcre2_s = reinterpret_cast<PCRE2_SPTR>(s.c_str());
|
||||
PCRE2_SIZE startOffset = 0;
|
||||
|
||||
@ -213,11 +260,21 @@ bool Regex::searchGlobal(const std::string& s, std::vector<SMatchCapture>& captu
|
||||
pcre2_options = PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED;
|
||||
}
|
||||
int rc = pcre2_match(m_pc, pcre2_s, s.length(),
|
||||
startOffset, pcre2_options, match_data, NULL);
|
||||
startOffset, pcre2_options, match_data, match_context);
|
||||
PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
|
||||
|
||||
#else
|
||||
const char *subject = s.c_str();
|
||||
pcre_extra local_pce;
|
||||
pcre_extra *pce = m_pce;
|
||||
|
||||
if (m_pce != NULL && match_limit > 0) {
|
||||
local_pce = *m_pce;
|
||||
local_pce.match_limit = match_limit;
|
||||
local_pce.flags |= PCRE_EXTRA_MATCH_LIMIT;
|
||||
pce = &local_pce;
|
||||
}
|
||||
|
||||
int startOffset = 0;
|
||||
|
||||
while (startOffset <= s.length()) {
|
||||
@ -226,7 +283,12 @@ bool Regex::searchGlobal(const std::string& s, std::vector<SMatchCapture>& captu
|
||||
if (prev_match_zero_length) {
|
||||
pcre_options = PCRE_NOTEMPTY_ATSTART | PCRE_ANCHORED;
|
||||
}
|
||||
int rc = pcre_exec(m_pc, m_pce, subject, s.length(), startOffset, pcre_options, ovector, OVECCOUNT);
|
||||
int rc = pcre_exec(m_pc, pce, subject, s.length(), startOffset, pcre_options, ovector, OVECCOUNT);
|
||||
|
||||
RegexResult regex_result = to_regex_result(rc);
|
||||
if (regex_result != RegexResult::Ok) {
|
||||
return regex_result;
|
||||
}
|
||||
|
||||
#endif
|
||||
if (rc > 0) {
|
||||
@ -278,7 +340,7 @@ bool Regex::searchGlobal(const std::string& s, std::vector<SMatchCapture>& captu
|
||||
#ifdef WITH_PCRE2
|
||||
pcre2_match_data_free(match_data);
|
||||
#endif
|
||||
return (captures.size() > 0);
|
||||
return RegexResult::Ok;
|
||||
}
|
||||
|
||||
int Regex::search(const std::string& s, SMatch *match) const {
|
||||
@ -340,5 +402,30 @@ int Regex::search(const std::string& s) const {
|
||||
#endif
|
||||
}
|
||||
|
||||
RegexResult Regex::to_regex_result(int pcre_exec_result) const {
|
||||
if (
|
||||
pcre_exec_result > 0 ||
|
||||
#ifdef WITH_PCRE2
|
||||
pcre_exec_result == PCRE2_ERROR_NOMATCH
|
||||
#else
|
||||
pcre_exec_result == PCRE_ERROR_NOMATCH
|
||||
#endif
|
||||
) {
|
||||
return RegexResult::Ok;
|
||||
} else if(
|
||||
#ifdef WITH_PCRE2
|
||||
pcre_exec_result == PCRE2_ERROR_MATCHLIMIT
|
||||
#else
|
||||
pcre_exec_result == PCRE_ERROR_MATCHLIMIT
|
||||
#endif
|
||||
) {
|
||||
return RegexResult::ErrorMatchLimit;
|
||||
} else {
|
||||
// Note that this can include the case where the PCRE result was zero.
|
||||
// Zero is returned if the offset vector is not large enough and can be considered an error.
|
||||
return RegexResult::ErrorOther;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
} // namespace modsecurity
|
||||
|
@ -34,6 +34,12 @@ namespace Utils {
|
||||
|
||||
#define OVECCOUNT 900
|
||||
|
||||
enum class RegexResult {
|
||||
Ok,
|
||||
ErrorMatchLimit,
|
||||
ErrorOther,
|
||||
};
|
||||
|
||||
class SMatch {
|
||||
public:
|
||||
SMatch() :
|
||||
@ -76,13 +82,15 @@ class Regex {
|
||||
return (m_pc == NULL);
|
||||
}
|
||||
std::list<SMatch> searchAll(const std::string& s) const;
|
||||
bool searchOneMatch(const std::string& s, std::vector<SMatchCapture>& captures) const;
|
||||
bool searchGlobal(const std::string& s, std::vector<SMatchCapture>& captures) const;
|
||||
RegexResult searchOneMatch(const std::string& s, std::vector<SMatchCapture>& captures, unsigned long match_limit = 0) const;
|
||||
RegexResult searchGlobal(const std::string& s, std::vector<SMatchCapture>& captures, unsigned long match_limit = 0) const;
|
||||
int search(const std::string &s, SMatch *match) const;
|
||||
int search(const std::string &s) const;
|
||||
|
||||
const std::string pattern;
|
||||
private:
|
||||
RegexResult to_regex_result(int pcre_exec_result) const;
|
||||
|
||||
#if WITH_PCRE2
|
||||
pcre2_code *m_pc;
|
||||
int m_pcje;
|
||||
|
39
src/variables/msc_pcre_error.h
Normal file
39
src/variables/msc_pcre_error.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* ModSecurity, http://www.modsecurity.org/
|
||||
* Copyright (c) 2015 - 2022 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* You may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* If any of the files related to licensing are missing or if you have any
|
||||
* other questions related to licensing please contact Trustwave Holdings, Inc.
|
||||
* directly using the email address security@modsecurity.org.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <utility>
|
||||
|
||||
#ifndef SRC_VARIABLES_MSC_PCRE_ERROR_H_
|
||||
#define SRC_VARIABLES_MSC_PCRE_ERROR_H_
|
||||
|
||||
#include "src/variables/variable.h"
|
||||
|
||||
namespace modsecurity {
|
||||
|
||||
class Transaction;
|
||||
namespace variables {
|
||||
|
||||
|
||||
DEFINE_VARIABLE(MscPcreError, MSC_PCRE_ERROR, m_variableMscPcreError)
|
||||
|
||||
|
||||
} // namespace variables
|
||||
} // namespace modsecurity
|
||||
|
||||
#endif // SRC_VARIABLES_MSC_PCRE_ERROR_H_
|
39
src/variables/msc_pcre_limits_exceeded.h
Normal file
39
src/variables/msc_pcre_limits_exceeded.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* ModSecurity, http://www.modsecurity.org/
|
||||
* Copyright (c) 2015 - 2022 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* You may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* If any of the files related to licensing are missing or if you have any
|
||||
* other questions related to licensing please contact Trustwave Holdings, Inc.
|
||||
* directly using the email address security@modsecurity.org.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <utility>
|
||||
|
||||
#ifndef SRC_VARIABLES_MSC_PCRE_LIMITS_EXCEEDED_H_
|
||||
#define SRC_VARIABLES_MSC_PCRE_LIMITS_EXCEEDED_H_
|
||||
|
||||
#include "src/variables/variable.h"
|
||||
|
||||
namespace modsecurity {
|
||||
|
||||
class Transaction;
|
||||
namespace variables {
|
||||
|
||||
|
||||
DEFINE_VARIABLE(MscPcreLimitsExceeded, MSC_PCRE_LIMITS_EXCEEDED, m_variableMscPcreLimitsExceeded)
|
||||
|
||||
|
||||
} // namespace variables
|
||||
} // namespace modsecurity
|
||||
|
||||
#endif // SRC_VARIABLES_MSC_PCRE_LIMITS_EXCEEDED_H_
|
@ -202,6 +202,10 @@ class VariableMonkeyResolution {
|
||||
t->m_variableMatchedVar.evaluate(l);
|
||||
} else if (comp(variable, "MATCHED_VAR_NAME")) {
|
||||
t->m_variableMatchedVarName.evaluate(l);
|
||||
} else if (comp(variable, "MSC_PCRE_ERROR")) {
|
||||
t->m_variableMscPcreError.evaluate(l);
|
||||
} else if (comp(variable, "MSC_PCRE_LIMITS_EXCEEDED")) {
|
||||
t->m_variableMscPcreLimitsExceeded.evaluate(l);
|
||||
} else if (comp(variable, "MULTIPART_CRLF_LF_LINES")) {
|
||||
t->m_variableMultipartCrlfLFLines.evaluate(l);
|
||||
} else if (comp(variable, "MULTIPART_DATA_AFTER")) {
|
||||
@ -365,6 +369,10 @@ class VariableMonkeyResolution {
|
||||
vv = t->m_variableMatchedVar.resolveFirst();
|
||||
} else if (comp(variable, "MATCHED_VAR_NAME")) {
|
||||
vv = t->m_variableMatchedVarName.resolveFirst();
|
||||
} else if (comp(variable, "MSC_PCRE_ERROR")) {
|
||||
vv = t->m_variableMscPcreError.resolveFirst();
|
||||
} else if (comp(variable, "MSC_PCRE_LIMITS_EXCEEDED")) {
|
||||
vv = t->m_variableMscPcreLimitsExceeded.resolveFirst();
|
||||
} else if (comp(variable, "MULTIPART_CRLF_LF_LINES")) {
|
||||
vv = t->m_variableMultipartCrlfLFLines.resolveFirst();
|
||||
} else if (comp(variable, "MULTIPART_DATA_AFTER")) {
|
||||
|
@ -127,5 +127,95 @@
|
||||
"SecRuleEngine On",
|
||||
"SecRule REQUEST_HEADERS:Content-Type \"@rx a(b\" \"id:1,phase:2,pass,t:trim,block\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"enabled":1,
|
||||
"version_min":300000,
|
||||
"title":"Testing Operator :: @rx with PCRE error",
|
||||
"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":"/?rxtest=wwwwwwwwwwwwwwwwwwwwwowwwwwwwwwww",
|
||||
"method":"HEAD",
|
||||
"body": [ ]
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"body":[
|
||||
"no need."
|
||||
]
|
||||
},
|
||||
"expected":{
|
||||
"debug_log":"rx: regex error 'MATCH_LIMIT' for pattern",
|
||||
"error_log":"Matched \"Operator `StrEq' with parameter `1' against variable `MSC_PCRE_ERROR'"
|
||||
},
|
||||
"rules":[
|
||||
"SecRuleEngine On",
|
||||
"SecPcreMatchLimit 2",
|
||||
"SecRule ARGS:rxtest \"@rx (w+)+$\" \"id:1,phase:1,pass,t:trim,block\"",
|
||||
"SecRule MSC_PCRE_ERROR \"@streq 1\" \"id:2,phase:1,pass,t:trim,block\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"enabled":1,
|
||||
"version_min":300000,
|
||||
"title":"Testing Operator :: @rx with PCRE match limits exceeded",
|
||||
"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":"/?rxtest=wwwwwwwwwwwwwwwwwwwwwowwwwwwwwwww",
|
||||
"method":"HEAD",
|
||||
"body": [ ]
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"body":[
|
||||
"no need."
|
||||
]
|
||||
},
|
||||
"expected":{
|
||||
"debug_log":"rx: regex error 'MATCH_LIMIT' for pattern",
|
||||
"error_log":"Matched \"Operator `StrEq' with parameter `1' against variable `MSC_PCRE_LIMITS_EXCEEDED'"
|
||||
},
|
||||
"rules":[
|
||||
"SecRuleEngine On",
|
||||
"SecPcreMatchLimit 2",
|
||||
"SecRule ARGS:rxtest \"@rx (w+)+$\" \"id:1,phase:1,pass,t:trim,block\"",
|
||||
"SecRule MSC_PCRE_LIMITS_EXCEEDED \"@streq 1\" \"id:2,phase:1,pass,t:trim,block\""
|
||||
]
|
||||
}
|
||||
]
|
||||
|
Loading…
x
Reference in New Issue
Block a user