Adds support to SecWebAppID

This commit is contained in:
Felipe Zimmerle 2017-11-08 09:02:42 -03:00
parent 37c34f3e65
commit 082a3e3287
No known key found for this signature in database
GPG Key ID: E6DFB08CE8B11277
16 changed files with 3027 additions and 2915 deletions

View File

@ -1,7 +1,9 @@
v3.0.????? - ?
---------------------------
- Adds support for SecWebAppId
[Issue #1442 - @zimmerle, @victorhora]
- Adds support for SecRuleRemoveByTag.
[Issue #1476 - @zimmerle, @victorhora]
- Adds support for update target by message.

View File

@ -52,7 +52,7 @@ class Collection {
virtual void del(const std::string& key) = 0;
virtual std::unique_ptr<std::string> resolveFirst(
const std::string& var) = 0;
const std::string& var) = 0;
virtual void resolveSingleMatch(const std::string& var,
std::vector<const Variable *> *l) = 0;
@ -62,52 +62,129 @@ class Collection {
std::vector<const Variable *> *l) = 0;
/* store */
virtual void store(std::string key, std::string compartment,
std::string value) {
std::string nkey = compartment + "::" + key;
store(nkey, value);
}
virtual void store(std::string key, std::string compartment,
std::string compartment2, std::string value) {
std::string nkey = compartment + "::" + compartment2 + "::" + key;
store(nkey, value);
}
/* storeOrUpdateFirst */
virtual bool storeOrUpdateFirst(const std::string &key,
std::string compartment, const std::string &value) {
std::string nkey = compartment + "::" + key;
return storeOrUpdateFirst(nkey, value);
}
virtual bool storeOrUpdateFirst(const std::string &key,
std::string compartment, std::string compartment2,
const std::string &value) {
std::string nkey = compartment + "::" + compartment2 + "::" + key;
return storeOrUpdateFirst(nkey, value);
}
/* updateFirst */
virtual bool updateFirst(const std::string &key, std::string compartment,
const std::string &value) {
std::string nkey = compartment + "::" + key;
return updateFirst(nkey, value);
}
virtual bool updateFirst(const std::string &key, std::string compartment,
std::string compartment2, const std::string &value) {
std::string nkey = compartment + "::" + compartment2 + "::" + key;
return updateFirst(nkey, value);
}
/* del */
virtual void del(const std::string& key, std::string compartment) {
std::string nkey = compartment + "::" + key;
del(nkey);
}
virtual void del(const std::string& key, std::string compartment,
std::string compartment2) {
std::string nkey = compartment + "::" + compartment2 + "::" + key;
del(nkey);
}
/* resolveFirst */
virtual std::unique_ptr<std::string> resolveFirst(const std::string& var,
std::string compartment) {
std::string nkey = compartment + "::" + var;
return resolveFirst(nkey);
}
virtual std::unique_ptr<std::string> resolveFirst(const std::string& var,
std::string compartment, std::string compartment2) {
std::string nkey = compartment + "::" + compartment2 + "::" + var;
return resolveFirst(nkey);
}
/* resolveSingleMatch */
virtual void resolveSingleMatch(const std::string& var,
std::string compartment, std::vector<const Variable *> *l) {
std::string nkey = compartment + "::" + var;
resolveSingleMatch(nkey, l);
}
virtual void resolveSingleMatch(const std::string& var,
std::string compartment, std::string compartment2,
std::vector<const Variable *> *l) {
std::string nkey = compartment + "::" + compartment2 + "::" + var;
resolveSingleMatch(nkey, l);
}
/* resolveMultiMatches */
virtual void resolveMultiMatches(const std::string& var,
std::string compartment, std::vector<const Variable *> *l) {
std::string nkey = compartment + "::" + var;
resolveMultiMatches(nkey, l);
}
virtual void resolveMultiMatches(const std::string& var,
std::string compartment, std::string compartment2,
std::vector<const Variable *> *l) {
std::string nkey = compartment + "::" + compartment2 + "::" + var;
resolveMultiMatches(nkey, l);
}
/* resolveRegularExpression */
virtual void resolveRegularExpression(const std::string& var,
std::string compartment, std::vector<const Variable *> *l) {
std::string nkey = compartment + "::" + var;
resolveRegularExpression(nkey, l);
}
virtual void resolveRegularExpression(const std::string& var,
std::string compartment, std::string compartment2,
std::vector<const Variable *> *l) {
std::string nkey = compartment + "::" + compartment2 + "::" + var;
resolveRegularExpression(nkey, l);
}
std::string m_name;
};
} // namespace collection

View File

@ -54,28 +54,48 @@ class Collections :
void storeOrUpdateFirst(const std::string& collectionName,
const std::string& variableName,
const std::string& targetValue);
void storeOrUpdateFirst(const std::string& collectionName,
const std::string& variableName,
const std::string& appid,
const std::string& targetValue);
bool storeOrUpdateFirst(const std::string &key, const std::string &value);
bool updateFirst(const std::string &key, const std::string &value);
void del(const std::string& key);
std::unique_ptr<std::string> resolveFirst(const std::string& var);
std::unique_ptr<std::string> resolveFirst(const std::string& collectionName,
const std::string& var);
std::unique_ptr<std::string> resolveFirst(const std::string& collectionName,
const std::string &appid, const std::string& var);
void resolveSingleMatch(const std::string& var,
std::vector<const Variable *> *l);
void resolveSingleMatch(const std::string& var,
const std::string& collection,
std::vector<const Variable *> *l);
void resolveSingleMatch(const std::string& var,
const std::string& collection,
const std::string& appid,
std::vector<const Variable *> *l);
void resolveMultiMatches(const std::string& var,
std::vector<const Variable *> *l);
void resolveMultiMatches(const std::string& var,
const std::string& collection,
std::vector<const Variable *> *l);
void resolveMultiMatches(const std::string& var,
const std::string& collection,
const std::string& appid,
std::vector<const Variable *> *l);
void resolveRegularExpression(const std::string& var,
std::vector<const Variable *> *l);
void resolveRegularExpression(const std::string& var,
const std::string& collection,
std::vector<const Variable *> *l);
void resolveRegularExpression(const std::string& var,
const std::string& collection,
const std::string& appid,
std::vector<const Variable *> *l);
/**
* This is a special collection to host the transaction variables.

View File

@ -340,6 +340,11 @@ class RulesProperties {
from->m_secArgumentSeparator.m_value;
}
if (from->m_secWebAppId.m_set == true) {
to->m_secWebAppId.m_value = \
from->m_secWebAppId.m_value;
}
if (from->m_unicodeMapTable.m_set == true) {
to->m_unicodeMapTable.m_unicode_map_table = \
from->m_unicodeMapTable.m_unicode_map_table;
@ -380,7 +385,6 @@ class RulesProperties {
}
}
if (to->m_auditLog) {
std::string error;
to->m_auditLog->merge(from->m_auditLog, &error);
@ -480,6 +484,7 @@ class RulesProperties {
ConfigString m_uploadDirectory;
ConfigString m_uploadTmpDirectory;
ConfigString m_secArgumentSeparator;
ConfigString m_secWebAppId;
std::vector<actions::Action *> m_defaultActions[8];
std::vector<modsecurity::Rule *> m_rules[8];
ConfigUnicodeMap m_unicodeMapTable;

View File

@ -20,6 +20,7 @@
#include <memory>
#include "modsecurity/transaction.h"
#include "modsecurity/rules.h"
#include "modsecurity/rule.h"
#include "src/macro_expansion.h"
#include "src/utils/string.h"
@ -154,7 +155,7 @@ bool SetVar::evaluate(Rule *rule, Transaction *transm_parser_payload) {
+ ":" + m_variableNameExpanded + " with value: " + targetValue);
#endif
transm_parser_payload->m_collections.storeOrUpdateFirst(m_collectionName,
m_variableNameExpanded, targetValue);
m_variableNameExpanded, transm_parser_payload->m_rules->m_secWebAppId.m_value, targetValue);
end:
return true;

View File

@ -61,6 +61,12 @@ Collections::~Collections() {
void Collections::storeOrUpdateFirst(const std::string& collectionName,
const std::string& variableName,
const std::string& targetValue) {
storeOrUpdateFirst(collectionName, variableName, "", targetValue);
}
void Collections::storeOrUpdateFirst(const std::string& collectionName,
const std::string& variableName, const std::string& appid,
const std::string& targetValue) {
if (utils::string::tolower(collectionName) == "ip"
&& !m_ip_collection_key.empty()) {
m_ip_collection->storeOrUpdateFirst(collectionName + ":"
@ -78,14 +84,14 @@ void Collections::storeOrUpdateFirst(const std::string& collectionName,
if (utils::string::tolower(collectionName) == "resource"
&& !m_resource_collection_key.empty()) {
m_resource_collection->storeOrUpdateFirst(collectionName + ":"
+ variableName, m_resource_collection_key, targetValue);
+ variableName, m_resource_collection_key, appid, targetValue);
return;
}
if (utils::string::tolower(collectionName) == "session"
&& !m_session_collection_key.empty()) {
m_session_collection->storeOrUpdateFirst(collectionName + ":"
+ variableName, m_session_collection_key, targetValue);
+ variableName, m_session_collection_key, appid, targetValue);
return;
}
@ -144,9 +150,14 @@ std::unique_ptr<std::string> Collections::resolveFirst(const std::string& var) {
return nullptr;
}
std::unique_ptr<std::string> Collections::resolveFirst(
const std::string& collectionName, const std::string& var) {
return resolveFirst(collectionName, "", var);
}
std::unique_ptr<std::string> Collections::resolveFirst(
const std::string& collectionName, const std::string& var) {
const std::string& collectionName, const std::string &appid,
const std::string& var) {
if (utils::string::tolower(collectionName) == "ip"
&& !m_ip_collection_key.empty()) {
return m_ip_collection->resolveFirst(
@ -165,14 +176,14 @@ std::unique_ptr<std::string> Collections::resolveFirst(
&& !m_resource_collection_key.empty()) {
return m_resource_collection->resolveFirst(
utils::string::toupper(collectionName)
+ ":" + var, m_resource_collection_key);
+ ":" + var, m_resource_collection_key, appid);
}
if (utils::string::tolower(collectionName) == "session"
&& !m_session_collection_key.empty()) {
return m_session_collection->resolveFirst(
utils::string::toupper(collectionName)
+ ":" + var, m_session_collection_key);
+ ":" + var, m_session_collection_key, appid);
}
for (auto &a : *this) {
@ -197,10 +208,15 @@ void Collections::resolveSingleMatch(const std::string& var,
m_transient->resolveSingleMatch(var, l);
}
void Collections::resolveSingleMatch(const std::string& var,
const std::string& collection,
std::vector<const Variable *> *l) {
resolveSingleMatch(var, collection, "", l);
}
void Collections::resolveSingleMatch(const std::string& var,
const std::string& collection, const std::string& appid,
std::vector<const Variable *> *l) {
if (utils::string::tolower(collection) == "ip"
&& !m_ip_collection_key.empty()) {
@ -218,14 +234,14 @@ void Collections::resolveSingleMatch(const std::string& var,
if (utils::string::tolower(collection) == "resource"
&& !m_resource_collection_key.empty()) {
m_resource_collection->resolveSingleMatch(var,
m_resource_collection_key, l);
m_resource_collection_key, appid, l);
return;
}
if (utils::string::tolower(collection) == "session"
&& !m_session_collection_key.empty()) {
m_session_collection->resolveSingleMatch(var,
m_session_collection_key, l);
m_session_collection_key, appid, l);
return;
}
@ -240,10 +256,15 @@ void Collections::resolveMultiMatches(const std::string& var,
m_transient->resolveMultiMatches(var, l);
}
void Collections::resolveMultiMatches(const std::string& var,
const std::string& collection,
std::vector<const Variable *> *l) {
return resolveMultiMatches(var, collection, "", l);
}
void Collections::resolveMultiMatches(const std::string& var,
const std::string& collection, const std::string &appid,
std::vector<const Variable *> *l) {
if (utils::string::tolower(collection) == "ip"
&& !m_ip_collection_key.empty()) {
m_ip_collection->resolveMultiMatches(var, m_ip_collection_key, l);
@ -260,14 +281,14 @@ void Collections::resolveMultiMatches(const std::string& var,
if (utils::string::tolower(collection) == "resource"
&& !m_resource_collection_key.empty()) {
m_resource_collection->resolveMultiMatches(var,
m_resource_collection_key, l);
m_resource_collection_key, appid, l);
return;
}
if (utils::string::tolower(collection) == "session"
&& !m_session_collection_key.empty()) {
m_session_collection->resolveMultiMatches(var,
m_session_collection_key, l);
m_session_collection_key, appid, l);
return;
}
@ -281,10 +302,15 @@ void Collections::resolveRegularExpression(const std::string& var,
m_transient->resolveRegularExpression(var, l);
}
void Collections::resolveRegularExpression(const std::string& var,
const std::string& collection,
std::vector<const Variable *> *l) {
return resolveRegularExpression(var, collection, "", l);
}
void Collections::resolveRegularExpression(const std::string& var,
const std::string& collection, const std::string &appid,
std::vector<const Variable *> *l) {
if (utils::string::tolower(collection) == "ip"
&& !m_ip_collection_key.empty()) {
m_ip_collection->resolveRegularExpression(
@ -305,7 +331,7 @@ void Collections::resolveRegularExpression(const std::string& var,
&& !m_resource_collection_key.empty()) {
m_resource_collection->resolveRegularExpression(
utils::string::toupper(collection)
+ ":" + var, m_resource_collection_key, l);
+ ":" + var, m_resource_collection_key, appid, l);
return;
}
@ -313,7 +339,7 @@ void Collections::resolveRegularExpression(const std::string& var,
&& !m_session_collection_key.empty()) {
m_session_collection->resolveRegularExpression(
utils::string::toupper(collection)
+ ":" + var, m_session_collection_key, l);
+ ":" + var, m_session_collection_key, appid, l);
return;
}

View File

@ -2045,8 +2045,8 @@ namespace yy {
case 87:
#line 1231 "seclang-parser.yy" // lalr1.cc:859
{
driver.error(yystack_[1].location, "SecWebAppId is not supported.");
YYERROR;
driver.m_secWebAppId.m_value = yystack_[0].value.as< std::string > ();
driver.m_secWebAppId.m_set = true;
}
#line 2052 "seclang-parser.cc" // lalr1.cc:859
break;

View File

@ -1229,8 +1229,8 @@ expression:
}
| CONFIG_SEC_WEB_APP_ID
{
driver.error(@0, "SecWebAppId is not supported.");
YYERROR;
driver.m_secWebAppId.m_value = $1;
driver.m_secWebAppId.m_set = true;
}
| CONFIG_SEC_SERVER_SIG
{

File diff suppressed because it is too large Load Diff

View File

@ -363,7 +363,6 @@ EQUALS_PLUS (?i:=\+)
EQUALS_MINUS (?i:=\-)
%x TRANSACTION_TO_VARIABLE
%x EXPECTING_VARIABLE
%x EXPECTING_OPERATOR
@ -589,7 +588,7 @@ EQUALS_MINUS (?i:=\-)
{CONFIG_COMPONENT_SIG}[ \t]+["]{FREE_TEXT}["] { return p::make_CONFIG_COMPONENT_SIG(strchr(yytext, ' ') + 2, *driver.loc.back()); }
{CONFIG_SEC_SERVER_SIG}[ \t]+["]{FREE_TEXT}["] { return p::make_CONFIG_SEC_SERVER_SIG(strchr(yytext, ' ') + 2, *driver.loc.back()); }
{CONFIG_SEC_WEB_APP_ID}[ \t]+["]{FREE_TEXT}["] { return p::make_CONFIG_SEC_WEB_APP_ID(parserSanitizer(strchr(yytext, ' ') + 2), *driver.loc.back()); }
{CONFIG_SEC_WEB_APP_ID}[ \t]+{FREE_TEXT} { return p::make_CONFIG_SEC_WEB_APP_ID(parserSanitizer(strchr(yytext, ' ') + 2), *driver.loc.back()); }
{CONFIG_SEC_WEB_APP_ID}[ \t]+{FREE_TEXT_NEW_LINE} { return p::make_CONFIG_SEC_WEB_APP_ID(parserSanitizer(strchr(yytext, ' ') + 1), *driver.loc.back()); }
{CONFIG_CONTENT_INJECTION} { return p::make_CONFIG_CONTENT_INJECTION(*driver.loc.back()); }
{CONFIG_DIR_AUDIT_DIR_MOD}[ \t]+{CONFIG_VALUE_NUMBER} { return p::make_CONFIG_DIR_AUDIT_DIR_MOD(parserSanitizer(strchr(yytext, ' ') + 1), *driver.loc.back()); }
{CONFIG_DIR_AUDIT_DIR_MOD}[ \t]+["]{CONFIG_VALUE_NUMBER}["] { return p::make_CONFIG_DIR_AUDIT_DIR_MOD(parserSanitizer(strchr(yytext, ' ') + 1), *driver.loc.back()); }

View File

@ -446,7 +446,7 @@ std::vector<std::unique_ptr<collection::Variable>> Rule::getFinalVars(
}
if (a.second->m_isExclusion) {
std::vector<const collection::Variable *> z;
a.second->evaluateInternal(trans, this, &z);
a.second->evaluate(trans, this, &z);
for (auto &y : z) {
exclusions_update_by_tag_remove.push_back(std::string(y->m_key));
delete y;
@ -465,7 +465,7 @@ std::vector<std::unique_ptr<collection::Variable>> Rule::getFinalVars(
}
if (a.second->m_isExclusion) {
std::vector<const collection::Variable *> z;
a.second->evaluateInternal(trans, this, &z);
a.second->evaluate(trans, this, &z);
for (auto &y : z) {
exclusions_update_by_msg_remove.push_back(std::string(y->m_key));
delete y;
@ -484,7 +484,7 @@ std::vector<std::unique_ptr<collection::Variable>> Rule::getFinalVars(
}
if (a.second->m_isExclusion) {
std::vector<const collection::Variable *> z;
a.second->evaluateInternal(trans, this, &z);
a.second->evaluate(trans, this, &z);
for (auto &y : z) {
exclusions_update_by_id_remove.push_back(std::string(y->m_key));
delete y;
@ -500,7 +500,7 @@ std::vector<std::unique_ptr<collection::Variable>> Rule::getFinalVars(
Variable *variable = variables.at(i);
if (variable->m_isExclusion) {
std::vector<const collection::Variable *> z;
variable->evaluateInternal(trans, this, &z);
variable->evaluate(trans, this, &z);
for (auto &y : z) {
exclusions.push_back(std::string(y->m_key));
delete y;
@ -518,7 +518,7 @@ std::vector<std::unique_ptr<collection::Variable>> Rule::getFinalVars(
continue;
}
variable->evaluateInternal(trans, this, &e);
variable->evaluate(trans, this, &e);
for (const collection::Variable *v : e) {
std::string key = v->m_key;

View File

@ -39,7 +39,8 @@ class Resource_DictElement : public Variable {
void evaluate(Transaction *transaction,
Rule *rule,
std::vector<const collection::Variable *> *l) override {
transaction->m_collections.resolveMultiMatches(m_dictElement, "RESOURCE", l);
transaction->m_collections.resolveMultiMatches(m_dictElement,
"RESOURCE", transaction->m_rules->m_secWebAppId.m_value, l);
}
std::string m_dictElement;
@ -54,7 +55,8 @@ class Resource_NoDictElement : public Variable {
void evaluate(Transaction *transaction,
Rule *rule,
std::vector<const collection::Variable *> *l) override {
transaction->m_collections.resolveMultiMatches(m_name, "RESOURCE", l);
transaction->m_collections.resolveMultiMatches(m_name, "RESOURCE",
transaction->m_rules->m_secWebAppId.m_value, l);
}
};
@ -70,7 +72,7 @@ class Resource_DictElementRegexp : public Variable {
Rule *rule,
std::vector<const collection::Variable *> *l) override {
transaction->m_collections.resolveRegularExpression(m_dictElement,
"RESOURCE", l);
"RESOURCE", transaction->m_rules->m_secWebAppId.m_value, l);
}
Utils::Regex m_r;

View File

@ -40,7 +40,7 @@ class Session_DictElement : public Variable {
Rule *rule,
std::vector<const collection::Variable *> *l) override {
transaction->m_collections.resolveMultiMatches(m_dictElement,
"SESSION", l);
"SESSION", transaction->m_rules->m_secWebAppId.m_value, l);
}
std::string m_dictElement;
@ -55,7 +55,8 @@ class Session_NoDictElement : public Variable {
void evaluate(Transaction *transaction,
Rule *rule,
std::vector<const collection::Variable *> *l) override {
transaction->m_collections.resolveMultiMatches(m_name, "SESSION", l);
transaction->m_collections.resolveMultiMatches(m_name, "SESSION",
transaction->m_rules->m_secWebAppId.m_value, l);
}
};
@ -71,7 +72,7 @@ class Session_DictElementRegexp : public Variable {
Rule *rule,
std::vector<const collection::Variable *> *l) override {
transaction->m_collections.resolveRegularExpression(m_dictElement,
"SESSION", l);
"SESSION", transaction->m_rules->m_secWebAppId.m_value, l);
}
Utils::Regex m_r;

View File

@ -120,50 +120,6 @@ Variable::Variable(std::string name, VariableKind kind)
}
std::vector<const collection::Variable *> *
Variable::evaluate(Transaction *transaction) {
std::vector<const collection::Variable *> *l;
l = new std::vector<const collection::Variable *>();
evaluate(transaction, NULL, l);
return l;
}
void Variable::evaluate(Transaction *transaction,
Rule *rule,
std::vector<const collection::Variable *> *l) {
if (m_collectionName.empty() == false) {
if (m_kind == CollectionVarible && m_type == MultipleMatches) {
transaction->m_collections.resolveMultiMatches(m_name,
m_collectionName, l);
} else if (m_type == RegularExpression) {
transaction->m_collections.resolveRegularExpression(m_name,
m_collectionName, l);
} else {
transaction->m_collections.resolveSingleMatch(m_name,
m_collectionName, l);
}
} else {
if (m_kind == CollectionVarible && m_type == MultipleMatches) {
transaction->m_collections.resolveMultiMatches(m_name, l);
} else if (m_type == RegularExpression) {
transaction->m_collections.resolveRegularExpression(m_name, l);
} else {
transaction->m_collections.resolveSingleMatch(m_name, l);
}
}
}
void Variable::evaluateInternal(Transaction *transaction,
Rule *rule,
std::vector<const collection::Variable *> *l) {
evaluate(transaction, rule, l);
}
std::string Variable::to_s(
std::vector<Variable *> *variables) {
std::string ret;

View File

@ -70,19 +70,9 @@ class Variable {
Variable(std::string name, VariableKind kind);
virtual ~Variable() { }
virtual std::vector<const collection::Variable *>
*evaluate(Transaction *transaction);
virtual void evaluate(Transaction *transaction,
Rule *rule,
std::vector<const collection::Variable *> *l);
virtual void evaluateInternal(Transaction *transaction,
Rule *rule,
std::vector<const collection::Variable *> *l);
std::vector<const collection::Variable *> *l) = 0;
static std::string to_s(std::vector<Variable *> *variables);

View File

@ -40,7 +40,7 @@
},
"expected":{
"audit_log":"",
"debug_log":"Target value: \"123\" \\(Variable: whee::RESOURCE:test\\)",
"debug_log":"Target value: \"123\" \\(Variable: whee::::RESOURCE:test\\)",
"error_log":""
},
"rules":[
@ -50,5 +50,58 @@
"SecRule ARGS:resource \"@unconditionalmatch \" \"phase:2,pass,expirevar:resource.timeout=3600,id:9000033\"",
"SecRule RESOURCE:test \"@unconditionalmatch \" \"phase:2,pass,expirevar:resource.timeout=3600,id:9000034\""
]
},
{
"enabled":1,
"version_min":300000,
"version_max":0,
"title":"Testing collection :: RESOURCE (2/2)",
"client":{
"ip":"200.249.12.31",
"port":2313
},
"server":{
"ip":"200.249.12.31",
"port":80
},
"request":{
"headers":{
"User-Agent":"Mozilla\/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko\/20091102 Firefox\/3.5.5 (.NET CLR 3.5.30729)",
"Accept":"text\/html,application\/xhtml+xml,application\/xml;q=0.9,*\/*;q=0.8",
"Accept-Language":"en-us,en;q=0.5",
"Accept-Encoding":"gzip,deflate",
"Accept-Charset":"ISO-8859-1,utf-8;q=0.7,*;q=0.7",
"Keep-Alive":"300",
"Connection":"keep-alive",
"Cookie":"PHPSESSID=rAAAAAAA2t5uvjq435r4q7ib3vtdjq120",
"Pragma":"no-cache",
"Cache-Control":"no-cache"
},
"uri":"\/test.pl?resource=whee",
"method":"GET",
"http_version":1.1,
"body":""
},
"response":{
"headers":{
"Content-Type":"text\/xml; charset=utf-8\n\r",
"Content-Length":"length\n\r"
},
"body":[
]
},
"expected":{
"audit_log":"",
"debug_log":"whee::webappid::RESOURCE:test",
"error_log":""
},
"rules":[
"SecRuleEngine On",
"SecWebAppId webappid",
"SecRule ARGS:resource \"@unconditionalmatch \" \"phase:2,pass,initcol:resource=%{ARGS.resource},id:900003\"",
"SecRule ARGS:resource \"@unconditionalmatch \" \"phase:2,pass,setvar:resource.test=123,id:900000\"",
"SecRule ARGS:resource \"@unconditionalmatch \" \"phase:2,pass,expirevar:resource.timeout=3600,id:9000033\"",
"SecRule RESOURCE:test \"@unconditionalmatch \" \"phase:2,pass,expirevar:resource.timeout=3600,id:9000034\""
]
}
]