libddwaf 1.3.0.0.0 → 1.5.1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/lint.yml +33 -0
- data/.github/workflows/package.yml +32 -19
- data/.github/workflows/test.yml +9 -12
- data/CHANGELOG.md +79 -0
- data/README.md +118 -0
- data/Steepfile +22 -0
- data/lib/datadog/appsec/waf/version.rb +2 -2
- data/lib/datadog/appsec/waf.rb +345 -96
- data/libddwaf.gemspec +1 -1
- data/sig/datadog/appsec/waf/version.rbs +13 -0
- data/sig/datadog/appsec/waf.rbs +230 -0
- data/sig/libddwaf.rbs +0 -0
- data/vendor/rbs/ffi/0/ffi.rbs +62 -0
- data/vendor/rbs/gem/0/gem.rbs +7 -0
- data/vendor/rbs/jruby/0/jruby.rbs +3 -0
- metadata +12 -3
data/lib/datadog/appsec/waf.rb
CHANGED
@@ -6,7 +6,13 @@ module Datadog
|
|
6
6
|
module AppSec
|
7
7
|
module WAF
|
8
8
|
module LibDDWAF
|
9
|
-
class Error < StandardError
|
9
|
+
class Error < StandardError
|
10
|
+
attr_reader :ruleset_info
|
11
|
+
|
12
|
+
def initialize(msg, ruleset_info: nil)
|
13
|
+
@ruleset_info = ruleset_info
|
14
|
+
end
|
15
|
+
end
|
10
16
|
|
11
17
|
extend ::FFI::Library
|
12
18
|
|
@@ -26,12 +32,22 @@ module Datadog
|
|
26
32
|
Gem::Platform.local.os
|
27
33
|
end
|
28
34
|
|
35
|
+
def self.local_version
|
36
|
+
return nil unless local_os == 'linux'
|
37
|
+
|
38
|
+
# Old rubygems don't handle non-gnu linux correctly
|
39
|
+
return $1 if RUBY_PLATFORM =~ /linux-(.+)$/
|
40
|
+
|
41
|
+
'gnu'
|
42
|
+
end
|
43
|
+
|
29
44
|
def self.local_cpu
|
30
45
|
if RUBY_ENGINE == 'jruby'
|
31
46
|
os_arch = java.lang.System.get_property('os.arch')
|
32
47
|
|
33
48
|
cpu = case os_arch
|
34
49
|
when 'amd64' then 'x86_64'
|
50
|
+
when 'aarch64' then 'aarch64'
|
35
51
|
else raise Error, "unsupported JRuby os.arch: #{os_arch.inspect}"
|
36
52
|
end
|
37
53
|
|
@@ -41,27 +57,50 @@ module Datadog
|
|
41
57
|
Gem::Platform.local.cpu
|
42
58
|
end
|
43
59
|
|
60
|
+
def self.source_dir
|
61
|
+
__dir__ || raise('__dir__ is nil: eval?')
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.vendor_dir
|
65
|
+
File.join(source_dir, '../../../vendor')
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.libddwaf_vendor_dir
|
69
|
+
File.join(vendor_dir, 'libddwaf')
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.shared_lib_triplet(version: local_version)
|
73
|
+
version ? "#{local_os}-#{version}-#{local_cpu}" : "#{local_os}-#{local_cpu}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.libddwaf_dir
|
77
|
+
default = File.join(libddwaf_vendor_dir,
|
78
|
+
"libddwaf-#{Datadog::AppSec::WAF::VERSION::BASE_STRING}-#{shared_lib_triplet}")
|
79
|
+
candidates = [
|
80
|
+
default
|
81
|
+
]
|
82
|
+
|
83
|
+
if local_os == 'linux'
|
84
|
+
candidates << File.join(libddwaf_vendor_dir,
|
85
|
+
"libddwaf-#{Datadog::AppSec::WAF::VERSION::BASE_STRING}-#{shared_lib_triplet(version: nil)}")
|
86
|
+
end
|
87
|
+
|
88
|
+
candidates.find { |d| Dir.exist?(d) } || default
|
89
|
+
end
|
90
|
+
|
44
91
|
def self.shared_lib_extname
|
45
92
|
Gem::Platform.local.os == 'darwin' ? '.dylib' : '.so'
|
46
93
|
end
|
47
94
|
|
48
95
|
def self.shared_lib_path
|
49
|
-
File.join(
|
96
|
+
File.join(libddwaf_dir, 'lib', "libddwaf#{shared_lib_extname}")
|
50
97
|
end
|
51
98
|
|
52
99
|
ffi_lib [shared_lib_path]
|
53
100
|
|
54
101
|
# version
|
55
102
|
|
56
|
-
|
57
|
-
layout :major, :uint16,
|
58
|
-
:minor, :uint16,
|
59
|
-
:patch, :uint16
|
60
|
-
end
|
61
|
-
|
62
|
-
typedef Version.by_ref, :ddwaf_version
|
63
|
-
|
64
|
-
attach_function :ddwaf_get_version, [:ddwaf_version], :void
|
103
|
+
attach_function :ddwaf_get_version, [], :string
|
65
104
|
|
66
105
|
# ddwaf::object data structure
|
67
106
|
|
@@ -70,7 +109,9 @@ module Datadog
|
|
70
109
|
:ddwaf_obj_unsigned, 1 << 1,
|
71
110
|
:ddwaf_obj_string, 1 << 2,
|
72
111
|
:ddwaf_obj_array, 1 << 3,
|
73
|
-
:ddwaf_obj_map, 1 << 4
|
112
|
+
:ddwaf_obj_map, 1 << 4,
|
113
|
+
:ddwaf_obj_bool, 1 << 5
|
114
|
+
typedef DDWAF_OBJ_TYPE, :ddwaf_obj_type
|
74
115
|
|
75
116
|
typedef :pointer, :charptr
|
76
117
|
typedef :pointer, :charptrptr
|
@@ -97,7 +138,8 @@ module Datadog
|
|
97
138
|
layout :stringValue, :charptr,
|
98
139
|
:uintValue, :uint64,
|
99
140
|
:intValue, :int64,
|
100
|
-
:array, :pointer
|
141
|
+
:array, :pointer,
|
142
|
+
:boolean, :bool
|
101
143
|
end
|
102
144
|
|
103
145
|
class Object < ::FFI::Struct
|
@@ -105,7 +147,7 @@ module Datadog
|
|
105
147
|
:parameterNameLength, :uint64,
|
106
148
|
:valueUnion, ObjectValueUnion,
|
107
149
|
:nbEntries, :uint64,
|
108
|
-
:type,
|
150
|
+
:type, :ddwaf_obj_type
|
109
151
|
end
|
110
152
|
|
111
153
|
typedef Object.by_ref, :ddwaf_object
|
@@ -120,6 +162,7 @@ module Datadog
|
|
120
162
|
attach_function :ddwaf_object_signed, [:ddwaf_object, :int64], :ddwaf_object
|
121
163
|
attach_function :ddwaf_object_unsigned_force, [:ddwaf_object, :uint64], :ddwaf_object
|
122
164
|
attach_function :ddwaf_object_signed_force, [:ddwaf_object, :int64], :ddwaf_object
|
165
|
+
attach_function :ddwaf_object_bool, [:ddwaf_object, :bool], :ddwaf_object
|
123
166
|
|
124
167
|
attach_function :ddwaf_object_array, [:ddwaf_object], :ddwaf_object
|
125
168
|
attach_function :ddwaf_object_array_add, [:ddwaf_object, :ddwaf_object], :bool
|
@@ -150,6 +193,8 @@ module Datadog
|
|
150
193
|
typedef :pointer, :ddwaf_handle
|
151
194
|
typedef Object.by_ref, :ddwaf_rule
|
152
195
|
|
196
|
+
callback :ddwaf_object_free_fn, [:ddwaf_object], :void
|
197
|
+
|
153
198
|
class Config < ::FFI::Struct
|
154
199
|
class Limits < ::FFI::Struct
|
155
200
|
layout :max_container_size, :uint32,
|
@@ -158,12 +203,13 @@ module Datadog
|
|
158
203
|
end
|
159
204
|
|
160
205
|
class Obfuscator < ::FFI::Struct
|
161
|
-
layout :key_regex, :
|
162
|
-
:value_regex, :
|
206
|
+
layout :key_regex, :pointer, # should be :charptr
|
207
|
+
:value_regex, :pointer # should be :charptr
|
163
208
|
end
|
164
209
|
|
165
210
|
layout :limits, Limits,
|
166
|
-
:obfuscator, Obfuscator
|
211
|
+
:obfuscator, Obfuscator,
|
212
|
+
:free_fn, :pointer #:ddwaf_object_free_fn
|
167
213
|
end
|
168
214
|
|
169
215
|
typedef Config.by_ref, :ddwaf_config
|
@@ -183,34 +229,44 @@ module Datadog
|
|
183
229
|
attach_function :ddwaf_init, [:ddwaf_rule, :ddwaf_config, :ddwaf_ruleset_info], :ddwaf_handle
|
184
230
|
attach_function :ddwaf_destroy, [:ddwaf_handle], :void
|
185
231
|
|
186
|
-
attach_function :ddwaf_required_addresses, [:ddwaf_handle,
|
232
|
+
attach_function :ddwaf_required_addresses, [:ddwaf_handle, UInt32Ptr], :charptrptr
|
233
|
+
attach_function :ddwaf_required_rule_data_ids, [:ddwaf_handle, UInt32Ptr], :charptrptr
|
234
|
+
|
235
|
+
# updating
|
236
|
+
|
237
|
+
DDWAF_RET_CODE = enum :ddwaf_err_internal, -3,
|
238
|
+
:ddwaf_err_invalid_object, -2,
|
239
|
+
:ddwaf_err_invalid_argument, -1,
|
240
|
+
:ddwaf_ok, 0,
|
241
|
+
:ddwaf_match, 1
|
242
|
+
typedef DDWAF_RET_CODE, :ddwaf_ret_code
|
243
|
+
|
244
|
+
attach_function :ddwaf_update_rule_data, [:ddwaf_handle, :ddwaf_object], :ddwaf_ret_code
|
245
|
+
attach_function :ddwaf_toggle_rules, [:ddwaf_handle, :ddwaf_object], :ddwaf_ret_code
|
187
246
|
|
188
247
|
# running
|
189
248
|
|
190
249
|
typedef :pointer, :ddwaf_context
|
191
250
|
|
192
|
-
|
193
|
-
|
194
|
-
attach_function :ddwaf_context_init, [:ddwaf_handle, :ddwaf_object_free_fn], :ddwaf_context
|
251
|
+
attach_function :ddwaf_context_init, [:ddwaf_handle], :ddwaf_context
|
195
252
|
attach_function :ddwaf_context_destroy, [:ddwaf_context], :void
|
196
253
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
:ddwaf_monitor, 1,
|
202
|
-
:ddwaf_block, 2
|
254
|
+
class ResultActions < ::FFI::Struct
|
255
|
+
layout :array, :charptrptr,
|
256
|
+
:size, :uint32
|
257
|
+
end
|
203
258
|
|
204
259
|
class Result < ::FFI::Struct
|
205
260
|
layout :timeout, :bool,
|
206
261
|
:data, :string,
|
262
|
+
:actions, ResultActions,
|
207
263
|
:total_runtime, :uint64
|
208
264
|
end
|
209
265
|
|
210
266
|
typedef Result.by_ref, :ddwaf_result
|
211
267
|
typedef :uint64, :timeout_us
|
212
268
|
|
213
|
-
attach_function :ddwaf_run, [:ddwaf_context, :ddwaf_object, :ddwaf_result, :timeout_us],
|
269
|
+
attach_function :ddwaf_run, [:ddwaf_context, :ddwaf_object, :ddwaf_result, :timeout_us], :ddwaf_ret_code, blocking: true
|
214
270
|
attach_function :ddwaf_result_free, [:ddwaf_result], :void
|
215
271
|
|
216
272
|
# logging
|
@@ -221,20 +277,28 @@ module Datadog
|
|
221
277
|
:ddwaf_log_warn,
|
222
278
|
:ddwaf_log_error,
|
223
279
|
:ddwaf_log_off
|
280
|
+
typedef DDWAF_LOG_LEVEL, :ddwaf_log_level
|
281
|
+
|
282
|
+
callback :ddwaf_log_cb, [:ddwaf_log_level, :string, :string, :uint, :charptr, :uint64], :void
|
283
|
+
|
284
|
+
attach_function :ddwaf_set_log_cb, [:ddwaf_log_cb, :ddwaf_log_level], :bool
|
285
|
+
|
286
|
+
DEFAULT_MAX_CONTAINER_SIZE = 0
|
287
|
+
DEFAULT_MAX_CONTAINER_DEPTH = 0
|
288
|
+
DEFAULT_MAX_STRING_LENGTH = 0
|
224
289
|
|
225
|
-
|
290
|
+
DDWAF_MAX_CONTAINER_SIZE = 256
|
291
|
+
DDWAF_MAX_CONTAINER_DEPTH = 20
|
292
|
+
DDWAF_MAX_STRING_LENGTH = 4096
|
226
293
|
|
227
|
-
|
294
|
+
DDWAF_RUN_TIMEOUT = 5000
|
228
295
|
end
|
229
296
|
|
230
297
|
def self.version
|
231
|
-
|
232
|
-
LibDDWAF.ddwaf_get_version(version.pointer)
|
233
|
-
|
234
|
-
[version[:major], version[:minor], version[:patch]]
|
298
|
+
LibDDWAF.ddwaf_get_version
|
235
299
|
end
|
236
300
|
|
237
|
-
def self.ruby_to_object(val)
|
301
|
+
def self.ruby_to_object(val, max_container_size: nil, max_container_depth: nil, max_string_length: nil, coerce: true)
|
238
302
|
case val
|
239
303
|
when Array
|
240
304
|
obj = LibDDWAF::Object.new
|
@@ -243,12 +307,20 @@ module Datadog
|
|
243
307
|
fail LibDDWAF::Error, "Could not convert into object: #{val}"
|
244
308
|
end
|
245
309
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
310
|
+
max_index = max_container_size - 1 if max_container_size
|
311
|
+
val.each.with_index do |e, i|
|
312
|
+
member = ruby_to_object(e,
|
313
|
+
max_container_size: max_container_size,
|
314
|
+
max_container_depth: (max_container_depth - 1 if max_container_depth),
|
315
|
+
max_string_length: max_string_length,
|
316
|
+
coerce: coerce)
|
317
|
+
e_res = LibDDWAF.ddwaf_object_array_add(obj, member)
|
318
|
+
unless e_res
|
319
|
+
fail LibDDWAF::Error, "Could not add to array object: #{e.inspect}"
|
250
320
|
end
|
251
|
-
|
321
|
+
|
322
|
+
break val if max_index && i >= max_index
|
323
|
+
end unless max_container_depth == 0
|
252
324
|
|
253
325
|
obj
|
254
326
|
when Hash
|
@@ -258,35 +330,56 @@ module Datadog
|
|
258
330
|
fail LibDDWAF::Error, "Could not convert into object: #{val}"
|
259
331
|
end
|
260
332
|
|
261
|
-
|
262
|
-
|
263
|
-
|
333
|
+
max_index = max_container_size - 1 if max_container_size
|
334
|
+
val.each.with_index do |e, i|
|
335
|
+
k, v = e[0], e[1] # for Steep, which doesn't handle |(k, v), i|
|
336
|
+
|
337
|
+
k = k.to_s[0, max_string_length] if max_string_length
|
338
|
+
member = ruby_to_object(v,
|
339
|
+
max_container_size: max_container_size,
|
340
|
+
max_container_depth: (max_container_depth - 1 if max_container_depth),
|
341
|
+
max_string_length: max_string_length,
|
342
|
+
coerce: coerce)
|
343
|
+
kv_res = LibDDWAF.ddwaf_object_map_addl(obj, k.to_s, k.to_s.bytesize, member)
|
344
|
+
unless kv_res
|
264
345
|
fail LibDDWAF::Error, "Could not add to map object: #{k.inspect} => #{v.inspect}"
|
265
346
|
end
|
266
|
-
|
347
|
+
|
348
|
+
break val if max_index && i >= max_index
|
349
|
+
end unless max_container_depth == 0
|
267
350
|
|
268
351
|
obj
|
269
352
|
when String
|
270
353
|
obj = LibDDWAF::Object.new
|
271
|
-
|
354
|
+
val = val.to_s[0, max_string_length] if max_string_length
|
355
|
+
str = val.to_s
|
356
|
+
res = LibDDWAF.ddwaf_object_stringl(obj, str, str.bytesize)
|
272
357
|
if res.null?
|
273
|
-
fail LibDDWAF::Error, "Could not convert into object: #{val}"
|
358
|
+
fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
|
274
359
|
end
|
275
360
|
|
276
361
|
obj
|
277
362
|
when Symbol
|
278
363
|
obj = LibDDWAF::Object.new
|
279
|
-
|
364
|
+
val = val.to_s[0, max_string_length] if max_string_length
|
365
|
+
str = val.to_s
|
366
|
+
res = LibDDWAF.ddwaf_object_stringl(obj, str, str.bytesize)
|
280
367
|
if res.null?
|
281
|
-
fail LibDDWAF::Error, "Could not convert into object: #{val}"
|
368
|
+
fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
|
282
369
|
end
|
283
370
|
|
284
371
|
obj
|
285
372
|
when Integer
|
286
373
|
obj = LibDDWAF::Object.new
|
287
|
-
res =
|
374
|
+
res = if coerce
|
375
|
+
LibDDWAF.ddwaf_object_string(obj, val.to_s)
|
376
|
+
elsif val < 0
|
377
|
+
LibDDWAF.ddwaf_object_signed_force(obj, val)
|
378
|
+
else
|
379
|
+
LibDDWAF.ddwaf_object_unsigned_force(obj, val)
|
380
|
+
end
|
288
381
|
if res.null?
|
289
|
-
fail LibDDWAF::Error, "Could not convert into object: #{val}"
|
382
|
+
fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
|
290
383
|
end
|
291
384
|
|
292
385
|
obj
|
@@ -294,15 +387,19 @@ module Datadog
|
|
294
387
|
obj = LibDDWAF::Object.new
|
295
388
|
res = LibDDWAF.ddwaf_object_string(obj, val.to_s)
|
296
389
|
if res.null?
|
297
|
-
fail LibDDWAF::Error, "Could not convert into object: #{val}"
|
390
|
+
fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
|
298
391
|
end
|
299
392
|
|
300
393
|
obj
|
301
394
|
when TrueClass, FalseClass
|
302
395
|
obj = LibDDWAF::Object.new
|
303
|
-
res =
|
396
|
+
res = if coerce
|
397
|
+
LibDDWAF.ddwaf_object_string(obj, val.to_s)
|
398
|
+
else
|
399
|
+
LibDDWAF.ddwaf_object_bool(obj, val)
|
400
|
+
end
|
304
401
|
if res.null?
|
305
|
-
fail LibDDWAF::Error, "Could not convert into object: #{val}"
|
402
|
+
fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
|
306
403
|
end
|
307
404
|
|
308
405
|
obj
|
@@ -315,6 +412,8 @@ module Datadog
|
|
315
412
|
case obj[:type]
|
316
413
|
when :ddwaf_obj_invalid
|
317
414
|
nil
|
415
|
+
when :ddwaf_obj_bool
|
416
|
+
obj[:valueUnion][:boolean]
|
318
417
|
when :ddwaf_obj_string
|
319
418
|
obj[:valueUnion][:stringValue].read_bytes(obj[:nbEntries])
|
320
419
|
when :ddwaf_obj_signed
|
@@ -339,22 +438,50 @@ module Datadog
|
|
339
438
|
end
|
340
439
|
end
|
341
440
|
|
441
|
+
def self.log_callback(level, func, file, line, message, len)
|
442
|
+
return if logger.nil?
|
443
|
+
|
444
|
+
logger.debug do
|
445
|
+
{
|
446
|
+
level: level,
|
447
|
+
func: func,
|
448
|
+
file: file,
|
449
|
+
line: line,
|
450
|
+
message: message.read_bytes(len)
|
451
|
+
}.inspect
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
def self.logger
|
456
|
+
@logger
|
457
|
+
end
|
458
|
+
|
342
459
|
def self.logger=(logger)
|
343
|
-
@
|
344
|
-
|
460
|
+
unless @log_callback
|
461
|
+
log_callback = method(:log_callback)
|
462
|
+
Datadog::AppSec::WAF::LibDDWAF.ddwaf_set_log_cb(log_callback, :ddwaf_log_trace)
|
463
|
+
|
464
|
+
# retain logging proc if set properly
|
465
|
+
@log_callback = log_callback
|
345
466
|
end
|
346
467
|
|
347
|
-
|
468
|
+
@logger = logger
|
348
469
|
end
|
349
470
|
|
471
|
+
RESULT_CODE = {
|
472
|
+
ddwaf_err_internal: :err_internal,
|
473
|
+
ddwaf_err_invalid_object: :err_invalid_object,
|
474
|
+
ddwaf_err_invalid_argument: :err_invalid_argument,
|
475
|
+
ddwaf_ok: :ok,
|
476
|
+
ddwaf_match: :match,
|
477
|
+
}
|
478
|
+
|
350
479
|
class Handle
|
351
480
|
attr_reader :handle_obj
|
352
481
|
|
353
|
-
|
354
|
-
DEFAULT_MAX_CONTAINER_DEPTH = 0
|
355
|
-
DEFAULT_MAX_STRING_LENGTH = 0
|
482
|
+
attr_reader :ruleset_info
|
356
483
|
|
357
|
-
def initialize(rule,
|
484
|
+
def initialize(rule, limits: {}, obfuscator: {})
|
358
485
|
rule_obj = Datadog::AppSec::WAF.ruby_to_object(rule)
|
359
486
|
if rule_obj.null? || rule_obj[:type] == :ddwaf_object_invalid
|
360
487
|
fail LibDDWAF::Error, "Could not convert object #{rule.inspect}"
|
@@ -364,71 +491,154 @@ module Datadog
|
|
364
491
|
if config_obj.null?
|
365
492
|
fail LibDDWAF::Error, 'Could not create config struct'
|
366
493
|
end
|
494
|
+
retain(config_obj)
|
367
495
|
|
368
|
-
config_obj[:limits][:max_container_size] =
|
369
|
-
config_obj[:limits][:max_container_depth] =
|
370
|
-
config_obj[:limits][:max_string_length] =
|
496
|
+
config_obj[:limits][:max_container_size] = limits[:max_container_size] || LibDDWAF::DEFAULT_MAX_CONTAINER_SIZE
|
497
|
+
config_obj[:limits][:max_container_depth] = limits[:max_container_depth] || LibDDWAF::DEFAULT_MAX_CONTAINER_DEPTH
|
498
|
+
config_obj[:limits][:max_string_length] = limits[:max_string_length] || LibDDWAF::DEFAULT_MAX_STRING_LENGTH
|
499
|
+
config_obj[:obfuscator][:key_regex] = FFI::MemoryPointer.from_string(obfuscator[:key_regex]) if obfuscator[:key_regex]
|
500
|
+
config_obj[:obfuscator][:value_regex] = FFI::MemoryPointer.from_string(obfuscator[:value_regex]) if obfuscator[:value_regex]
|
501
|
+
config_obj[:free_fn] = Datadog::AppSec::WAF::LibDDWAF::ObjectNoFree
|
371
502
|
|
372
|
-
ruleset_info = LibDDWAF::
|
503
|
+
ruleset_info = LibDDWAF::RuleSetInfo.new
|
373
504
|
|
374
505
|
@handle_obj = Datadog::AppSec::WAF::LibDDWAF.ddwaf_init(rule_obj, config_obj, ruleset_info)
|
506
|
+
|
507
|
+
@ruleset_info = {
|
508
|
+
loaded: ruleset_info[:loaded],
|
509
|
+
failed: ruleset_info[:failed],
|
510
|
+
errors: WAF.object_to_ruby(ruleset_info[:errors]),
|
511
|
+
version: ruleset_info[:version],
|
512
|
+
}
|
513
|
+
|
375
514
|
if @handle_obj.null?
|
376
|
-
fail LibDDWAF::Error
|
515
|
+
fail LibDDWAF::Error.new('Could not create handle', ruleset_info: @ruleset_info)
|
377
516
|
end
|
378
517
|
|
379
|
-
|
518
|
+
validate!
|
380
519
|
ensure
|
381
520
|
Datadog::AppSec::WAF::LibDDWAF.ddwaf_ruleset_info_free(ruleset_info) if ruleset_info
|
382
521
|
Datadog::AppSec::WAF::LibDDWAF.ddwaf_object_free(rule_obj) if rule_obj
|
383
522
|
end
|
384
523
|
|
385
|
-
def
|
386
|
-
|
387
|
-
|
388
|
-
|
524
|
+
def finalize
|
525
|
+
invalidate!
|
526
|
+
|
527
|
+
Datadog::AppSec::WAF::LibDDWAF.ddwaf_destroy(handle_obj)
|
528
|
+
end
|
529
|
+
|
530
|
+
def required_addresses
|
531
|
+
valid!
|
532
|
+
|
533
|
+
count = Datadog::AppSec::WAF::LibDDWAF::UInt32Ptr.new
|
534
|
+
list = Datadog::AppSec::WAF::LibDDWAF.ddwaf_required_addresses(handle_obj, count)
|
535
|
+
|
536
|
+
return [] if count == 0 # list is null
|
537
|
+
|
538
|
+
list.get_array_of_string(0, count[:value])
|
539
|
+
end
|
540
|
+
|
541
|
+
def update_rule_data(data)
|
542
|
+
data_obj = Datadog::AppSec::WAF.ruby_to_object(data, coerce: false)
|
543
|
+
res = Datadog::AppSec::WAF::LibDDWAF.ddwaf_update_rule_data(@handle_obj, data_obj)
|
544
|
+
|
545
|
+
RESULT_CODE[res]
|
546
|
+
ensure
|
547
|
+
Datadog::AppSec::WAF::LibDDWAF.ddwaf_object_free(data_obj) if data_obj
|
548
|
+
end
|
549
|
+
|
550
|
+
def toggle_rules(map)
|
551
|
+
map_obj = Datadog::AppSec::WAF.ruby_to_object(map, coerce: false)
|
552
|
+
res = Datadog::AppSec::WAF::LibDDWAF.ddwaf_toggle_rules(@handle_obj, map_obj)
|
553
|
+
|
554
|
+
RESULT_CODE[res]
|
555
|
+
ensure
|
556
|
+
Datadog::AppSec::WAF::LibDDWAF.ddwaf_object_free(map_obj) if map_obj
|
557
|
+
end
|
558
|
+
|
559
|
+
private
|
560
|
+
|
561
|
+
def validate!
|
562
|
+
@valid = true
|
563
|
+
end
|
564
|
+
|
565
|
+
def invalidate!
|
566
|
+
@valid = false
|
567
|
+
end
|
568
|
+
|
569
|
+
def valid?
|
570
|
+
@valid
|
571
|
+
end
|
572
|
+
|
573
|
+
def valid!
|
574
|
+
return if valid?
|
575
|
+
|
576
|
+
fail LibDDWAF::Error, "Attempt to use an invalid instance: #{inspect}"
|
577
|
+
end
|
578
|
+
|
579
|
+
def retained
|
580
|
+
@retained ||= []
|
581
|
+
end
|
582
|
+
|
583
|
+
def retain(object)
|
584
|
+
retained << object
|
585
|
+
end
|
586
|
+
|
587
|
+
def release(object)
|
588
|
+
retained.delete(object)
|
389
589
|
end
|
390
590
|
end
|
391
591
|
|
392
|
-
Result
|
592
|
+
class Result
|
593
|
+
attr_reader :status, :data, :total_runtime, :timeout, :actions
|
594
|
+
|
595
|
+
def initialize(status, data, total_runtime, timeout, actions)
|
596
|
+
@status = status
|
597
|
+
@data = data
|
598
|
+
@total_runtime = total_runtime
|
599
|
+
@timeout = timeout
|
600
|
+
@actions = actions
|
601
|
+
end
|
602
|
+
end
|
393
603
|
|
394
604
|
class Context
|
395
605
|
attr_reader :context_obj
|
396
606
|
|
397
607
|
def initialize(handle)
|
398
608
|
handle_obj = handle.handle_obj
|
399
|
-
|
609
|
+
retain(handle)
|
400
610
|
|
401
|
-
@context_obj = Datadog::AppSec::WAF::LibDDWAF.ddwaf_context_init(handle_obj
|
611
|
+
@context_obj = Datadog::AppSec::WAF::LibDDWAF.ddwaf_context_init(handle_obj)
|
402
612
|
if @context_obj.null?
|
403
613
|
fail LibDDWAF::Error, 'Could not create context'
|
404
614
|
end
|
405
615
|
|
406
|
-
|
407
|
-
|
408
|
-
ObjectSpace.define_finalizer(self, Context.finalizer(context_obj, @input_objs))
|
616
|
+
validate!
|
409
617
|
end
|
410
618
|
|
411
|
-
def
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
619
|
+
def finalize
|
620
|
+
invalidate!
|
621
|
+
|
622
|
+
retained.each do |retained_obj|
|
623
|
+
next unless retained_obj.is_a?(Datadog::AppSec::WAF::LibDDWAF::Object)
|
624
|
+
|
625
|
+
Datadog::AppSec::WAF::LibDDWAF.ddwaf_object_free(retained_obj)
|
417
626
|
end
|
627
|
+
|
628
|
+
Datadog::AppSec::WAF::LibDDWAF.ddwaf_context_destroy(context_obj)
|
418
629
|
end
|
419
630
|
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
ddwaf_monitor: :monitor,
|
427
|
-
ddwaf_block: :block,
|
428
|
-
}
|
631
|
+
def run(input, timeout = LibDDWAF::DDWAF_RUN_TIMEOUT)
|
632
|
+
valid!
|
633
|
+
|
634
|
+
max_container_size = LibDDWAF::DDWAF_MAX_CONTAINER_SIZE
|
635
|
+
max_container_depth = LibDDWAF::DDWAF_MAX_CONTAINER_DEPTH
|
636
|
+
max_string_length = LibDDWAF::DDWAF_MAX_STRING_LENGTH
|
429
637
|
|
430
|
-
|
431
|
-
|
638
|
+
input_obj = Datadog::AppSec::WAF.ruby_to_object(input,
|
639
|
+
max_container_size: max_container_size,
|
640
|
+
max_container_depth: max_container_depth,
|
641
|
+
max_string_length: max_string_length)
|
432
642
|
if input_obj.null?
|
433
643
|
fail LibDDWAF::Error, "Could not convert input: #{input.inspect}"
|
434
644
|
end
|
@@ -439,21 +649,60 @@ module Datadog
|
|
439
649
|
end
|
440
650
|
|
441
651
|
# retain C objects in memory for subsequent calls to run
|
442
|
-
|
652
|
+
retain(input_obj)
|
443
653
|
|
444
654
|
code = Datadog::AppSec::WAF::LibDDWAF.ddwaf_run(@context_obj, input_obj, result_obj, timeout)
|
445
655
|
|
656
|
+
actions = if result_obj[:actions][:size] > 0
|
657
|
+
result_obj[:actions][:array].get_array_of_string(0, result_obj[:actions][:size])
|
658
|
+
else
|
659
|
+
[]
|
660
|
+
end
|
661
|
+
|
446
662
|
result = Result.new(
|
447
|
-
|
663
|
+
RESULT_CODE[code],
|
448
664
|
(JSON.parse(result_obj[:data]) if result_obj[:data] != nil),
|
449
665
|
result_obj[:total_runtime],
|
450
666
|
result_obj[:timeout],
|
667
|
+
actions,
|
451
668
|
)
|
452
669
|
|
453
|
-
[
|
670
|
+
[RESULT_CODE[code], result]
|
454
671
|
ensure
|
455
672
|
Datadog::AppSec::WAF::LibDDWAF.ddwaf_result_free(result_obj) if result_obj
|
456
673
|
end
|
674
|
+
|
675
|
+
private
|
676
|
+
|
677
|
+
def validate!
|
678
|
+
@valid = true
|
679
|
+
end
|
680
|
+
|
681
|
+
def invalidate!
|
682
|
+
@valid = false
|
683
|
+
end
|
684
|
+
|
685
|
+
def valid?
|
686
|
+
@valid
|
687
|
+
end
|
688
|
+
|
689
|
+
def valid!
|
690
|
+
return if valid?
|
691
|
+
|
692
|
+
fail LibDDWAF::Error, "Attempt to use an invalid instance: #{inspect}"
|
693
|
+
end
|
694
|
+
|
695
|
+
def retained
|
696
|
+
@retained ||= []
|
697
|
+
end
|
698
|
+
|
699
|
+
def retain(object)
|
700
|
+
retained << object
|
701
|
+
end
|
702
|
+
|
703
|
+
def release(object)
|
704
|
+
retained.delete(object)
|
705
|
+
end
|
457
706
|
end
|
458
707
|
end
|
459
708
|
end
|
data/libddwaf.gemspec
CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
libddwaf packages a WAF implementation in C++, exposed to Ruby
|
18
18
|
EOS
|
19
19
|
|
20
|
-
spec.homepage = 'https://github.com/DataDog/libddwaf'
|
20
|
+
spec.homepage = 'https://github.com/DataDog/libddwaf-rb'
|
21
21
|
spec.license = 'BSD-3-Clause'
|
22
22
|
|
23
23
|
if spec.respond_to?(:metadata)
|