Skip to content

Commit e75f56a

Browse files
CarlesDDsimon-id
andauthored
Update libddwaf to v1.24.1 (#137)
* New libddwaf builder API * Update typings * Update tests * Throw error on update/remove config fail * Refactor instance update handling * Get loaded config paths * Macro for path arguments * Minor fixes * Organize test * Destroy builder on finalize * Disable log * Fix cpp linting * Fix JS linting * Fix tests * Change assertion to be order insensitive * Add some assertions * Fix Napi returning type Co-authored-by: simon-id <simon.id@datadoghq.com> * Revert "Fix Napi returning type" This reverts commit d55c6ac. * Refactor getting string pointer for config path * Test * Test * Fix linting * Test the old handle still alive when an new empty config is applied --------- Co-authored-by: simon-id <simon.id@datadoghq.com>
1 parent f4102e4 commit e75f56a

File tree

8 files changed

+382
-145
lines changed

8 files changed

+382
-145
lines changed

index.d.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ export class DDWAF {
5656

5757
readonly disposed: boolean;
5858

59+
readonly configPaths: string[];
60+
5961
readonly diagnostics: {
6062
ruleset_version?: string,
6163
rules?: diagnosticsResult,
@@ -70,12 +72,13 @@ export class DDWAF {
7072
readonly knownAddresses: Set<string>;
7173
readonly knownActions: Set<string>;
7274

73-
constructor(rules: rules, config?: {
75+
constructor(rules: rules, rulesPath: string, config?: {
7476
obfuscatorKeyRegex?: string,
7577
obfuscatorValueRegex?: string
7678
});
7779

78-
update(rules: rules): void;
80+
createOrUpdateConfig(config: rules, path: string): boolean;
81+
removeConfig(path: string): boolean;
7982

8083
createContext(): DDWAFContext;
8184
dispose(): void;

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "8.5.2",
44
"description": "Node.js bindings for libddwaf",
55
"main": "index.js",
6-
"libddwaf_version": "1.22.0",
6+
"libddwaf_version": "1.24.1",
77
"scripts": {
88
"install": "exit 0",
99
"rebuild": "node-gyp rebuild",

src/main.cpp

Lines changed: 130 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ Napi::Object DDWAF::Init(Napi::Env env, Napi::Object exports) {
2020
mlog("Setting up class DDWAF");
2121
Napi::Function func = DefineClass(env, "DDWAF", {
2222
StaticMethod<&DDWAF::version>("version"),
23-
InstanceMethod<&DDWAF::update>("update"),
23+
InstanceMethod<&DDWAF::update_config>("createOrUpdateConfig"),
24+
InstanceMethod<&DDWAF::remove_config>("removeConfig"),
25+
InstanceAccessor("configPaths", &DDWAF::GetConfigPaths, nullptr, napi_enumerable),
2426
InstanceMethod<&DDWAF::createContext>("createContext"),
2527
InstanceMethod<&DDWAF::dispose>("dispose"),
2628
InstanceAccessor("disposed", &DDWAF::GetDisposed, nullptr, napi_enumerable),
@@ -42,29 +44,35 @@ Napi::Value DDWAF::GetDisposed(const Napi::CallbackInfo& info) {
4244
DDWAF::DDWAF(const Napi::CallbackInfo& info) : Napi::ObjectWrap<DDWAF>(info) {
4345
Napi::Env env = info.Env();
4446
size_t arg_len = info.Length();
45-
if (arg_len < 1) {
46-
Napi::Error::New(env, "Wrong number of arguments, expected at least 1").ThrowAsJavaScriptException();
47+
if (arg_len < 2) {
48+
Napi::Error::New(env, "Wrong number of arguments, expected at least 2").ThrowAsJavaScriptException();
4749
return;
4850
}
51+
4952
if (!info[0].IsObject()) {
5053
Napi::TypeError::New(env, "First argument must be an object").ThrowAsJavaScriptException();
5154
return;
5255
}
5356

57+
if (!info[1].IsString()) {
58+
Napi::TypeError::New(env, "Second argument must be a string").ThrowAsJavaScriptException();
59+
return;
60+
}
61+
5462
ddwaf_config waf_config{{0, 0, 0}, {nullptr, nullptr}, ddwaf_object_free};
5563

5664
// do not touch these strings after the c_str() assigment
5765
std::string key_regex_str;
5866
std::string value_regex_str;
5967

60-
if (arg_len >= 2) { // TODO(@simon-id): there is a bug here ?
68+
if (arg_len >= 3) { // TODO(@simon-id): there is a bug here ?
6169
// TODO(@simon-id) make a macro here someday
62-
if (!info[1].IsObject()) {
70+
if (!info[2].IsObject()) {
6371
Napi::TypeError::New(env, "Second argument must be an object").ThrowAsJavaScriptException();
6472
return;
6573
}
6674

67-
Napi::Object config = info[1].ToObject();
75+
Napi::Object config = info[2].ToObject();
6876

6977
if (config.Has("obfuscatorKeyRegex")) {
7078
Napi::Value key_regex = config.Get("obfuscatorKeyRegex");
@@ -94,23 +102,35 @@ DDWAF::DDWAF(const Napi::CallbackInfo& info) : Napi::ObjectWrap<DDWAF>(info) {
94102
ddwaf_object rules;
95103
mlog("building rules");
96104
to_ddwaf_object(&rules, env, info[0], 0, false, false, JsSet::Create(env), nullptr);
105+
std::string config_path = info[1].As<Napi::String>().Utf8Value();
97106

98107
ddwaf_object diagnostics;
99108

100-
mlog("Init WAF");
101-
ddwaf_handle handle = ddwaf_init(&rules, &waf_config, &diagnostics);
109+
mlog("Init Builder");
110+
ddwaf_builder builder = ddwaf_builder_init(&waf_config);
111+
bool result = ddwaf_builder_add_or_update_config(builder, LSTRARG(config_path.c_str()), &rules, &diagnostics);
112+
102113
ddwaf_object_free(&rules);
103114

104115
Napi::Value diagnostics_js = from_ddwaf_object(&diagnostics, env);
105116
info.This().As<Napi::Object>().Set("diagnostics", diagnostics_js);
106117

107118
ddwaf_object_free(&diagnostics);
108119

120+
if (!result) {
121+
Napi::Error::New(env, "Invalid rules").ThrowAsJavaScriptException();
122+
return;
123+
}
124+
125+
mlog("Init WAF");
126+
ddwaf_handle handle = ddwaf_builder_build_instance(builder);
127+
109128
if (handle == nullptr) {
110129
Napi::Error::New(env, "Invalid rules").ThrowAsJavaScriptException();
111130
return;
112131
}
113132

133+
this->_builder = builder;
114134
this->_handle = handle;
115135
this->_disposed = false;
116136

@@ -124,6 +144,7 @@ void DDWAF::Finalize(Napi::Env env) {
124144
return;
125145
}
126146
ddwaf_destroy(this->_handle);
147+
ddwaf_builder_destroy(this->_builder);
127148
this->_disposed = true;
128149
}
129150

@@ -132,52 +153,133 @@ void DDWAF::dispose(const Napi::CallbackInfo& info) {
132153
return this->Finalize(info.Env());
133154
}
134155

135-
void DDWAF::update(const Napi::CallbackInfo& info) {
136-
mlog("calling update on DDWAF");
156+
Napi::Value DDWAF::update_config(const Napi::CallbackInfo& info) {
157+
mlog("Calling update config on DDWAF");
137158

138159
Napi::Env env = info.Env();
139160

140161
if (this->_disposed) {
141162
Napi::Error::New(env, "Could not update a disposed WAF instance").ThrowAsJavaScriptException();
142-
return;
163+
return env.Undefined();
143164
}
144165

145-
if (info.Length() < 1) {
146-
Napi::Error::New(env, "Wrong number of arguments, expected at least 1").ThrowAsJavaScriptException();
147-
return;
166+
if (info.Length() < 2) {
167+
Napi::Error::New(env, "Wrong number of arguments, expected at least 2").ThrowAsJavaScriptException();
168+
return env.Undefined();
148169
}
170+
149171
if (!info[0].IsObject()) {
150172
Napi::TypeError::New(env, "First argument must be an object").ThrowAsJavaScriptException();
151-
return;
173+
return env.Undefined();
174+
}
175+
176+
if (!info[1].IsString()) {
177+
Napi::TypeError::New(env, "Second argument must be a string").ThrowAsJavaScriptException();
178+
return env.Undefined();
152179
}
153180

154181
ddwaf_object update;
155-
mlog("building rules update");
182+
mlog("Building config update");
156183
to_ddwaf_object(&update, env, info[0], 0, false, false, JsSet::Create(env), nullptr);
157184

185+
mlog("Obtaining config update path");
186+
std::string config_path = info[1].As<Napi::String>().Utf8Value();
187+
158188
ddwaf_object diagnostics;
159189

160-
mlog("Update DDWAF instance");
161-
ddwaf_handle updated_handle = ddwaf_update(this->_handle, &update, &diagnostics);
162-
ddwaf_object_free(&update);
190+
mlog("Applying new config to builder");
191+
bool update_result = ddwaf_builder_add_or_update_config(
192+
this->_builder,
193+
LSTRARG(config_path.c_str()),
194+
&update, &diagnostics);
163195

164196
Napi::Value diagnostics_js = from_ddwaf_object(&diagnostics, env);
165197
info.This().As<Napi::Object>().Set("diagnostics", diagnostics_js);
166198

167199
ddwaf_object_free(&diagnostics);
168200

169-
if (updated_handle == nullptr) {
170-
mlog("DDWAF updated handle is null");
171-
Napi::Error::New(env, "WAF has not been updated").ThrowAsJavaScriptException();
172-
return;
201+
if (!update_result) {
202+
mlog("DDWAF Builder update config has failed");
203+
return Napi::Boolean::New(env, false);
173204
}
174205

175-
mlog("New DDWAF updated instance")
176-
ddwaf_destroy(this->_handle);
177-
this->_handle = updated_handle;
206+
mlog("Update DDWAF instance");
207+
ddwaf_handle updated_handle = ddwaf_builder_build_instance(this->_builder);
208+
ddwaf_object_free(&update);
178209

179-
this->update_known_addresses(info);
180-
this->update_known_actions(info);
210+
if (updated_handle != nullptr) {
211+
mlog("New DDWAF updated instance")
212+
ddwaf_destroy(this->_handle);
213+
this->_handle = updated_handle;
214+
215+
this->update_known_addresses(info);
216+
this->update_known_actions(info);
217+
}
218+
219+
return Napi::Boolean::New(env, true);
220+
}
221+
222+
Napi::Value DDWAF::remove_config(const Napi::CallbackInfo& info) {
223+
mlog("Calling remove config on DDWAF");
224+
225+
Napi::Env env = info.Env();
226+
227+
if (this->_disposed) {
228+
Napi::Error::New(env, "Could not update a disposed WAF instance").ThrowAsJavaScriptException();
229+
return env.Undefined();
230+
}
231+
232+
if (info.Length() < 1) {
233+
Napi::Error::New(env, "Wrong number of arguments, expected at least 1").ThrowAsJavaScriptException();
234+
return env.Undefined();
235+
}
236+
237+
if (!info[0].IsString()) {
238+
Napi::TypeError::New(env, "First argument must be a string").ThrowAsJavaScriptException();
239+
return env.Undefined();
240+
}
241+
242+
mlog("Obtaining config remove path");
243+
std::string config_path = info[0].As<Napi::String>().Utf8Value();
244+
245+
mlog("Applying removed config to builder");
246+
bool remove_result = ddwaf_builder_remove_config(this->_builder, LSTRARG(config_path.c_str()));
247+
248+
if (!remove_result) {
249+
mlog("DDWAF Builder remove config has failed");
250+
return Napi::Boolean::New(env, false);
251+
}
252+
253+
mlog("Update DDWAF instance");
254+
ddwaf_handle updated_handle = ddwaf_builder_build_instance(this->_builder);
255+
256+
if (updated_handle != nullptr) {
257+
mlog("New DDWAF updated instance")
258+
ddwaf_destroy(this->_handle);
259+
this->_handle = updated_handle;
260+
261+
this->update_known_addresses(info);
262+
this->update_known_actions(info);
263+
}
264+
265+
return Napi::Boolean::New(env, true);
266+
}
267+
268+
Napi::Value DDWAF::GetConfigPaths(const Napi::CallbackInfo& info) {
269+
Napi::Env env = info.Env();
270+
271+
if (this->_disposed) {
272+
return Napi::Array::New(env, 0);
273+
}
274+
275+
ddwaf_object config_paths;
276+
ddwaf_builder_get_config_paths(this->_builder, &config_paths, nullptr, 0);
277+
278+
Napi::Value config_paths_js = from_ddwaf_object(&config_paths, env);
279+
280+
ddwaf_object_free(&config_paths);
281+
282+
return config_paths_js;
181283
}
182284

183285
void DDWAF::update_known_addresses(const Napi::CallbackInfo& info) {

src/main.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include <ddwaf.h>
99
#include "src/metrics.h"
1010

11+
#define LSTRARG(value) value, static_cast<uint32_t>(strlen(value))
12+
1113
// TODO(@vdeturckheim): logs with ddwaf_set_log_cb
1214
// TODO(@vdeturckheim): fix issue when used with workers
1315

@@ -21,7 +23,9 @@ class DDWAF : public Napi::ObjectWrap<DDWAF> {
2123
explicit DDWAF(const Napi::CallbackInfo& info);
2224

2325
// JS instance methods
24-
void update(const Napi::CallbackInfo& info);
26+
Napi::Value update_config(const Napi::CallbackInfo& info);
27+
Napi::Value remove_config(const Napi::CallbackInfo& info);
28+
Napi::Value GetConfigPaths(const Napi::CallbackInfo& info);
2529
Napi::Value createContext(const Napi::CallbackInfo& info);
2630
void Finalize(Napi::Env env);
2731
Napi::Value GetDisposed(const Napi::CallbackInfo& info);
@@ -32,6 +36,7 @@ class DDWAF : public Napi::ObjectWrap<DDWAF> {
3236
void update_known_actions(const Napi::CallbackInfo& info);
3337

3438
bool _disposed;
39+
ddwaf_builder _builder;
3540
ddwaf_handle _handle;
3641
};
3742

test/fuzz.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const blns = require('./blns.json')
1010

1111
const TIMEOUT = 9999e3
1212

13-
const waf = new DDWAF(rules)
13+
const waf = new DDWAF(rules, 'recommended')
1414

1515
const ENCODINGS = [ // from https://github.com/nodejs/node/blob/master/lib/buffer.js
1616
'utf8',

0 commit comments

Comments
 (0)