From b8f7fb441d20fefc9ac210e0ed6fd4fa123d5692 Mon Sep 17 00:00:00 2001 From: Felipe Zimmerle Date: Fri, 24 Jul 2015 19:15:25 -0300 Subject: [PATCH] Adds support to SecRemoteRules and Include directives This commit includes a refactoring on important pieces of the parser to allow it work in a stack fashion. Driver and Rules classes were simplified and the RulesProperties class was created. --- headers/modsecurity/rules.h | 91 +------ headers/modsecurity/rules_properties.h | 187 +++++++++++++ src/Makefile.am | 11 +- src/assay.cc | 6 +- src/parser/driver.cc | 49 ++-- src/parser/driver.h | 20 +- src/parser/seclang-parser.yy | 17 +- src/parser/seclang-scanner.ll | 211 +++++++++------ src/rules.cc | 65 ++--- test/regression/regression.cc | 2 +- test/test-cases/data/config_example.txt | 3 + test/test-cases/data/config_example2.txt | 2 + .../test-cases/regression/config-include.json | 256 ++++++++++++++++++ .../regression/config-secremoterules.json | 44 +++ 14 files changed, 726 insertions(+), 238 deletions(-) create mode 100644 headers/modsecurity/rules_properties.h create mode 100644 test/test-cases/data/config_example.txt create mode 100644 test/test-cases/data/config_example2.txt create mode 100644 test/test-cases/regression/config-include.json create mode 100644 test/test-cases/regression/config-secremoterules.json diff --git a/headers/modsecurity/rules.h b/headers/modsecurity/rules.h index 6e900e4c..dc131b3a 100644 --- a/headers/modsecurity/rules.h +++ b/headers/modsecurity/rules.h @@ -32,6 +32,7 @@ typedef struct Assay_t Assay; #include "modsecurity/modsecurity.h" #include "modsecurity/assay.h" +#include "modsecurity/rules_properties.h" #ifdef __cplusplus @@ -43,30 +44,20 @@ class Driver; } /** @ingroup ModSecurity_CPP_API */ -class Rules { +class Rules : public RulesProperties { public: Rules() - : m_referenceCount(0), - requestBodyLimit(0), - responseBodyLimit(0), - m_custom_debug_log(NULL) { } + : RulesProperties(NULL) { } - explicit Rules(DebugLog *custom_log) - : m_referenceCount(0), - m_custom_debug_log(custom_log) { } + Rules(DebugLog *customLog) + : RulesProperties(customLog) { } ~Rules(); void incrementReferenceCount(void); void decrementReferenceCount(void); - /** - * FIXME: - * - * names should follow a patterner - * - */ - bool loadFromUri(char *uri); + bool loadFromUri(const char *uri); bool loadRemote(char *key, char *uri); bool load(const char *rules); bool load(const char *rules, const std::string &ref); @@ -79,81 +70,13 @@ class Rules { int evaluate(int phase, Assay *assay); std::string getParserError(); - std::vector rules[7]; // Number of Phases. - - /** - * - * The RuleEngine enumerator consists in mapping the different states - * of the rule engine. - * - */ - enum RuleEngine { - /** - * Rules won't be evaluated if Rule Engine is set to DisabledRuleEngine - * - */ - DisabledRuleEngine, - /** - * Rules will be evaluated and disturb actions will take place if needed. - * - */ - EnabledRuleEngine, - /** - * Rules will be evaluated but it won't generate any disruptive action. - * - */ - DetectionOnlyRuleEngine - }; - - enum BodyLimitAction { - /** - * Process partial - * - */ - ProcessPartialBodyLimitAction, - /** - * Process partial - * - */ - RejectBodyLimitAction - }; - static const char *ruleEngineStateString(RuleEngine i) { - switch (i) { - case DisabledRuleEngine: - return "Disabled"; - case EnabledRuleEngine: - return "Enabled"; - case DetectionOnlyRuleEngine: - return "DetectionOnly"; - } - return NULL; - } - - RuleEngine secRuleEngine; - int sec_audit_type; - bool sec_audit_engine; - bool sec_request_body_access; - bool sec_response_body_access; - std::string audit_log_path; - std::string audit_log_parts; - std::string debug_log_path; - int debug_level; - DebugLog *debug_log; void debug(int level, std::string message); - std::list components; - - int requestBodyLimit; - int responseBodyLimit; - int requestBodyLimitAction; - int responseBodyLimitAction; std::ostringstream parserError; - AuditLog *audit_log; - + DebugLog *debugLog; private: int m_referenceCount; - DebugLog *m_custom_debug_log; }; #endif diff --git a/headers/modsecurity/rules_properties.h b/headers/modsecurity/rules_properties.h new file mode 100644 index 00000000..1f8a639a --- /dev/null +++ b/headers/modsecurity/rules_properties.h @@ -0,0 +1,187 @@ +/* + * ModSecurity, http://www.modsecurity.org/ + * Copyright (c) 2015 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. + * + */ + +#ifdef __cplusplus +#include +#include +#include +#include +#include +#endif + + +#ifndef HEADERS_MODSECURITY_RULES_PROPERTIES_H_ +#define HEADERS_MODSECURITY_RULES_PROPERTIES_H_ + +#include "modsecurity/modsecurity.h" +#include "modsecurity/assay.h" +#include "modsecurity/assay.h" + +#ifdef __cplusplus + +namespace ModSecurity { +class Rule; +class AuditLog; +namespace Parser { +class Driver; +} + +/** @ingroup ModSecurity_CPP_API */ +class RulesProperties { + public: + RulesProperties() + : audit_log(NULL), + customDebugLog(NULL), + remoteRulesActionOnFailed(AbortOnFailedRemoteRulesAction), + requestBodyLimit(0), + requestBodyLimitAction(ProcessPartialBodyLimitAction), + responseBodyLimit(0), + responseBodyLimitAction(ProcessPartialBodyLimitAction), + secRuleEngine(DetectionOnlyRuleEngine) { } + + explicit RulesProperties(DebugLog *customDebugLog) + : audit_log(NULL), + customDebugLog(customDebugLog), + remoteRulesActionOnFailed(AbortOnFailedRemoteRulesAction), + requestBodyLimit(0), + requestBodyLimitAction(ProcessPartialBodyLimitAction), + responseBodyLimit(0), + responseBodyLimitAction(ProcessPartialBodyLimitAction), + secRuleEngine(DetectionOnlyRuleEngine) { } + + ~RulesProperties() { }; + + std::vector rules[7]; // ModSecurity::Phases::NUMBER_OF_PHASES + + + /** + * + * The RuleEngine enumerator consists in mapping the different states + * of the rule engine. + * + */ + enum RuleEngine { + /** + * + * Rules won't be evaluated if Rule Engine is set to DisabledRuleEngine + * + */ + DisabledRuleEngine, + /** + * + * Rules will be evaluated and disturb actions will take place if needed. + * + */ + EnabledRuleEngine, + /** + * Rules will be evaluated but it won't generate any disruptive action. + * + */ + DetectionOnlyRuleEngine + }; + + /** + * + * Defines what actions should be taken in case the body (response or + * request) is bigger than the expected size. + * + */ + enum BodyLimitAction { + /** + * + * Process partial + * + */ + ProcessPartialBodyLimitAction, + /** + * + * Reject the request + * + */ + RejectBodyLimitAction + }; + + + /** + * + * Defines what actions should be taken in case the remote rules failed to + * be downloaded (independent of the circumstances) + * + * + */ + enum OnFailedRemoteRulesAction { + /** + * + * Abort + * + */ + AbortOnFailedRemoteRulesAction, + /** + * + * Warn on logging + * + */ + WarnOnFailedRemoteRulesAction + }; + + + static const char *ruleEngineStateString(RuleEngine i) { + switch (i) { + case DisabledRuleEngine: + return "Disabled"; + case EnabledRuleEngine: + return "Enabled"; + case DetectionOnlyRuleEngine: + return "DetectionOnly"; + } + return NULL; + } + + + RuleEngine secRuleEngine; + double requestBodyLimit; + double responseBodyLimit; + BodyLimitAction requestBodyLimitAction; + BodyLimitAction responseBodyLimitAction; + + DebugLog *customDebugLog; + + int sec_audit_type; + bool sec_audit_engine; + bool sec_request_body_access; + bool sec_response_body_access; + std::string audit_log_path; + std::string audit_log_parts; + std::string debug_log_path; + int debug_level; + std::list components; + + + std::ostringstream parserError; + + AuditLog *audit_log; + + OnFailedRemoteRulesAction remoteRulesActionOnFailed; + + +}; + +#endif + +#ifdef __cplusplus +} // namespace ModSecurity +#endif + +#endif // HEADERS_MODSECURITY_RULES_PROPERTIES_H_ diff --git a/src/Makefile.am b/src/Makefile.am index a18bcb7d..a26fcaf9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -22,11 +22,12 @@ MAINTAINERCLEANFILES = \ pkginclude_HEADERS = \ - ../headers/modsecurity/modsecurity.h \ - ../headers/modsecurity/assay.h \ - ../headers/modsecurity/rules.h \ - ../headers/modsecurity/debug_log.h \ - ../headers/modsecurity/intervention.h + ../headers/modsecurity/assay.h \ + ../headers/modsecurity/debug_log.h \ + ../headers/modsecurity/intervention.h \ + ../headers/modsecurity/modsecurity.h \ + ../headers/modsecurity/rules.h \ + ../headers/modsecurity/rules_properties.h VARIABLES = \ diff --git a/src/assay.cc b/src/assay.cc index 468831d3..60694c2f 100644 --- a/src/assay.cc +++ b/src/assay.cc @@ -517,7 +517,9 @@ int Assay::processRequestBody() { if (m_requestBodyType == WWWFormUrlEncoded) { std::string content = uri_decode(m_requestBody.str()); - content.erase(content.length()-1, 1); + if (content.empty() == false) { + content.pop_back(); + } /** * FIXME: @@ -577,7 +579,7 @@ int Assay::processRequestBody() { store_variable("FULL_REQUEST", fullRequest); store_variable("FULL_REQUEST_LENGTH", std::to_string(fullRequest.size())); - if (m_requestBody.tellp() <= 0) { + if (m_requestBody.tellp() > 0) { store_variable("REQUEST_BODY", m_requestBody.str()); store_variable("REQUEST_BODY_LENGTH", std::to_string(m_requestBody.str().size())); diff --git a/src/parser/driver.cc b/src/parser/driver.cc index 0cb24ea6..0777cd1e 100644 --- a/src/parser/driver.cc +++ b/src/parser/driver.cc @@ -64,14 +64,18 @@ int Driver::addSecRule(Rule *rule) { } } - this->rules[rule->phase].push_back(rule); - + rules[rule->phase].push_back(rule); return true; } int Driver::parse(const std::string &f, const std::string &ref) { - this->ref = ref; + if (ref.empty()) { + this->ref = "<>"; + } else { + this->ref = ref; + } + buffer = f; scan_begin(); yy::seclang_parser parser(*this); @@ -81,43 +85,48 @@ int Driver::parse(const std::string &f, const std::string &ref) { if (audit_log->init() == false) { return false; } - scan_end(); + return res == 0; } int Driver::parseFile(const std::string &f) { - this->ref = f; - file = f; - scan_begin(); - yy::seclang_parser parser(*this); - parser.set_debug_level(trace_parsing); - int res = parser.parse(); + std::ifstream t(f); + std::string str; - if (audit_log->init() == false) { + if (t.is_open() == false) { + parserError << "Failed to open the file: " << f << std::endl; return false; } - scan_end(); - return res == 0; + t.seekg(0, std::ios::end); + str.reserve(t.tellg()); + t.seekg(0, std::ios::beg); + + str.assign((std::istreambuf_iterator(t)), + std::istreambuf_iterator()); + + return parse(str, f); +} + + +void Driver::error(const yy::location& l, const std::string& m) { + error(l, m, ""); } void Driver::error(const yy::location& l, const std::string& m, const std::string& c) { if (parserError.tellp() == 0) { - parserError << "Parser error, "; + parserError << "Configuration error, "; parserError << "File: " << ref << ". "; parserError << "Line: " << l.end.line << ". "; parserError << "Column: " << l.end.column << ". "; } - parserError << c; -} - - -void Driver::parser_error(const yy::location& l, const std::string& m) { - parserError << ". " << m << "." << std::endl; + if (c.empty() == false) { + parserError << c; + } } diff --git a/src/parser/driver.h b/src/parser/driver.h index 32f872a1..bc376d08 100644 --- a/src/parser/driver.h +++ b/src/parser/driver.h @@ -27,6 +27,7 @@ #include "modsecurity/modsecurity.h" #include "src/rule.h" #include "modsecurity/rules.h" +#include "modsecurity/rules_properties.h" #include "src/audit_log.h" #include "parser/seclang-parser.hh" @@ -50,11 +51,7 @@ typedef struct Driver_t Driver; #endif -class Driver : public Rules { -/** - * @todo Place driver and parser under the correct namespace. - * - */ +class Driver : public RulesProperties { public: Driver(); virtual ~Driver(); @@ -63,30 +60,23 @@ class Driver : public Rules { int result; - // Handling the scanner. - void scan_begin(); + bool scan_begin(); void scan_end(); bool trace_scanning; - // Run the parser on file F. - // Return 0 on success. int parseFile(const std::string& f); int parse(const std::string& f, const std::string &ref); - // The name of the file being parsed. - // Used later to pass the file name to the location tracker. std::string file; - // Whether parser traces should be generated. bool trace_parsing; - - // Error handling. void error(const yy::location& l, const std::string& m); - void parser_error(const yy::location& l, const std::string& m); void error(const yy::location& l, const std::string& m, const std::string& c); + yy::location loc; + std::string ref; std::string buffer; }; diff --git a/src/parser/seclang-parser.yy b/src/parser/seclang-parser.yy index 9c46f54b..b362495c 100644 --- a/src/parser/seclang-parser.yy +++ b/src/parser/seclang-parser.yy @@ -4,6 +4,7 @@ %define parser_class_name {seclang_parser} %define api.token.constructor %define api.value.type variant +//%define api.namespace {ModSecurity::yy} %define parse.assert %code requires { @@ -103,6 +104,8 @@ using ModSecurity::Variables::Variable; %token CONFIG_VALUE_RELEVANT_ONLY %token CONFIG_VALUE_PROCESS_PARTIAL %token CONFIG_VALUE_REJECT +%token CONFIG_VALUE_ABORT +%token CONFIG_VALUE_WARN %token CONFIG_DIR_AUDIT_DIR %token CONFIG_DIR_AUDIT_DIR_MOD @@ -135,6 +138,10 @@ using ModSecurity::Variables::Variable; %token RUN_TIME_VAR_TIME_WDAY %token RUN_TIME_VAR_TIME_YEAR +%token CONFIG_INCLUDE +%token CONFIG_SEC_REMOTE_RULES +%token CONFIG_SEC_REMOTE_RULES_FAIL_ACTION + %token CONFIG_DIR_GEO_DB %token OPERATOR @@ -316,6 +323,14 @@ expression: { driver.responseBodyLimitAction = ModSecurity::Rules::BodyLimitAction::RejectBodyLimitAction; } + | CONFIG_SEC_REMOTE_RULES_FAIL_ACTION CONFIG_VALUE_ABORT + { + driver.remoteRulesActionOnFailed = Rules::OnFailedRemoteRulesAction::AbortOnFailedRemoteRulesAction; + } + | CONFIG_SEC_REMOTE_RULES_FAIL_ACTION CONFIG_VALUE_WARN + { + driver.remoteRulesActionOnFailed = Rules::OnFailedRemoteRulesAction::WarnOnFailedRemoteRulesAction; + } variables: variables PIPE VARIABLE @@ -572,5 +587,5 @@ void yy::seclang_parser::error (const location_type& l, const std::string& m) { - driver.parser_error (l, m); + driver.error (l, m); } diff --git a/src/parser/seclang-scanner.ll b/src/parser/seclang-scanner.ll index 152e8774..fac8e303 100755 --- a/src/parser/seclang-scanner.ll +++ b/src/parser/seclang-scanner.ll @@ -1,12 +1,16 @@ %{ /* -*- C++ -*- */ -# include -# include -# include -# include -# include "parser/driver.h" -# include "seclang-parser.hh" +#include +#include +#include +#include +#include "parser/driver.h" +#include "seclang-parser.hh" +#include "utils/https_client.h" +#include "utils.h" using ModSecurity::Parser::Driver; +using ModSecurity::Utils::HttpsClient; +using ModSecurity::split; // Work around an incompatibility in flex (at least versions // 2.5.31 through 2.5.33): it generates code that does @@ -16,9 +20,9 @@ using ModSecurity::Parser::Driver; # define yywrap() 1 // The location of the current token. -static yy::location loc; %} %option noyywrap nounput batch debug noinput + ACTION (?i:accuracy|allow|append|auditlog|block|capture|chain|ctl|deny|deprecatevar|drop|exec|expirevar|id:[0-9]+|id:'[0-9]+'|initcol|log|logdata|maturity|msg|multiMatch|noauditlog|nolog|pass|pause|phase:[0-9]+|prepend|proxy|redirect:[A-Z0-9_\|\&\:\/\/\.]+|rev|sanitiseArg|sanitiseMatched|sanitiseMatchedBytes|sanitiseRequestHeader|sanitiseResponseHeader|setuid|setrsc|setsid|setenv|setvar|skip|skipAfter|status:[0-9]+|tag|ver|xmlns) ACTION_SEVERITY (?i:severity:[0-9]+|severity:'[0-9]+'|severity:(EMERGENCY|ALERT|CRITICAL|ERROR|WARNING|NOTICE|INFO|DEBUG)|severity:'(EMERGENCY|ALERT|CRITICAL|ERROR|WARNING|NOTICE|INFO|DEBUG)') DIRECTIVE SecRule @@ -53,7 +57,11 @@ CONFIG_DIR_DEBUG_LVL SecDebugLogLevel CONFIG_COMPONENT_SIG (?i:SecComponentSignature) -CONFIG_INCLUDE Include +CONFIG_INCLUDE (?i:Include) +CONFIG_SEC_REMOTE_RULES (?i:SecRemoteRules) +CONFIG_SEC_REMOTE_RULES_FAIL_ACTION (?i:SecRemoteRulesFailAction) + + DICT_ELEMENT [A-Za-z_]+ @@ -92,8 +100,10 @@ CONFIG_VALUE_RELEVANT_ONLY RelevantOnly CONFIG_VALUE_PROCESS_PARTIAL (?i:ProcessPartial) CONFIG_VALUE_REJECT (?i:Reject) +CONFIG_VALUE_ABORT (?i:Abort) +CONFIG_VALUE_WARN (?i:Warn) -CONFIG_VALUE_PATH [A-Za-z_/\.]+ +CONFIG_VALUE_PATH [0-9A-Za-z_/\.\-]+ AUDIT_PARTS [ABCDEFHJKZ]+ CONFIG_VALUE_NUMBER [0-9]+ @@ -102,112 +112,155 @@ FREE_TEXT_NEW_LINE [^\"|\n]+ %{ // Code run each time a pattern is matched. - # define YY_USER_ACTION loc.columns (yyleng); + # define YY_USER_ACTION driver.loc.columns (yyleng); %} %% %{ // Code run each time yylex is called. - loc.step(); + driver.loc.step(); %} -{DIRECTIVE} { return yy::seclang_parser::make_DIRECTIVE(yytext, loc); } -{TRANSFORMATION} { return yy::seclang_parser::make_TRANSFORMATION(yytext, loc); } -{CONFIG_DIR_RULE_ENG} { return yy::seclang_parser::make_CONFIG_DIR_RULE_ENG(yytext, loc); } -{CONFIG_DIR_RES_BODY} { return yy::seclang_parser::make_CONFIG_DIR_RES_BODY(yytext, loc); } -{CONFIG_DIR_REQ_BODY} { return yy::seclang_parser::make_CONFIG_DIR_REQ_BODY(yytext, loc); } +{DIRECTIVE} { return yy::seclang_parser::make_DIRECTIVE(yytext, driver.loc); } +{TRANSFORMATION} { return yy::seclang_parser::make_TRANSFORMATION(yytext, driver.loc); } +{CONFIG_DIR_RULE_ENG} { return yy::seclang_parser::make_CONFIG_DIR_RULE_ENG(yytext, driver.loc); } +{CONFIG_DIR_RES_BODY} { return yy::seclang_parser::make_CONFIG_DIR_RES_BODY(yytext, driver.loc); } +{CONFIG_DIR_REQ_BODY} { return yy::seclang_parser::make_CONFIG_DIR_REQ_BODY(yytext, driver.loc); } %{ /* Audit log entries */ %} -{CONFIG_DIR_AUDIT_DIR}[ ]{CONFIG_VALUE_PATH} { return yy::seclang_parser::make_CONFIG_DIR_AUDIT_DIR(strchr(yytext, ' ') + 1, loc); } -{CONFIG_DIR_AUDIT_DIR_MOD}[ ]{CONFIG_VALUE_NUMBER} { return yy::seclang_parser::make_CONFIG_DIR_AUDIT_DIR_MOD(strchr(yytext, ' ') + 1, loc); } -{CONFIG_DIR_AUDIT_ENG} { return yy::seclang_parser::make_CONFIG_DIR_AUDIT_ENG(yytext, loc); } -{CONFIG_DIR_AUDIT_FLE_MOD}[ ]{CONFIG_VALUE_NUMBER} { return yy::seclang_parser::make_CONFIG_DIR_AUDIT_FLE_MOD(strchr(yytext, ' ') + 1, loc); } -{CONFIG_DIR_AUDIT_LOG2}[ ]{CONFIG_VALUE_PATH} { return yy::seclang_parser::make_CONFIG_DIR_AUDIT_LOG2(strchr(yytext, ' ') + 1, loc); } -{CONFIG_DIR_AUDIT_LOG}[ ]{CONFIG_VALUE_PATH} { return yy::seclang_parser::make_CONFIG_DIR_AUDIT_LOG(strchr(yytext, ' ') + 1, loc); } -{CONFIG_DIR_AUDIT_LOG_P}[ ]{AUDIT_PARTS} { return yy::seclang_parser::make_CONFIG_DIR_AUDIT_LOG_P(strchr(yytext, ' ') + 1, loc); } -{CONFIG_DIR_AUDIT_STS}[ ]["]{FREE_TEXT}["] { return yy::seclang_parser::make_CONFIG_DIR_AUDIT_STS(strchr(yytext, ' ') + 1, loc); } -{CONFIG_DIR_AUDIT_TPE} { return yy::seclang_parser::make_CONFIG_DIR_AUDIT_TPE(yytext, loc); } +{CONFIG_DIR_AUDIT_DIR}[ ]{CONFIG_VALUE_PATH} { return yy::seclang_parser::make_CONFIG_DIR_AUDIT_DIR(strchr(yytext, ' ') + 1, driver.loc); } +{CONFIG_DIR_AUDIT_DIR_MOD}[ ]{CONFIG_VALUE_NUMBER} { return yy::seclang_parser::make_CONFIG_DIR_AUDIT_DIR_MOD(strchr(yytext, ' ') + 1, driver.loc); } +{CONFIG_DIR_AUDIT_ENG} { return yy::seclang_parser::make_CONFIG_DIR_AUDIT_ENG(yytext, driver.loc); } +{CONFIG_DIR_AUDIT_FLE_MOD}[ ]{CONFIG_VALUE_NUMBER} { return yy::seclang_parser::make_CONFIG_DIR_AUDIT_FLE_MOD(strchr(yytext, ' ') + 1, driver.loc); } +{CONFIG_DIR_AUDIT_LOG2}[ ]{CONFIG_VALUE_PATH} { return yy::seclang_parser::make_CONFIG_DIR_AUDIT_LOG2(strchr(yytext, ' ') + 1, driver.loc); } +{CONFIG_DIR_AUDIT_LOG}[ ]{CONFIG_VALUE_PATH} { return yy::seclang_parser::make_CONFIG_DIR_AUDIT_LOG(strchr(yytext, ' ') + 1, driver.loc); } +{CONFIG_DIR_AUDIT_LOG_P}[ ]{AUDIT_PARTS} { return yy::seclang_parser::make_CONFIG_DIR_AUDIT_LOG_P(strchr(yytext, ' ') + 1, driver.loc); } +{CONFIG_DIR_AUDIT_STS}[ ]["]{FREE_TEXT}["] { return yy::seclang_parser::make_CONFIG_DIR_AUDIT_STS(strchr(yytext, ' ') + 1, driver.loc); } +{CONFIG_DIR_AUDIT_TPE} { return yy::seclang_parser::make_CONFIG_DIR_AUDIT_TPE(yytext, driver.loc); } %{ /* Debug log entries */ %} -{CONFIG_DIR_DEBUG_LOG}[ ]{CONFIG_VALUE_PATH} { return yy::seclang_parser::make_CONFIG_DIR_DEBUG_LOG(strchr(yytext, ' ') + 1, loc); } -{CONFIG_DIR_DEBUG_LVL}[ ]{CONFIG_VALUE_NUMBER} { return yy::seclang_parser::make_CONFIG_DIR_DEBUG_LVL(strchr(yytext, ' ') + 1, loc); } +{CONFIG_DIR_DEBUG_LOG}[ ]{CONFIG_VALUE_PATH} { return yy::seclang_parser::make_CONFIG_DIR_DEBUG_LOG(strchr(yytext, ' ') + 1, driver.loc); } +{CONFIG_DIR_DEBUG_LVL}[ ]{CONFIG_VALUE_NUMBER} { return yy::seclang_parser::make_CONFIG_DIR_DEBUG_LVL(strchr(yytext, ' ') + 1, driver.loc); } %{ /* Variables */ %} -{VARIABLE}:?{DICT_ELEMENT}? { return yy::seclang_parser::make_VARIABLE(yytext, loc); } -{RUN_TIME_VAR_DUR} { return yy::seclang_parser::make_RUN_TIME_VAR_DUR(yytext, loc); } -{RUN_TIME_VAR_ENV}:?{DICT_ELEMENT}? { return yy::seclang_parser::make_RUN_TIME_VAR_ENV(yytext, loc); } -{RUN_TIME_VAR_BLD} { return yy::seclang_parser::make_RUN_TIME_VAR_BLD(yytext, loc); } -{RUN_TIME_VAR_HSV} { return yy::seclang_parser::make_RUN_TIME_VAR_HSV(yytext, loc); } +{VARIABLE}:?{DICT_ELEMENT}? { return yy::seclang_parser::make_VARIABLE(yytext, driver.loc); } +{RUN_TIME_VAR_DUR} { return yy::seclang_parser::make_RUN_TIME_VAR_DUR(yytext, driver.loc); } +{RUN_TIME_VAR_ENV}:?{DICT_ELEMENT}? { return yy::seclang_parser::make_RUN_TIME_VAR_ENV(yytext, driver.loc); } +{RUN_TIME_VAR_BLD} { return yy::seclang_parser::make_RUN_TIME_VAR_BLD(yytext, driver.loc); } +{RUN_TIME_VAR_HSV} { return yy::seclang_parser::make_RUN_TIME_VAR_HSV(yytext, driver.loc); } %{ /* Variables: TIME */ %} -{RUN_TIME_VAR_TIME} { return yy::seclang_parser::make_RUN_TIME_VAR_TIME(yytext, loc); } -{RUN_TIME_VAR_TIME_DAY} { return yy::seclang_parser::make_RUN_TIME_VAR_TIME_DAY(yytext, loc); } -{RUN_TIME_VAR_TIME_EPOCH} { return yy::seclang_parser::make_RUN_TIME_VAR_TIME_EPOCH(yytext, loc); } -{RUN_TIME_VAR_TIME_HOUR} { return yy::seclang_parser::make_RUN_TIME_VAR_TIME_HOUR(yytext, loc); } -{RUN_TIME_VAR_TIME_MIN} { return yy::seclang_parser::make_RUN_TIME_VAR_TIME_MIN(yytext, loc); } -{RUN_TIME_VAR_TIME_MON} { return yy::seclang_parser::make_RUN_TIME_VAR_TIME_MON(yytext, loc); } -{RUN_TIME_VAR_TIME_SEC} { return yy::seclang_parser::make_RUN_TIME_VAR_TIME_SEC(yytext, loc); } -{RUN_TIME_VAR_TIME_WDAY} { return yy::seclang_parser::make_RUN_TIME_VAR_TIME_WDAY(yytext, loc); } -{RUN_TIME_VAR_TIME_YEAR} { return yy::seclang_parser::make_RUN_TIME_VAR_TIME_YEAR(yytext, loc); } +{RUN_TIME_VAR_TIME} { return yy::seclang_parser::make_RUN_TIME_VAR_TIME(yytext, driver.loc); } +{RUN_TIME_VAR_TIME_DAY} { return yy::seclang_parser::make_RUN_TIME_VAR_TIME_DAY(yytext, driver.loc); } +{RUN_TIME_VAR_TIME_EPOCH} { return yy::seclang_parser::make_RUN_TIME_VAR_TIME_EPOCH(yytext, driver.loc); } +{RUN_TIME_VAR_TIME_HOUR} { return yy::seclang_parser::make_RUN_TIME_VAR_TIME_HOUR(yytext, driver.loc); } +{RUN_TIME_VAR_TIME_MIN} { return yy::seclang_parser::make_RUN_TIME_VAR_TIME_MIN(yytext, driver.loc); } +{RUN_TIME_VAR_TIME_MON} { return yy::seclang_parser::make_RUN_TIME_VAR_TIME_MON(yytext, driver.loc); } +{RUN_TIME_VAR_TIME_SEC} { return yy::seclang_parser::make_RUN_TIME_VAR_TIME_SEC(yytext, driver.loc); } +{RUN_TIME_VAR_TIME_WDAY} { return yy::seclang_parser::make_RUN_TIME_VAR_TIME_WDAY(yytext, driver.loc); } +{RUN_TIME_VAR_TIME_YEAR} { return yy::seclang_parser::make_RUN_TIME_VAR_TIME_YEAR(yytext, driver.loc); } %{ /* Geo DB loopkup */ %} -{CONFIG_DIR_GEO_DB}[ ]{FREE_TEXT_NEW_LINE} { return yy::seclang_parser::make_CONFIG_DIR_GEO_DB(strchr(yytext, ' ') + 1, loc); } +{CONFIG_DIR_GEO_DB}[ ]{FREE_TEXT_NEW_LINE} { return yy::seclang_parser::make_CONFIG_DIR_GEO_DB(strchr(yytext, ' ') + 1, driver.loc); } %{ /* Request body limit */ %} -{CONFIG_DIR_REQ_BODY_LIMIT}[ ]{CONFIG_VALUE_NUMBER} { return yy::seclang_parser::make_CONFIG_DIR_REQ_BODY_LIMIT(strchr(yytext, ' ') + 1, loc); } -{CONFIG_DIR_REQ_BODY_LIMIT_ACTION} { return yy::seclang_parser::make_CONFIG_DIR_REQ_BODY_LIMIT_ACTION(yytext, loc); } +{CONFIG_DIR_REQ_BODY_LIMIT}[ ]{CONFIG_VALUE_NUMBER} { return yy::seclang_parser::make_CONFIG_DIR_REQ_BODY_LIMIT(strchr(yytext, ' ') + 1, driver.loc); } +{CONFIG_DIR_REQ_BODY_LIMIT_ACTION} { return yy::seclang_parser::make_CONFIG_DIR_REQ_BODY_LIMIT_ACTION(yytext, driver.loc); } %{ /* Reponse body limit */ %} -{CONFIG_DIR_RES_BODY_LIMIT}[ ]{CONFIG_VALUE_NUMBER} { return yy::seclang_parser::make_CONFIG_DIR_RES_BODY_LIMIT(strchr(yytext, ' ') + 1, loc); } -{CONFIG_DIR_RES_BODY_LIMIT_ACTION} { return yy::seclang_parser::make_CONFIG_DIR_RES_BODY_LIMIT_ACTION(yytext, loc); } +{CONFIG_DIR_RES_BODY_LIMIT}[ ]{CONFIG_VALUE_NUMBER} { return yy::seclang_parser::make_CONFIG_DIR_RES_BODY_LIMIT(strchr(yytext, ' ') + 1, driver.loc); } +{CONFIG_DIR_RES_BODY_LIMIT_ACTION} { return yy::seclang_parser::make_CONFIG_DIR_RES_BODY_LIMIT_ACTION(yytext, driver.loc); } -{CONFIG_COMPONENT_SIG}[ ]["]{FREE_TEXT}["] { return yy::seclang_parser::make_CONFIG_COMPONENT_SIG(strchr(yytext, ' ') + 2, loc); } +{CONFIG_COMPONENT_SIG}[ ]["]{FREE_TEXT}["] { return yy::seclang_parser::make_CONFIG_COMPONENT_SIG(strchr(yytext, ' ') + 2, driver.loc); } -{CONFIG_VALUE_ON} { return yy::seclang_parser::make_CONFIG_VALUE_ON(yytext, loc); } -{CONFIG_VALUE_OFF} { return yy::seclang_parser::make_CONFIG_VALUE_OFF(yytext, loc); } -{CONFIG_VALUE_SERIAL} { return yy::seclang_parser::make_CONFIG_VALUE_SERIAL(yytext, loc); } -{CONFIG_VALUE_PARALLEL} { return yy::seclang_parser::make_CONFIG_VALUE_PARALLEL(yytext, loc); } -{CONFIG_VALUE_DETC} { return yy::seclang_parser::make_CONFIG_VALUE_DETC(yytext, loc); } -{CONFIG_VALUE_RELEVANT_ONLY} { return yy::seclang_parser::make_CONFIG_VALUE_RELEVANT_ONLY(yytext, loc); } -{CONFIG_VALUE_PROCESS_PARTIAL} { return yy::seclang_parser::make_CONFIG_VALUE_PROCESS_PARTIAL(yytext, loc); } -{CONFIG_VALUE_REJECT} { return yy::seclang_parser::make_CONFIG_VALUE_REJECT(yytext, loc); } +{CONFIG_VALUE_WARN} { return yy::seclang_parser::make_CONFIG_VALUE_WARN(yytext, driver.loc); } +{CONFIG_VALUE_ABORT} { return yy::seclang_parser::make_CONFIG_VALUE_ABORT(yytext, driver.loc); } +{CONFIG_VALUE_ON} { return yy::seclang_parser::make_CONFIG_VALUE_ON(yytext, driver.loc); } +{CONFIG_VALUE_OFF} { return yy::seclang_parser::make_CONFIG_VALUE_OFF(yytext, driver.loc); } +{CONFIG_VALUE_SERIAL} { return yy::seclang_parser::make_CONFIG_VALUE_SERIAL(yytext, driver.loc); } +{CONFIG_VALUE_PARALLEL} { return yy::seclang_parser::make_CONFIG_VALUE_PARALLEL(yytext, driver.loc); } +{CONFIG_VALUE_DETC} { return yy::seclang_parser::make_CONFIG_VALUE_DETC(yytext, driver.loc); } +{CONFIG_VALUE_RELEVANT_ONLY} { return yy::seclang_parser::make_CONFIG_VALUE_RELEVANT_ONLY(yytext, driver.loc); } +{CONFIG_VALUE_PROCESS_PARTIAL} { return yy::seclang_parser::make_CONFIG_VALUE_PROCESS_PARTIAL(yytext, driver.loc); } +{CONFIG_VALUE_REJECT} { return yy::seclang_parser::make_CONFIG_VALUE_REJECT(yytext, driver.loc); } + +["]{OPERATOR}[ ]{FREE_TEXT}["] { return yy::seclang_parser::make_OPERATOR(yytext, driver.loc); } +["]{OPERATORNOARG}["] { return yy::seclang_parser::make_OPERATOR(yytext, driver.loc); } +{ACTION} { return yy::seclang_parser::make_ACTION(yytext, driver.loc); } +{ACTION_SEVERITY} { return yy::seclang_parser::make_ACTION_SEVERITY(yytext, driver.loc); } +["] { return yy::seclang_parser::make_QUOTATION_MARK(driver.loc); } +[,] { return yy::seclang_parser::make_COMMA(driver.loc); } +[|] { return yy::seclang_parser::make_PIPE(driver.loc); } +{VARIABLENOCOLON} { return yy::seclang_parser::make_VARIABLE(yytext, driver.loc); } +[ \t]+ { return yy::seclang_parser::make_SPACE(driver.loc); } +[\n]+ { driver.loc.lines(yyleng); driver.loc.step(); } +. { driver.error (driver.loc, "invalid character", yytext); } +<> { + if (yyin) { + fclose(yyin); + } + + yypop_buffer_state(); + if (!YY_CURRENT_BUFFER) + { + return yy::seclang_parser::make_END(driver.loc); + } + } + +%{ /* Include external configurations */ %} +{CONFIG_INCLUDE}[ ]{CONFIG_VALUE_PATH} { + const char *file = strchr(yytext, ' ') + 1; + yyin = fopen(file, "r" ); + yypush_buffer_state(yy_create_buffer( yyin, YY_BUF_SIZE )); +} + +{CONFIG_SEC_REMOTE_RULES}[ ][^ ]+[ ][^\n\r ]+ { + HttpsClient c; + std::string key; + std::string url; + + std::vector conf = split(yytext, ' '); + key = conf[1]; + url = conf[2]; + + YY_BUFFER_STATE temp = YY_CURRENT_BUFFER; + yypush_buffer_state(temp); + + bool ret = c.download(url); + + if (ret == false) { + /** + * TODO: Implement the fail action. + * + */ + if (driver.remoteRulesActionOnFailed == Rules::OnFailedRemoteRulesAction::WarnOnFailedRemoteRulesAction) { + } + if (driver.remoteRulesActionOnFailed == Rules::OnFailedRemoteRulesAction::AbortOnFailedRemoteRulesAction) { + } + } + + yy_scan_string(c.content.c_str()); +} + +{CONFIG_SEC_REMOTE_RULES_FAIL_ACTION} { return yy::seclang_parser::make_CONFIG_SEC_REMOTE_RULES_FAIL_ACTION(yytext, driver.loc); } -["]{OPERATOR}[ ]{FREE_TEXT}["] { return yy::seclang_parser::make_OPERATOR(yytext, loc); } -["]{OPERATORNOARG}["] { return yy::seclang_parser::make_OPERATOR(yytext, loc); } -{ACTION} { return yy::seclang_parser::make_ACTION(yytext, loc); } -{ACTION_SEVERITY} { return yy::seclang_parser::make_ACTION_SEVERITY(yytext, loc); } -["] { return yy::seclang_parser::make_QUOTATION_MARK(loc); } -[,] { return yy::seclang_parser::make_COMMA(loc); } -[|] { return yy::seclang_parser::make_PIPE(loc); } -{VARIABLENOCOLON} { return yy::seclang_parser::make_VARIABLE(yytext, loc); } -[ \t]+ { return yy::seclang_parser::make_SPACE(loc); } -[\n]+ { loc.lines(yyleng); loc.step(); } -. { driver.error (loc, "invalid character", yytext); } -<> { return yy::seclang_parser::make_END(loc); } %% namespace ModSecurity { -void Driver::scan_begin () { +bool Driver::scan_begin () { yy_flex_debug = trace_scanning; + if (buffer.empty() == false) { yy_scan_string(buffer.c_str()); - } else if (file.empty() == false) { - if (!(yyin = fopen (file.c_str (), "r"))) { - // FIXME: we should return a decent error. - exit (EXIT_FAILURE); - } + return true; } - + return false; } void Driver::scan_end () { - if (buffer.empty() == false) { - yy_scan_string(buffer.c_str()); - } else if (file.empty() == false) { - fclose(yyin); - } } } + diff --git a/src/rules.cc b/src/rules.cc index be358891..3f03a1f1 100644 --- a/src/rules.cc +++ b/src/rules.cc @@ -97,11 +97,11 @@ Rules::~Rules() { * @retval false Problem loading the rules. * */ -bool Rules::loadFromUri(char *uri) { +bool Rules::loadFromUri(const char *uri) { Driver *driver = new Driver(); if (driver->parseFile(uri) == false) { - parserError << driver->parserError.rdbuf(); + parserError << driver->parserError.str(); return false; } @@ -111,6 +111,19 @@ bool Rules::loadFromUri(char *uri) { return true; } +bool Rules::load(const char *file, const std::string &ref) { + Driver *driver = new Driver(); + + if (driver->parse(file, ref) == false) { + parserError << driver->parserError.str(); + return false; + } + this->merge(driver); + delete driver; + + return true; +} + bool Rules::loadRemote(char *key, char *uri) { HttpsClient client; @@ -124,22 +137,8 @@ bool Rules::loadRemote(char *key, char *uri) { } -bool Rules::load(const char *plain_rules) { - return this->load(plain_rules, ""); -} - - -bool Rules::load(const char *plain_rules, const std::string &ref) { - Driver *driver = new Driver(); - - if (driver->parse(plain_rules, ref) == false) { - parserError << driver->parserError.str(); - return false; - } - this->merge(driver); - delete driver; - - return true; +bool Rules::load(const char *plainRules) { + return this->load(plainRules, ""); } @@ -188,23 +187,20 @@ int Rules::merge(Driver *from) { this->requestBodyLimitAction = from->requestBodyLimitAction; this->responseBodyLimitAction = from->responseBodyLimitAction; - if (m_custom_debug_log) { - this->debug_log = m_custom_debug_log->new_instance(); + if (customDebugLog) { + this->debugLog = customDebugLog->new_instance(); } else { - this->debug_log = new DebugLog(); + this->debugLog = new DebugLog(); } this->audit_log = from->audit_log; - this->debug_log->setDebugLevel(this->debug_level); - this->debug_log->setOutputFile(this->debug_log_path); + this->debugLog->setDebugLevel(this->debug_level); + this->debugLog->setOutputFile(this->debug_log_path); return 0; } -void Rules::debug(int level, std::string message) { - this->debug_log->write_log(level, message); -} int Rules::merge(Rules *from) { for (int i = 0; i < ModSecurity::Phases::NUMBER_OF_PHASES; i++) { @@ -226,21 +222,28 @@ int Rules::merge(Rules *from) { this->requestBodyLimitAction = from->requestBodyLimitAction; this->responseBodyLimitAction = from->responseBodyLimitAction; - if (m_custom_debug_log) { - this->debug_log = m_custom_debug_log->new_instance(); + if (customDebugLog) { + this->debugLog = customDebugLog->new_instance(); } else { - this->debug_log = new DebugLog(); + this->debugLog = new DebugLog(); } this->audit_log = from->audit_log; - this->debug_log->setDebugLevel(this->debug_level); - this->debug_log->setOutputFile(this->debug_log_path); + this->debugLog->setDebugLevel(this->debug_level); + this->debugLog->setOutputFile(this->debug_log_path); return 0; } +void Rules::debug(int level, std::string message) { + if (debugLog != NULL) { + debugLog->write_log(level, message); + } +} + + void Rules::dump() { std::cout << "Rules: " << std::endl; for (int i = 0; i < ModSecurity::Phases::NUMBER_OF_PHASES; i++) { diff --git a/test/regression/regression.cc b/test/regression/regression.cc index f4ad56b9..2da96b30 100644 --- a/test/regression/regression.cc +++ b/test/regression/regression.cc @@ -161,7 +161,7 @@ end: modsec_assay->processLogging(r.status); CustomDebugLog *d = reinterpret_cast - (modsec_rules->debug_log); + (modsec_rules->debugLog); if (!d->contains(t->debug_log)) { std::cout << "Debug log was not matching the expected results."; diff --git a/test/test-cases/data/config_example.txt b/test/test-cases/data/config_example.txt new file mode 100644 index 00000000..39d369c7 --- /dev/null +++ b/test/test-cases/data/config_example.txt @@ -0,0 +1,3 @@ +Include test-cases/data/config_example2.txt +SecRule ARGS "@contains config_example" "id:10,pass,t:trim" +Include test-cases/data/config_example2.txt \ No newline at end of file diff --git a/test/test-cases/data/config_example2.txt b/test/test-cases/data/config_example2.txt new file mode 100644 index 00000000..fa8a7102 --- /dev/null +++ b/test/test-cases/data/config_example2.txt @@ -0,0 +1,2 @@ +SecRule ARGS "@contains config_example2" "id:20,pass,t:trim" + diff --git a/test/test-cases/regression/config-include.json b/test/test-cases/regression/config-include.json new file mode 100644 index 00000000..1b48f411 --- /dev/null +++ b/test/test-cases/regression/config-include.json @@ -0,0 +1,256 @@ +[ + { + "enabled":1, + "version_min":300000, + "title":"Include (1/6)", + "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":"*/*" + }, + "uri":"/?key=value&key=other_value", + "protocol":"GET" + }, + "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":"Executing operator \"@contains \" with param \"config_example2\" against ARGS." + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecDebugLogLevel 9", + "Include test-cases/data/config_example.txt", + "SecRule ARGS \"@contains test\" \"id:9,pass,t:trim\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Include (2/6)", + "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":"*/*" + }, + "uri":"/?key=value&key=other_value", + "protocol":"GET" + }, + "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":"Executing operator \"@contains \" with param \"config_example\" against ARGS." + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecDebugLogLevel 9", + "Include test-cases/data/config_example.txt", + "SecRule ARGS \"@contains test\" \"id:9,pass,t:trim\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Include (3/6)", + "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":"*/*" + }, + "uri":"/?key=value&key=other_value", + "protocol":"GET" + }, + "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":"Executing operator \"@contains \" with param \"config_example2\" against ARGS." + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecDebugLogLevel 9", + "Include test-cases/data/config_example2.txt", + "SecRule ARGS \"@contains test\" \"id:9,pass,t:trim\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Include (4/6)", + "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":"*/*" + }, + "uri":"/?key=value&key=other_value", + "protocol":"GET" + }, + "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":"Executing operator \"@contains \" with param \"test\" against ARGS." + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecDebugLogLevel 9", + "SecRule ARGS \"@contains test\" \"id:9,pass,t:trim\"", + "Include test-cases/data/config_example2.txt" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Include (5/6)", + "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":"*/*" + }, + "uri":"/?key=value&key=other_value", + "protocol":"GET" + }, + "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":"Executing operator \"@contains \" with param \"config_example2\" against ARGS." + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecDebugLogLevel 9", + "SecRule ARGS \"@contains test\" \"id:1,pass,t:trim\"", + "Include test-cases/data/config_example.txt", + "Include test-cases/data/config_example.txt", + "SecRule ARGS \"@contains test\" \"id:3,pass,t:trim\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Include (6/6)", + "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":"*/*" + }, + "uri":"/?key=value&key=other_value", + "protocol":"GET" + }, + "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":"Executing operator \"@contains \" with param \"test\" against ARGS." + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecDebugLogLevel 9", + "Include test-cases/data/config_example2.txt", + "SecRule ARGS \"@contains test\" \"id:9,pass,t:trim\"" + ] + } +] \ No newline at end of file diff --git a/test/test-cases/regression/config-secremoterules.json b/test/test-cases/regression/config-secremoterules.json new file mode 100644 index 00000000..78ea208e --- /dev/null +++ b/test/test-cases/regression/config-secremoterules.json @@ -0,0 +1,44 @@ +[ + { + "enabled":1, + "version_min":300000, + "title":"Include", + "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":"*/*" + }, + "uri":"/?key=value&key=other_value", + "protocol":"GET" + }, + "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":"Executing operator \"@pmfromfile \" with param \"https://www.modsecurity.org/modsecurity-regression-test.txt\" against REQUEST_FILENAME" + }, + "rules":[ + "SecRuleEngine On", + "SecDebugLog \/tmp\/modsec_debug.log", + "SecDebugLogLevel 9", + "SecRemoteRules key https://www.modsecurity.org/modsecurity-regression-test-secremoterules.txt", + "SecRule ARGS \"@contains somethingelse\" \"id:9,pass,t:trim\"" + ] + } +] \ No newline at end of file