Support configurable limit on depth of JSON parsing

This commit is contained in:
Martin Vierula
2021-11-15 18:51:25 -08:00
parent ec86b242e1
commit ac79c1c29b
11 changed files with 5749 additions and 5554 deletions

View File

@@ -26,9 +26,15 @@
namespace modsecurity {
namespace RequestBodyProcessor {
static const double json_depth_limit_default = 10000.0;
static const char* json_depth_limit_exceeded_msg = ". Parsing depth limit exceeded";
JSON::JSON(Transaction *transaction) : m_transaction(transaction),
m_handle(NULL),
m_current_key("") {
m_current_key(""),
m_max_depth(json_depth_limit_default),
m_current_depth(0),
m_depth_limit_exceeded(false) {
/**
* yajl callback functions
* For more information on the function signatures and order, check
@@ -91,6 +97,9 @@ bool JSON::processChunk(const char *buf, unsigned int size, std::string *err) {
(const unsigned char *)buf, size);
/* We need to free the yajl error message later, how to do this? */
err->assign((const char *)e);
if (m_depth_limit_exceeded) {
err->append(json_depth_limit_exceeded_msg);
}
yajl_free_error(m_handle, e);
return false;
}
@@ -106,6 +115,9 @@ bool JSON::complete(std::string *err) {
unsigned char *e = yajl_get_error(m_handle, 0, NULL, 0);
/* We need to free the yajl error message later, how to do this? */
err->assign((const char *)e);
if (m_depth_limit_exceeded) {
err->append(json_depth_limit_exceeded_msg);
}
yajl_free_error(m_handle, e);
return false;
}
@@ -226,6 +238,11 @@ int JSON::yajl_start_array(void *ctx) {
std::string name = tthis->getCurrentKey();
tthis->m_containers.push_back(
reinterpret_cast<JSONContainer *>(new JSONContainerArray(name)));
tthis->m_current_depth++;
if (tthis->m_current_depth > tthis->m_max_depth) {
tthis->m_depth_limit_exceeded = true;
return 0;
}
return 1;
}
@@ -233,6 +250,7 @@ int JSON::yajl_start_array(void *ctx) {
int JSON::yajl_end_array(void *ctx) {
JSON *tthis = reinterpret_cast<JSON *>(ctx);
if (tthis->m_containers.empty()) {
tthis->m_current_depth--;
return 1;
}
@@ -246,6 +264,7 @@ int JSON::yajl_end_array(void *ctx) {
ja->m_elementCounter++;
}
}
tthis->m_current_depth--;
return 1;
}
@@ -256,6 +275,11 @@ int JSON::yajl_start_map(void *ctx) {
std::string name(tthis->getCurrentKey());
tthis->m_containers.push_back(
reinterpret_cast<JSONContainer *>(new JSONContainerMap(name)));
tthis->m_current_depth++;
if (tthis->m_current_depth > tthis->m_max_depth) {
tthis->m_depth_limit_exceeded = true;
return 0;
}
return 1;
}
@@ -267,6 +291,7 @@ int JSON::yajl_start_map(void *ctx) {
int JSON::yajl_end_map(void *ctx) {
JSON *tthis = reinterpret_cast<JSON *>(ctx);
if (tthis->m_containers.empty()) {
tthis->m_current_depth--;
return 1;
}
@@ -282,6 +307,7 @@ int JSON::yajl_end_map(void *ctx) {
}
}
tthis->m_current_depth--;
return 1;
}

View File

@@ -29,7 +29,6 @@
#include "modsecurity/rules_set.h"
namespace modsecurity {
namespace RequestBodyProcessor {
@@ -104,12 +103,19 @@ class JSON {
return ret;
}
void setMaxDepth(double max_depth) {
m_max_depth = max_depth;
}
private:
std::deque<JSONContainer *> m_containers;
Transaction *m_transaction;
yajl_handle m_handle;
yajl_status m_status;
std::string m_current_key;
double m_max_depth;
int64_t m_current_depth;
bool m_depth_limit_exceeded;
};