Skip to content

Commit fb27d43

Browse files
IlyasShabisimon-id
andauthored
report max truncated elements and errors (#130)
* report max truncated elements and errors * fix linter * Use error code instead * Typescript definition * update test to check truncated nested objects are not checked * add tests --------- Co-authored-by: simon-id <simon.id@datadoghq.com>
1 parent 7bf9fc4 commit fb27d43

File tree

7 files changed

+227
-38
lines changed

7 files changed

+227
-38
lines changed

index.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,21 @@ type diagnosticsError = {
2222

2323
type diagnosticsResult = diagnosticsInfo | diagnosticsError
2424

25+
type TruncationMetrics = {
26+
maxTruncatedString?: number;
27+
maxTruncatedContainerSize?: number;
28+
maxTruncatedContainerDepth?: number;
29+
}
30+
2531
type result = {
2632
timeout: boolean;
2733
totalRuntime?: number;
2834
events?: object[]; // https://github.com/DataDog/libddwaf/blob/master/schema/events.json
2935
status?: 'match'; // TODO: remove this if new statuses are never added
3036
actions?: object[];
3137
derivatives?: object;
38+
metrics?: TruncationMetrics;
39+
errorCode?: number;
3240
}
3341

3442
type payload = {

src/convert.cpp

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <limits>
1111
#include <string>
12+
#include <algorithm>
1213

1314
#include "src/convert.h"
1415
#include "src/log.h"
@@ -21,7 +22,8 @@ ddwaf_object* to_ddwaf_object(
2122
int depth,
2223
bool lim,
2324
bool ignoreToJSON,
24-
JsSet stack
25+
JsSet stack,
26+
WAFTruncationMetrics* metrics
2527
);
2628

2729
ddwaf_object* to_ddwaf_object_array(
@@ -31,12 +33,13 @@ ddwaf_object* to_ddwaf_object_array(
3133
int depth,
3234
bool lim,
3335
bool ignoreToJSON,
34-
JsSet stack
36+
JsSet stack,
37+
WAFTruncationMetrics* metrics
3538
) {
3639
if (!ignoreToJSON) {
3740
Napi::Value toJSON = arr.Get("toJSON");
3841
if (toJSON.IsFunction()) {
39-
return to_ddwaf_object(object, env, toJSON.As<Napi::Function>().Call(arr, {}), depth, lim, true, stack);
42+
return to_ddwaf_object(object, env, toJSON.As<Napi::Function>().Call(arr, {}), depth, lim, true, stack, metrics);
4043
}
4144
}
4245

@@ -53,12 +56,16 @@ ddwaf_object* to_ddwaf_object_array(
5356
// TODO(@vdeturckheim): handle arrays with
5457
// more than DDWAF_MAX_CONTAINER_SIZE chars
5558
if (lim && len > DDWAF_MAX_CONTAINER_SIZE) {
59+
if (metrics) {
60+
metrics->max_truncated_container_size = std::max(metrics->max_truncated_container_size,
61+
static_cast<size_t>(len));
62+
}
5663
len = DDWAF_MAX_CONTAINER_SIZE;
5764
}
5865
for (uint32_t i = 0; i < len; ++i) {
5966
Napi::Value item = arr.Get(i);
6067
ddwaf_object val;
61-
to_ddwaf_object(&val, env, item, depth, lim, false, stack);
68+
to_ddwaf_object(&val, env, item, depth, lim, false, stack, metrics);
6269
if (!ddwaf_object_array_add(object, &val)) {
6370
mlog("add to array failed, freeing");
6471
ddwaf_object_free(&val);
@@ -75,18 +82,23 @@ ddwaf_object* to_ddwaf_object_object(
7582
int depth,
7683
bool lim,
7784
bool ignoreToJSON,
78-
JsSet stack
85+
JsSet stack,
86+
WAFTruncationMetrics* metrics
7987
) {
8088
if (!ignoreToJSON) {
8189
Napi::Value toJSON = obj.Get("toJSON");
8290
if (toJSON.IsFunction()) {
83-
return to_ddwaf_object(object, env, toJSON.As<Napi::Function>().Call(obj, {}), depth, lim, true, stack);
91+
return to_ddwaf_object(object, env, toJSON.As<Napi::Function>().Call(obj, {}), depth, lim, true, stack, metrics);
8492
}
8593
}
8694

8795
Napi::Array properties = obj.GetPropertyNames();
8896
uint32_t len = properties.Length();
8997
if (lim && len > DDWAF_MAX_CONTAINER_SIZE) {
98+
if (metrics) {
99+
metrics->max_truncated_container_size = std::max(metrics->max_truncated_container_size,
100+
static_cast<size_t>(len));
101+
}
90102
len = DDWAF_MAX_CONTAINER_SIZE;
91103
}
92104
if (env.IsExceptionPending()) {
@@ -112,7 +124,7 @@ ddwaf_object* to_ddwaf_object_object(
112124
Napi::Value valV = obj.Get(keyV);
113125
mlog("Looping into ToPWArgs");
114126
ddwaf_object val;
115-
to_ddwaf_object(&val, env, valV, depth, lim, false, stack);
127+
to_ddwaf_object(&val, env, valV, depth, lim, false, stack, metrics);
116128
if (!ddwaf_object_map_add(map, key.c_str(), &val)) {
117129
mlog("add to object failed, freeing");
118130
ddwaf_object_free(&val);
@@ -122,10 +134,19 @@ ddwaf_object* to_ddwaf_object_object(
122134
return object;
123135
}
124136

125-
ddwaf_object* to_ddwaf_string(ddwaf_object *object, Napi::Value val, bool lim) {
137+
ddwaf_object* to_ddwaf_string(
138+
ddwaf_object *object,
139+
Napi::Value val,
140+
bool lim,
141+
WAFTruncationMetrics* metrics
142+
) {
126143
std::string str = val.ToString().Utf8Value();
127144
int len = str.length();
128145
if (lim && len > DDWAF_MAX_STRING_LENGTH) {
146+
if (metrics) {
147+
metrics->max_truncated_string_length = std::max(metrics->max_truncated_string_length,
148+
static_cast<size_t>(len));
149+
}
129150
len = DDWAF_MAX_STRING_LENGTH;
130151
}
131152
return ddwaf_object_stringl(object, str.c_str(), len);
@@ -138,11 +159,16 @@ ddwaf_object* to_ddwaf_object(
138159
int depth,
139160
bool lim,
140161
bool ignoreToJson,
141-
JsSet stack
162+
JsSet stack,
163+
WAFTruncationMetrics* metrics
142164
) {
143165
mlog("starting to convert an object");
144166
if (depth >= DDWAF_MAX_CONTAINER_DEPTH) {
145167
mlog("Max depth reached");
168+
if (metrics) {
169+
metrics->max_truncated_container_depth = std::max(metrics->max_truncated_container_depth,
170+
static_cast<size_t>(depth));
171+
}
146172
return ddwaf_object_map(object);
147173
}
148174
if (val.IsNull()) {
@@ -151,7 +177,7 @@ ddwaf_object* to_ddwaf_object(
151177
}
152178
if (val.IsString()) {
153179
mlog("creating String");
154-
return to_ddwaf_string(object, val, lim);
180+
return to_ddwaf_string(object, val, lim, metrics);
155181
}
156182
if (val.IsNumber()) {
157183
mlog("creating Number");
@@ -190,32 +216,22 @@ ddwaf_object* to_ddwaf_object(
190216
stack.Add(val);
191217
mlog("creating Array");
192218
auto result =
193-
to_ddwaf_object_array(object, env, val.ToObject().As<Napi::Array>(), depth + 1, lim, ignoreToJson, stack);
219+
to_ddwaf_object_array(object, env, val.ToObject().As<Napi::Array>(), depth + 1, lim, ignoreToJson, stack,
220+
metrics);
194221
stack.Delete(val);
195222
return result;
196223
}
197224
if (val.IsObject()) {
198225
stack.Add(val);
199226
mlog("creating Object");
200-
auto result = to_ddwaf_object_object(object, env, val.ToObject(), depth + 1, lim, ignoreToJson, stack);
227+
auto result = to_ddwaf_object_object(object, env, val.ToObject(), depth + 1, lim, ignoreToJson, stack, metrics);
201228
stack.Delete(val);
202229
return result;
203230
}
204231
mlog("creating invalid object");
205232
return ddwaf_object_invalid(object);
206233
}
207234

208-
ddwaf_object* to_ddwaf_object(
209-
ddwaf_object *object,
210-
Napi::Env env,
211-
Napi::Value val,
212-
int depth,
213-
bool lim,
214-
bool ignoreToJson
215-
) {
216-
return to_ddwaf_object(object, env, val, depth, lim, ignoreToJson, JsSet::Create(env));
217-
}
218-
219235
Napi::Value from_ddwaf_object(ddwaf_object *object, Napi::Env env) {
220236
DDWAF_OBJ_TYPE type = object->type;
221237

src/convert.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@
77

88
#include <napi.h>
99
#include <ddwaf.h>
10+
#include "src/jsset.h"
11+
#include "src/metrics.h"
1012

1113
ddwaf_object* to_ddwaf_object(
1214
ddwaf_object *object,
1315
Napi::Env env,
1416
Napi::Value val,
1517
int depth,
1618
bool lim,
17-
bool ignoreToJson = false
19+
bool ignoreToJson,
20+
JsSet stack,
21+
WAFTruncationMetrics *metrics
1822
);
1923

2024
Napi::Value from_ddwaf_object(ddwaf_object *object, Napi::Env env);

src/main.cpp

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ DDWAF::DDWAF(const Napi::CallbackInfo& info) : Napi::ObjectWrap<DDWAF>(info) {
9393

9494
ddwaf_object rules;
9595
mlog("building rules");
96-
to_ddwaf_object(&rules, env, info[0], 0, false);
96+
to_ddwaf_object(&rules, env, info[0], 0, false, false, JsSet::Create(env), nullptr);
9797

9898
ddwaf_object diagnostics;
9999

@@ -153,7 +153,7 @@ void DDWAF::update(const Napi::CallbackInfo& info) {
153153

154154
ddwaf_object update;
155155
mlog("building rules update");
156-
to_ddwaf_object(&update, env, info[0], 0, false);
156+
to_ddwaf_object(&update, env, info[0], 0, false, false, JsSet::Create(env), nullptr);
157157

158158
ddwaf_object diagnostics;
159159

@@ -299,17 +299,18 @@ Napi::Value DDWAFContext::run(const Napi::CallbackInfo& info) {
299299
}
300300

301301
ddwaf_object *ddwafPersistent = nullptr;
302+
this->_metrics = {};
302303

303304
if (persistent.IsObject()) {
304305
ddwafPersistent = static_cast<ddwaf_object *>(alloca(sizeof(ddwaf_object)));
305-
to_ddwaf_object(ddwafPersistent, env, persistent, 0, true);
306+
to_ddwaf_object(ddwafPersistent, env, persistent, 0, true, false, JsSet::Create(env), &this->_metrics);
306307
}
307308

308309
ddwaf_object *ddwafEphemeral = nullptr;
309310

310311
if (ephemeral.IsObject()) {
311312
ddwafEphemeral = static_cast<ddwaf_object *>(alloca(sizeof(ddwaf_object)));
312-
to_ddwaf_object(ddwafEphemeral, env, ephemeral, 0, true);
313+
to_ddwaf_object(ddwafEphemeral, env, ephemeral, 0, true, false, JsSet::Create(env), &this->_metrics);
313314
}
314315

315316
ddwaf_result result;
@@ -321,24 +322,39 @@ Napi::Value DDWAFContext::run(const Napi::CallbackInfo& info) {
321322
&result,
322323
static_cast<uint64_t>(timeout));
323324

325+
Napi::Object res = Napi::Object::New(env);
326+
Napi::Object metrics = Napi::Object::New(env);
327+
328+
res.Set("metrics", metrics);
329+
330+
if (this->_metrics.max_truncated_string_length > 0) {
331+
metrics.Set("maxTruncatedString",
332+
Napi::Number::New(env, this->_metrics.max_truncated_string_length));
333+
}
334+
335+
if (this->_metrics.max_truncated_container_size > 0) {
336+
metrics.Set("maxTruncatedContainerSize",
337+
Napi::Number::New(env, this->_metrics.max_truncated_container_size));
338+
}
339+
340+
if (this->_metrics.max_truncated_container_depth > 0) {
341+
metrics.Set("maxTruncatedContainerDepth",
342+
Napi::Number::New(env, this->_metrics.max_truncated_container_depth));
343+
}
344+
345+
// Report if there is an error first
324346
switch (code) {
325347
case DDWAF_ERR_INTERNAL:
326-
Napi::Error::New(env, "Internal error").ThrowAsJavaScriptException();
327-
ddwaf_result_free(&result);
328-
return env.Null();
329348
case DDWAF_ERR_INVALID_OBJECT:
330-
Napi::Error::New(env, "Invalid ddwaf object").ThrowAsJavaScriptException();
331-
ddwaf_result_free(&result);
332-
return env.Null();
333349
case DDWAF_ERR_INVALID_ARGUMENT:
334-
Napi::Error::New(env, "Invalid arguments").ThrowAsJavaScriptException();
350+
res.Set("errorCode", Napi::Number::New(env, code));
335351
ddwaf_result_free(&result);
336-
return env.Null();
352+
return res;
337353
default:
338354
break;
339355
}
340356
// there is no error. We need to collect perf data
341-
Napi::Object res = Napi::Object::New(env);
357+
342358
mlog("Set timeout");
343359
res.Set("timeout", Napi::Boolean::New(env, result.timeout));
344360

src/main.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#define SRC_MAIN_H_
77
#include <napi.h>
88
#include <ddwaf.h>
9+
#include "src/metrics.h"
910

1011
// TODO(@vdeturckheim): logs with ddwaf_set_log_cb
1112
// TODO(@vdeturckheim): fix issue when used with workers
@@ -54,5 +55,6 @@ class DDWAFContext : public Napi::ObjectWrap<DDWAFContext> {
5455
private:
5556
bool _disposed;
5657
ddwaf_context _context;
58+
WAFTruncationMetrics _metrics;
5759
};
5860
#endif // SRC_MAIN_H_

src/metrics.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc.
4+
**/
5+
6+
#ifndef SRC_METRICS_H_
7+
#define SRC_METRICS_H_
8+
9+
#include <napi.h>
10+
11+
struct WAFTruncationMetrics {
12+
size_t max_truncated_string_length = 0;
13+
size_t max_truncated_container_size = 0;
14+
size_t max_truncated_container_depth = 0;
15+
};
16+
17+
#endif // SRC_METRICS_H_

0 commit comments

Comments
 (0)