diff --git a/headers/modsecurity/rule.h b/headers/modsecurity/rule.h index 64e650ad..b585e099 100644 --- a/headers/modsecurity/rule.h +++ b/headers/modsecurity/rule.h @@ -52,6 +52,7 @@ class Rule { std::vector actions_runtime_pos; std::vector getActionNames(); + std::vector getActionsByName(const std::string& name); std::vector *variables; int phase; diff --git a/src/actions/xmlns.cc b/src/actions/xmlns.cc index b05b966d..efc07826 100644 --- a/src/actions/xmlns.cc +++ b/src/actions/xmlns.cc @@ -35,23 +35,23 @@ bool XmlNS::init(std::string *error) { error->assign("XMLS: Bad format, missing equals sign."); return false; } - m_name = std::string(m_parser_payload, 0, pos); - m_value = std::string(m_parser_payload, pos+1, m_parser_payload.size()); + m_scope = std::string(m_parser_payload, 0, pos); + m_href = std::string(m_parser_payload, pos+1, m_parser_payload.size()); - if (m_value.empty() || m_name.empty()) { + if (m_href.empty() || m_scope.empty()) { error->assign("XMLS: XMLNS is invalid. Expecting a " \ "name=value format."); return false; } - if (m_value.at(0) == '\'' && m_value.size() > 3) { - m_value.erase(0, 1); - m_value.pop_back(); + if (m_href.at(0) == '\'' && m_href.size() > 3) { + m_href.erase(0, 1); + m_href.pop_back(); } - if (m_value.compare(0, http.length(), http) != 0) { + if (m_href.compare(0, http.length(), http) != 0) { error->assign("XMLS: Missing xmlns href for prefix: " \ - "`" + m_value + "'."); + "`" + m_href + "'."); return false; } diff --git a/src/actions/xmlns.h b/src/actions/xmlns.h index f3ef3390..d9db4560 100644 --- a/src/actions/xmlns.h +++ b/src/actions/xmlns.h @@ -37,9 +37,8 @@ class XmlNS : public Action { bool init(std::string *error); - private: - std::string m_name; - std::string m_value; + std::string m_scope; + std::string m_href; }; diff --git a/src/rule.cc b/src/rule.cc index 15b54183..c8a7f61d 100644 --- a/src/rule.cc +++ b/src/rule.cc @@ -527,4 +527,25 @@ bool Rule::evaluate(Transaction *trasn) { return ret; } + +std::vector Rule::getActionsByName(const std::string& name) { + std::vector ret; + for (auto &z : this->actions_runtime_pos) { + if (z->m_name == name) { + ret.push_back(z); + } + } + for (auto &z : this->actions_runtime_pre) { + if (z->m_name == name) { + ret.push_back(z); + } + } + for (auto &z : this->actions_conf) { + if (z->m_name == name) { + ret.push_back(z); + } + } + return ret; +} + } // namespace modsecurity diff --git a/src/variables/xml.cc b/src/variables/xml.cc index 63518134..d2b7fb9c 100644 --- a/src/variables/xml.cc +++ b/src/variables/xml.cc @@ -23,6 +23,9 @@ #include #include #include +#include +#include +#include #include #include @@ -33,11 +36,14 @@ #include "modsecurity/transaction.h" #include "src/request_body_processor/xml.h" +#include "src/actions/action.h" +#include "src/actions/xmlns.h" namespace modsecurity { namespace Variables { void XML::evaluateInternal(Transaction *t, + Rule *rule, std::vector *l) { xmlXPathContextPtr xpathCtx; xmlXPathObjectPtr xpathObj; @@ -60,8 +66,10 @@ void XML::evaluateInternal(Transaction *t, /* Is there an XML document tree at all? */ if (t->m_xml->m_data.doc == NULL) { /* Sorry, we've got nothing to give! */ + t->debug(1, "XML: No XML document found, returning."); return; } + if (param.empty() == true) { /* Invocation without an XPath expression makes sense * with functions that manipulate the document tree. @@ -70,6 +78,7 @@ void XML::evaluateInternal(Transaction *t, std::string("[XML document tree]" + param))); return; } + /* Process the XPath expression. */ xpathExpr = (const xmlChar*)param.c_str(); xpathCtx = xmlXPathNewContext(t->m_xml->m_data.doc); @@ -77,32 +86,24 @@ void XML::evaluateInternal(Transaction *t, t->debug(1, "XML: Unable to create new XPath context."); return; } -#if 0 - /* Look through the actionset of the associated rule - * for the namespace information. Register them if any are found. - */ - tarr = apr_table_elts(rule->actionset->actions); - telts = (const apr_table_entry_t*)tarr->elts; - for (i = 0; i < tarr->nelts; i++) { - msre_action *action = (msre_action *)telts[i].val; - if (strcasecmp(action->metadata->name, "xmlns") == 0) { - char *prefix, *href; - - if (parse_name_eq_value(mptmp, action->param, &prefix, &href) < 0) return -1; - if ((prefix == NULL)||(href == NULL)) return -1; - - if(xmlXPathRegisterNs(xpathCtx, (const xmlChar*)prefix, (const xmlChar*)href) != 0) { - msr_log(msr, 1, "Failed to register XML namespace href \"%s\" prefix \"%s\".", - log_escape(mptmp, prefix), log_escape(mptmp, href)); - return -1; + if (rule == NULL) { + t->debug(2, "XML: Can't look for xmlns, internal error."); + } else { + std::vector acts = rule->getActionsByName("xmlns"); + for (auto &x : acts) { + actions::XmlNS *z = (actions::XmlNS *)x; + if (xmlXPathRegisterNs(xpathCtx, (const xmlChar*)z->m_scope.c_str(), + (const xmlChar*)z->m_href.c_str()) != 0) { + t->debug(1, "Failed to register XML namespace href \"" + \ + z->m_href + "\" prefix \"" + z->m_scope + "\"."); + return; } - msr_log(msr, 4, "Registered XML namespace href \"%s\" prefix \"%s\".", - log_escape(mptmp, prefix), log_escape(mptmp, href)); + t->debug(4, "Registered XML namespace href \"" + z->m_href + \ + "\" prefix \"" + z->m_scope + "\""); } } -#endif /* Initialise XPath expression. */ xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx); diff --git a/src/variables/xml.h b/src/variables/xml.h index f7a75220..865477d1 100644 --- a/src/variables/xml.h +++ b/src/variables/xml.h @@ -36,6 +36,7 @@ class XML : public Variable { : Variable(_name) { } void evaluateInternal(Transaction *transaction, + Rule *rule, std::vector *l) override; }; diff --git a/test/test-cases/regression/action-xmlns.json b/test/test-cases/regression/action-xmlns.json index 9212e426..fa9db840 100644 --- a/test/test-cases/regression/action-xmlns.json +++ b/test/test-cases/regression/action-xmlns.json @@ -34,5 +34,74 @@ "SecRule REQUEST_HEADERS:Content-Type \"^text/xml$\" \"id:500008,phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML\"", "SecRule REQUEST_HEADERS:User-Agent \"^(.*)$\" \"id:123,xmlns:soap='schemas.xmlsoap.org/soap/envelope/'\"" ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing XML request body parser (validate ok)", + "expected":{ + "debug_log": "Target value: \"39.95\" \(Variable: XML:\/bookstore\/book\/price\[text\(\)\]\)" + }, + "client":{ + "ip":"200.249.12.31", + "port":123 + }, + "request":{ + "headers":{ + "Host":"localhost", + "User-Agent":"curl/7.38.0", + "Accept":"*/*", + "Cookie": "PHPSESSID=rAAAAAAA2t5uvjq435r4q7ib3vtdjq120", + "Content-Type": "text/xml" + }, + "uri":"/?key=value&key=other_value", + "method":"POST", + "body": [ + "", + "", + "", + "Everyday Italian", + "Giada De Laurentiis", + "2005", + "30.00", + "", + + "", + "Harry Potter", + "J K. Rowling", + "2005", + "29.99", + "", + + "", + "XQuery Kick Start", + "James McGovern", + "Per Bothner", + "Kurt Cagle", + "James Linn", + "Vaidyanathan Nagarajan", + "2003", + "49.99", + "", + + "", + "Learning XML", + "Erik T. Ray", + "2003", + "39.95", + "", + "" + ] + }, + "server":{ + "ip":"200.249.12.31", + "port":80 + }, + "rules":[ + "SecRuleEngine On", + "SecRequestBodyAccess On", + "SecRule REQUEST_HEADERS:Content-Type \"^text/xml$\" \"id:500005,phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML\"", + "SecRule XML:/bookstore/book/price[text()] \"Fred\" \"phase:3,id:123,xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'\"" + ] } ] \ No newline at end of file