Skip to content

Commit d5fd3db

Browse files
committed
fix test
1 parent fd48a83 commit d5fd3db

4 files changed

Lines changed: 221 additions & 1 deletion

File tree

src/request_body_processor/json_backend_jsoncons.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ JsonParseResult fromJsonconsError(const std::error_code &error,
8585
return makeResult(JsonParseStatus::TruncatedInput,
8686
JsonSinkStatus::Continue, detail);
8787
case jsoncons::json_errc::max_nesting_depth_exceeded:
88+
return makeResult(JsonParseStatus::ParseError,
89+
JsonSinkStatus::Continue, detail);
8890
case jsoncons::json_errc::source_error:
8991
return makeResult(JsonParseStatus::InternalError,
9092
JsonSinkStatus::Continue, detail);

test/Makefile.am

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ unit_tests_SOURCES = \
3535
unit/unit_test.cc \
3636
common/custom_debug_log.cc
3737

38+
noinst_PROGRAMS += json_backend_depth_tests
39+
json_backend_depth_tests_SOURCES = \
40+
unit/json_backend_depth_tests.cc
41+
3842

3943
noinst_HEADERS = \
4044
$(srcdir)/common/colors.h \
@@ -91,6 +95,15 @@ unit_tests_CPPFLAGS = \
9195
$(SSDEEP_CFLAGS) \
9296
$(LIBXML2_CFLAGS)
9397

98+
json_backend_depth_tests_LDADD = \
99+
$(unit_tests_LDADD)
100+
101+
json_backend_depth_tests_LDFLAGS = \
102+
$(unit_tests_LDFLAGS)
103+
104+
json_backend_depth_tests_CPPFLAGS = \
105+
$(unit_tests_CPPFLAGS)
106+
94107

95108
# regression
96109

test/run-json-backend-matrix.sh

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ run_backend() {
124124

125125
(
126126
cd "${build_dir}" && \
127-
make -j "${jobs}" -C test regression_tests
127+
make -j "${jobs}" -C test regression_tests json_backend_depth_tests
128128
) >> "${raw_log}" 2>&1
129129
build_status=$?
130130
if [ "${build_status}" -ne 0 ]; then
@@ -141,6 +141,14 @@ run_backend() {
141141
fi
142142
done
143143

144+
(
145+
cd "${build_dir}/test" && \
146+
./json_backend_depth_tests
147+
) >> "${raw_log}" 2>&1
148+
if [ "$?" -ne 0 ]; then
149+
test_status=1
150+
fi
151+
144152
extract_summary "${backend}" "${raw_log}" "${summary_file}"
145153
if [ ! -s "${summary_file}" ]; then
146154
test_status=1
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
/*
2+
* ModSecurity, http://www.modsecurity.org/
3+
* Copyright (c) 2015 - 2024 Trustwave Holdings, Inc. (http://www.trustwave.com/)
4+
*
5+
* You may not use this file except in compliance with
6+
* the License. You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* If any of the files related to licensing are missing or if you have any
11+
* other questions related to licensing please contact Trustwave Holdings, Inc.
12+
* directly using the email address security@modsecurity.org.
13+
*
14+
*/
15+
16+
#include <cstddef>
17+
#include <iostream>
18+
#include <string>
19+
20+
#include "src/request_body_processor/json_adapter.h"
21+
22+
namespace modsecurity {
23+
namespace RequestBodyProcessor {
24+
namespace {
25+
26+
class AcceptAllSink : public JsonEventSink {
27+
public:
28+
JsonSinkStatus on_start_object() override {
29+
return JsonSinkStatus::Continue;
30+
}
31+
32+
JsonSinkStatus on_end_object() override {
33+
return JsonSinkStatus::Continue;
34+
}
35+
36+
JsonSinkStatus on_start_array() override {
37+
return JsonSinkStatus::Continue;
38+
}
39+
40+
JsonSinkStatus on_end_array() override {
41+
return JsonSinkStatus::Continue;
42+
}
43+
44+
JsonSinkStatus on_key(std::string_view value) override {
45+
(void) value;
46+
return JsonSinkStatus::Continue;
47+
}
48+
49+
JsonSinkStatus on_string(std::string_view value) override {
50+
(void) value;
51+
return JsonSinkStatus::Continue;
52+
}
53+
54+
JsonSinkStatus on_number(std::string_view raw_number) override {
55+
(void) raw_number;
56+
return JsonSinkStatus::Continue;
57+
}
58+
59+
JsonSinkStatus on_boolean(bool value) override {
60+
(void) value;
61+
return JsonSinkStatus::Continue;
62+
}
63+
64+
JsonSinkStatus on_null() override {
65+
return JsonSinkStatus::Continue;
66+
}
67+
};
68+
69+
const char *parseStatusName(JsonParseStatus status) {
70+
switch (status) {
71+
case JsonParseStatus::Ok:
72+
return "Ok";
73+
case JsonParseStatus::ParseError:
74+
return "ParseError";
75+
case JsonParseStatus::TruncatedInput:
76+
return "TruncatedInput";
77+
case JsonParseStatus::Utf8Error:
78+
return "Utf8Error";
79+
case JsonParseStatus::EngineAbort:
80+
return "EngineAbort";
81+
case JsonParseStatus::InternalError:
82+
return "InternalError";
83+
}
84+
85+
return "UnknownParseStatus";
86+
}
87+
88+
const char *sinkStatusName(JsonSinkStatus status) {
89+
switch (status) {
90+
case JsonSinkStatus::Continue:
91+
return "Continue";
92+
case JsonSinkStatus::EngineAbort:
93+
return "EngineAbort";
94+
case JsonSinkStatus::DepthLimitExceeded:
95+
return "DepthLimitExceeded";
96+
case JsonSinkStatus::InternalError:
97+
return "InternalError";
98+
}
99+
100+
return "UnknownSinkStatus";
101+
}
102+
103+
std::string makeNestedArrayJson(std::size_t depth) {
104+
std::string input(depth, '[');
105+
input.push_back('0');
106+
input.append(depth, ']');
107+
return input;
108+
}
109+
110+
std::string describeUnexpectedResult(const JsonParseResult &result,
111+
const char *expectation) {
112+
std::string detail = std::string("Expected ") + expectation + ", got "
113+
+ parseStatusName(result.parse_status) + "/"
114+
+ sinkStatusName(result.sink_status) + ".";
115+
if (!result.detail.empty()) {
116+
detail.append(" ");
117+
detail.append(result.detail);
118+
}
119+
return detail;
120+
}
121+
122+
bool expectBackendDepthLimitParseError(std::string *failure_detail) {
123+
AcceptAllSink sink;
124+
JSONAdapter adapter;
125+
JsonBackendParseOptions options;
126+
127+
options.technical_max_depth = 2;
128+
JsonParseResult result = adapter.parse(makeNestedArrayJson(8), &sink,
129+
options);
130+
131+
if (result.parse_status != JsonParseStatus::ParseError
132+
|| result.sink_status != JsonSinkStatus::Continue) {
133+
if (failure_detail != nullptr) {
134+
*failure_detail = describeUnexpectedResult(result,
135+
"ParseError/Continue");
136+
}
137+
return false;
138+
}
139+
140+
return true;
141+
}
142+
143+
bool expectBackendDepthHeadroomSuccess(std::string *failure_detail) {
144+
AcceptAllSink sink;
145+
JSONAdapter adapter;
146+
JsonBackendParseOptions options;
147+
148+
options.technical_max_depth = 32;
149+
JsonParseResult result = adapter.parse(makeNestedArrayJson(8), &sink,
150+
options);
151+
152+
if (!result.ok()) {
153+
if (failure_detail != nullptr) {
154+
*failure_detail = describeUnexpectedResult(result, "Ok/Continue");
155+
}
156+
return false;
157+
}
158+
159+
return true;
160+
}
161+
162+
bool reportTestResult(const char *name, bool passed,
163+
const std::string &detail) {
164+
std::cout << ":test-result: " << (passed ? "PASS " : "FAIL ")
165+
<< "json_backend_depth_tests:" << name << std::endl;
166+
if (!passed && !detail.empty()) {
167+
std::cerr << name << ": " << detail << std::endl;
168+
}
169+
return passed;
170+
}
171+
172+
} // namespace
173+
174+
int runJsonBackendDepthTests() {
175+
int failures = 0;
176+
std::string detail;
177+
178+
if (!reportTestResult("technical_depth_limit_returns_parse_error",
179+
expectBackendDepthLimitParseError(&detail), detail)) {
180+
failures++;
181+
}
182+
183+
detail.clear();
184+
if (!reportTestResult("technical_depth_with_headroom_succeeds",
185+
expectBackendDepthHeadroomSuccess(&detail), detail)) {
186+
failures++;
187+
}
188+
189+
return failures == 0 ? 0 : 1;
190+
}
191+
192+
} // namespace RequestBodyProcessor
193+
} // namespace modsecurity
194+
195+
int main() {
196+
return modsecurity::RequestBodyProcessor::runJsonBackendDepthTests();
197+
}

0 commit comments

Comments
 (0)