libddwaf 1.3.0.2.0 → 1.5.1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -57,20 +57,35 @@ module Datadog
57
57
  Gem::Platform.local.cpu
58
58
  end
59
59
 
60
+ def self.source_dir
61
+ __dir__ || raise('__dir__ is nil: eval?')
62
+ end
63
+
60
64
  def self.vendor_dir
61
- File.join(__dir__, '../../../vendor')
65
+ File.join(source_dir, '../../../vendor')
62
66
  end
63
67
 
64
68
  def self.libddwaf_vendor_dir
65
69
  File.join(vendor_dir, 'libddwaf')
66
70
  end
67
71
 
68
- def self.shared_lib_triplet
69
- local_version ? "#{local_os}-#{local_version}-#{local_cpu}" : "#{local_os}-#{local_cpu}"
72
+ def self.shared_lib_triplet(version: local_version)
73
+ version ? "#{local_os}-#{version}-#{local_cpu}" : "#{local_os}-#{local_cpu}"
70
74
  end
71
75
 
72
76
  def self.libddwaf_dir
73
- File.join(libddwaf_vendor_dir, "libddwaf-#{Datadog::AppSec::WAF::VERSION::BASE_STRING}-#{shared_lib_triplet}")
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
74
89
  end
75
90
 
76
91
  def self.shared_lib_extname
@@ -85,15 +100,7 @@ module Datadog
85
100
 
86
101
  # version
87
102
 
88
- class Version < ::FFI::Struct
89
- layout :major, :uint16,
90
- :minor, :uint16,
91
- :patch, :uint16
92
- end
93
-
94
- typedef Version.by_ref, :ddwaf_version
95
-
96
- attach_function :ddwaf_get_version, [:ddwaf_version], :void
103
+ attach_function :ddwaf_get_version, [], :string
97
104
 
98
105
  # ddwaf::object data structure
99
106
 
@@ -102,7 +109,9 @@ module Datadog
102
109
  :ddwaf_obj_unsigned, 1 << 1,
103
110
  :ddwaf_obj_string, 1 << 2,
104
111
  :ddwaf_obj_array, 1 << 3,
105
- :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
106
115
 
107
116
  typedef :pointer, :charptr
108
117
  typedef :pointer, :charptrptr
@@ -129,7 +138,8 @@ module Datadog
129
138
  layout :stringValue, :charptr,
130
139
  :uintValue, :uint64,
131
140
  :intValue, :int64,
132
- :array, :pointer
141
+ :array, :pointer,
142
+ :boolean, :bool
133
143
  end
134
144
 
135
145
  class Object < ::FFI::Struct
@@ -137,7 +147,7 @@ module Datadog
137
147
  :parameterNameLength, :uint64,
138
148
  :valueUnion, ObjectValueUnion,
139
149
  :nbEntries, :uint64,
140
- :type, DDWAF_OBJ_TYPE
150
+ :type, :ddwaf_obj_type
141
151
  end
142
152
 
143
153
  typedef Object.by_ref, :ddwaf_object
@@ -152,6 +162,7 @@ module Datadog
152
162
  attach_function :ddwaf_object_signed, [:ddwaf_object, :int64], :ddwaf_object
153
163
  attach_function :ddwaf_object_unsigned_force, [:ddwaf_object, :uint64], :ddwaf_object
154
164
  attach_function :ddwaf_object_signed_force, [:ddwaf_object, :int64], :ddwaf_object
165
+ attach_function :ddwaf_object_bool, [:ddwaf_object, :bool], :ddwaf_object
155
166
 
156
167
  attach_function :ddwaf_object_array, [:ddwaf_object], :ddwaf_object
157
168
  attach_function :ddwaf_object_array_add, [:ddwaf_object, :ddwaf_object], :bool
@@ -182,6 +193,8 @@ module Datadog
182
193
  typedef :pointer, :ddwaf_handle
183
194
  typedef Object.by_ref, :ddwaf_rule
184
195
 
196
+ callback :ddwaf_object_free_fn, [:ddwaf_object], :void
197
+
185
198
  class Config < ::FFI::Struct
186
199
  class Limits < ::FFI::Struct
187
200
  layout :max_container_size, :uint32,
@@ -195,7 +208,8 @@ module Datadog
195
208
  end
196
209
 
197
210
  layout :limits, Limits,
198
- :obfuscator, Obfuscator
211
+ :obfuscator, Obfuscator,
212
+ :free_fn, :pointer #:ddwaf_object_free_fn
199
213
  end
200
214
 
201
215
  typedef Config.by_ref, :ddwaf_config
@@ -216,33 +230,43 @@ module Datadog
216
230
  attach_function :ddwaf_destroy, [:ddwaf_handle], :void
217
231
 
218
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
219
246
 
220
247
  # running
221
248
 
222
249
  typedef :pointer, :ddwaf_context
223
250
 
224
- callback :ddwaf_object_free_fn, [:ddwaf_object], :void
225
-
226
- attach_function :ddwaf_context_init, [:ddwaf_handle, :ddwaf_object_free_fn], :ddwaf_context
251
+ attach_function :ddwaf_context_init, [:ddwaf_handle], :ddwaf_context
227
252
  attach_function :ddwaf_context_destroy, [:ddwaf_context], :void
228
253
 
229
- DDWAF_RET_CODE = enum :ddwaf_err_internal, -3,
230
- :ddwaf_err_invalid_object, -2,
231
- :ddwaf_err_invalid_argument, -1,
232
- :ddwaf_good, 0,
233
- :ddwaf_monitor, 1,
234
- :ddwaf_block, 2
254
+ class ResultActions < ::FFI::Struct
255
+ layout :array, :charptrptr,
256
+ :size, :uint32
257
+ end
235
258
 
236
259
  class Result < ::FFI::Struct
237
260
  layout :timeout, :bool,
238
261
  :data, :string,
262
+ :actions, ResultActions,
239
263
  :total_runtime, :uint64
240
264
  end
241
265
 
242
266
  typedef Result.by_ref, :ddwaf_result
243
267
  typedef :uint64, :timeout_us
244
268
 
245
- attach_function :ddwaf_run, [:ddwaf_context, :ddwaf_object, :ddwaf_result, :timeout_us], DDWAF_RET_CODE, blocking: true
269
+ attach_function :ddwaf_run, [:ddwaf_context, :ddwaf_object, :ddwaf_result, :timeout_us], :ddwaf_ret_code, blocking: true
246
270
  attach_function :ddwaf_result_free, [:ddwaf_result], :void
247
271
 
248
272
  # logging
@@ -253,20 +277,28 @@ module Datadog
253
277
  :ddwaf_log_warn,
254
278
  :ddwaf_log_error,
255
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
256
289
 
257
- callback :ddwaf_log_cb, [DDWAF_LOG_LEVEL, :string, :string, :uint, :charptr, :uint64], :void
290
+ DDWAF_MAX_CONTAINER_SIZE = 256
291
+ DDWAF_MAX_CONTAINER_DEPTH = 20
292
+ DDWAF_MAX_STRING_LENGTH = 4096
258
293
 
259
- attach_function :ddwaf_set_log_cb, [:ddwaf_log_cb, DDWAF_LOG_LEVEL], :bool
294
+ DDWAF_RUN_TIMEOUT = 5000
260
295
  end
261
296
 
262
297
  def self.version
263
- version = LibDDWAF::Version.new
264
- LibDDWAF.ddwaf_get_version(version.pointer)
265
-
266
- [version[:major], version[:minor], version[:patch]]
298
+ LibDDWAF.ddwaf_get_version
267
299
  end
268
300
 
269
- 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)
270
302
  case val
271
303
  when Array
272
304
  obj = LibDDWAF::Object.new
@@ -275,12 +307,20 @@ module Datadog
275
307
  fail LibDDWAF::Error, "Could not convert into object: #{val}"
276
308
  end
277
309
 
278
- val.each do |e|
279
- res = LibDDWAF.ddwaf_object_array_add(obj, ruby_to_object(e))
280
- unless res
281
- fail LibDDWAF::Error, "Could not add to map object: #{k.inspect} => #{v.inspect}"
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}"
282
320
  end
283
- end
321
+
322
+ break val if max_index && i >= max_index
323
+ end unless max_container_depth == 0
284
324
 
285
325
  obj
286
326
  when Hash
@@ -290,36 +330,56 @@ module Datadog
290
330
  fail LibDDWAF::Error, "Could not convert into object: #{val}"
291
331
  end
292
332
 
293
- val.each do |k, v|
294
- res = LibDDWAF.ddwaf_object_map_addl(obj, k.to_s, k.to_s.bytesize, ruby_to_object(v))
295
- unless res
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
296
345
  fail LibDDWAF::Error, "Could not add to map object: #{k.inspect} => #{v.inspect}"
297
346
  end
298
- end
347
+
348
+ break val if max_index && i >= max_index
349
+ end unless max_container_depth == 0
299
350
 
300
351
  obj
301
352
  when String
302
353
  obj = LibDDWAF::Object.new
303
- res = LibDDWAF.ddwaf_object_stringl(obj, val, val.bytesize)
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)
304
357
  if res.null?
305
- fail LibDDWAF::Error, "Could not convert into object: #{val}"
358
+ fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
306
359
  end
307
360
 
308
361
  obj
309
362
  when Symbol
310
363
  obj = LibDDWAF::Object.new
364
+ val = val.to_s[0, max_string_length] if max_string_length
311
365
  str = val.to_s
312
366
  res = LibDDWAF.ddwaf_object_stringl(obj, str, str.bytesize)
313
367
  if res.null?
314
- fail LibDDWAF::Error, "Could not convert into object: #{val}"
368
+ fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
315
369
  end
316
370
 
317
371
  obj
318
372
  when Integer
319
373
  obj = LibDDWAF::Object.new
320
- res = LibDDWAF.ddwaf_object_string(obj, val.to_s)
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
321
381
  if res.null?
322
- fail LibDDWAF::Error, "Could not convert into object: #{val}"
382
+ fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
323
383
  end
324
384
 
325
385
  obj
@@ -327,15 +387,19 @@ module Datadog
327
387
  obj = LibDDWAF::Object.new
328
388
  res = LibDDWAF.ddwaf_object_string(obj, val.to_s)
329
389
  if res.null?
330
- fail LibDDWAF::Error, "Could not convert into object: #{val}"
390
+ fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
331
391
  end
332
392
 
333
393
  obj
334
394
  when TrueClass, FalseClass
335
395
  obj = LibDDWAF::Object.new
336
- res = LibDDWAF.ddwaf_object_string(obj, val.to_s)
396
+ res = if coerce
397
+ LibDDWAF.ddwaf_object_string(obj, val.to_s)
398
+ else
399
+ LibDDWAF.ddwaf_object_bool(obj, val)
400
+ end
337
401
  if res.null?
338
- fail LibDDWAF::Error, "Could not convert into object: #{val}"
402
+ fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
339
403
  end
340
404
 
341
405
  obj
@@ -348,6 +412,8 @@ module Datadog
348
412
  case obj[:type]
349
413
  when :ddwaf_obj_invalid
350
414
  nil
415
+ when :ddwaf_obj_bool
416
+ obj[:valueUnion][:boolean]
351
417
  when :ddwaf_obj_string
352
418
  obj[:valueUnion][:stringValue].read_bytes(obj[:nbEntries])
353
419
  when :ddwaf_obj_signed
@@ -372,21 +438,47 @@ module Datadog
372
438
  end
373
439
  end
374
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
+
375
459
  def self.logger=(logger)
376
- @log_cb = proc do |level, func, file, line, message, len|
377
- logger.debug { { level: level, func: func, file: file, line: line, message: message.read_bytes(len) }.inspect }
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
378
466
  end
379
467
 
380
- Datadog::AppSec::WAF::LibDDWAF.ddwaf_set_log_cb(@log_cb, :ddwaf_log_trace)
468
+ @logger = logger
381
469
  end
382
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
+
383
479
  class Handle
384
480
  attr_reader :handle_obj
385
481
 
386
- DEFAULT_MAX_CONTAINER_SIZE = 0
387
- DEFAULT_MAX_CONTAINER_DEPTH = 0
388
- DEFAULT_MAX_STRING_LENGTH = 0
389
-
390
482
  attr_reader :ruleset_info
391
483
 
392
484
  def initialize(rule, limits: {}, obfuscator: {})
@@ -399,12 +491,14 @@ module Datadog
399
491
  if config_obj.null?
400
492
  fail LibDDWAF::Error, 'Could not create config struct'
401
493
  end
494
+ retain(config_obj)
402
495
 
403
- config_obj[:limits][:max_container_size] = limits[:max_container_size] || DEFAULT_MAX_CONTAINER_SIZE
404
- config_obj[:limits][:max_container_depth] = limits[:max_container_depth] || DEFAULT_MAX_CONTAINER_DEPTH
405
- config_obj[:limits][:max_string_length] = limits[:max_string_length] || DEFAULT_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
406
499
  config_obj[:obfuscator][:key_regex] = FFI::MemoryPointer.from_string(obfuscator[:key_regex]) if obfuscator[:key_regex]
407
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
408
502
 
409
503
  ruleset_info = LibDDWAF::RuleSetInfo.new
410
504
 
@@ -421,19 +515,21 @@ module Datadog
421
515
  fail LibDDWAF::Error.new('Could not create handle', ruleset_info: @ruleset_info)
422
516
  end
423
517
 
424
- ObjectSpace.define_finalizer(self, Handle.finalizer(handle_obj))
518
+ validate!
425
519
  ensure
426
520
  Datadog::AppSec::WAF::LibDDWAF.ddwaf_ruleset_info_free(ruleset_info) if ruleset_info
427
521
  Datadog::AppSec::WAF::LibDDWAF.ddwaf_object_free(rule_obj) if rule_obj
428
522
  end
429
523
 
430
- def self.finalizer(handle_obj)
431
- proc do |object_id|
432
- Datadog::AppSec::WAF::LibDDWAF.ddwaf_destroy(handle_obj)
433
- end
524
+ def finalize
525
+ invalidate!
526
+
527
+ Datadog::AppSec::WAF::LibDDWAF.ddwaf_destroy(handle_obj)
434
528
  end
435
529
 
436
530
  def required_addresses
531
+ valid!
532
+
437
533
  count = Datadog::AppSec::WAF::LibDDWAF::UInt32Ptr.new
438
534
  list = Datadog::AppSec::WAF::LibDDWAF.ddwaf_required_addresses(handle_obj, count)
439
535
 
@@ -441,48 +537,102 @@ module Datadog
441
537
 
442
538
  list.get_array_of_string(0, count[:value])
443
539
  end
540
+
541
+ def update_rule_data(data)
542
+ res = Datadog::AppSec::WAF::LibDDWAF.ddwaf_update_rule_data(@handle_obj, Datadog::AppSec::WAF.ruby_to_object(data, coerce: false))
543
+
544
+ RESULT_CODE[res]
545
+ end
546
+
547
+ def toggle_rules(map)
548
+ res = Datadog::AppSec::WAF::LibDDWAF.ddwaf_toggle_rules(@handle_obj, Datadog::AppSec::WAF.ruby_to_object(map, coerce: false))
549
+
550
+ RESULT_CODE[res]
551
+ end
552
+
553
+ private
554
+
555
+ def validate!
556
+ @valid = true
557
+ end
558
+
559
+ def invalidate!
560
+ @valid = false
561
+ end
562
+
563
+ def valid?
564
+ @valid
565
+ end
566
+
567
+ def valid!
568
+ return if valid?
569
+
570
+ fail LibDDWAF::Error, "Attempt to use an invalid instance: #{inspect}"
571
+ end
572
+
573
+ def retained
574
+ @retained ||= []
575
+ end
576
+
577
+ def retain(object)
578
+ retained << object
579
+ end
580
+
581
+ def release(object)
582
+ retained.delete(object)
583
+ end
444
584
  end
445
585
 
446
- Result = Struct.new(:action, :data, :total_runtime, :timeout)
586
+ class Result
587
+ attr_reader :status, :data, :total_runtime, :timeout, :actions
588
+
589
+ def initialize(status, data, total_runtime, timeout, actions)
590
+ @status = status
591
+ @data = data
592
+ @total_runtime = total_runtime
593
+ @timeout = timeout
594
+ @actions = actions
595
+ end
596
+ end
447
597
 
448
598
  class Context
449
599
  attr_reader :context_obj
450
600
 
451
601
  def initialize(handle)
452
602
  handle_obj = handle.handle_obj
453
- free_func = Datadog::AppSec::WAF::LibDDWAF::ObjectNoFree
603
+ retain(handle)
454
604
 
455
- @context_obj = Datadog::AppSec::WAF::LibDDWAF.ddwaf_context_init(handle_obj, free_func)
605
+ @context_obj = Datadog::AppSec::WAF::LibDDWAF.ddwaf_context_init(handle_obj)
456
606
  if @context_obj.null?
457
607
  fail LibDDWAF::Error, 'Could not create context'
458
608
  end
459
609
 
460
- @input_objs = []
461
-
462
- ObjectSpace.define_finalizer(self, Context.finalizer(context_obj, @input_objs))
610
+ validate!
463
611
  end
464
612
 
465
- def self.finalizer(context_obj, input_objs)
466
- proc do |object_id|
467
- input_objs.each do |input_obj|
468
- Datadog::AppSec::WAF::LibDDWAF.ddwaf_object_free(input_obj)
469
- end
470
- Datadog::AppSec::WAF::LibDDWAF.ddwaf_context_destroy(context_obj)
613
+ def finalize
614
+ invalidate!
615
+
616
+ retained.each do |retained_obj|
617
+ next unless retained_obj.is_a?(Datadog::AppSec::WAF::LibDDWAF::Object)
618
+
619
+ Datadog::AppSec::WAF::LibDDWAF.ddwaf_object_free(retained_obj)
471
620
  end
621
+
622
+ Datadog::AppSec::WAF::LibDDWAF.ddwaf_context_destroy(context_obj)
472
623
  end
473
624
 
474
- DEFAULT_TIMEOUT_US = 10_0000
475
- ACTION_MAP_OUT = {
476
- ddwaf_err_internal: :err_internal,
477
- ddwaf_err_invalid_object: :err_invalid_object,
478
- ddwaf_err_invalid_argument: :err_invalid_argument,
479
- ddwaf_good: :good,
480
- ddwaf_monitor: :monitor,
481
- ddwaf_block: :block,
482
- }
625
+ def run(input, timeout = LibDDWAF::DDWAF_RUN_TIMEOUT)
626
+ valid!
627
+
628
+ max_container_size = LibDDWAF::DDWAF_MAX_CONTAINER_SIZE
629
+ max_container_depth = LibDDWAF::DDWAF_MAX_CONTAINER_DEPTH
630
+ max_string_length = LibDDWAF::DDWAF_MAX_CONTAINER_SIZE
483
631
 
484
- def run(input, timeout = DEFAULT_TIMEOUT_US)
485
- input_obj = Datadog::AppSec::WAF.ruby_to_object(input)
632
+ input_obj = Datadog::AppSec::WAF.ruby_to_object(input,
633
+ max_container_size: max_container_size,
634
+ max_container_depth: max_container_depth,
635
+ max_string_length: max_string_length)
486
636
  if input_obj.null?
487
637
  fail LibDDWAF::Error, "Could not convert input: #{input.inspect}"
488
638
  end
@@ -493,21 +643,60 @@ module Datadog
493
643
  end
494
644
 
495
645
  # retain C objects in memory for subsequent calls to run
496
- @input_objs << input_obj
646
+ retain(input_obj)
497
647
 
498
648
  code = Datadog::AppSec::WAF::LibDDWAF.ddwaf_run(@context_obj, input_obj, result_obj, timeout)
499
649
 
650
+ actions = if result_obj[:actions][:size] > 0
651
+ result_obj[:actions][:array].get_array_of_string(0, result_obj[:actions][:size])
652
+ else
653
+ []
654
+ end
655
+
500
656
  result = Result.new(
501
- ACTION_MAP_OUT[code],
657
+ RESULT_CODE[code],
502
658
  (JSON.parse(result_obj[:data]) if result_obj[:data] != nil),
503
659
  result_obj[:total_runtime],
504
660
  result_obj[:timeout],
661
+ actions,
505
662
  )
506
663
 
507
- [ACTION_MAP_OUT[code], result]
664
+ [RESULT_CODE[code], result]
508
665
  ensure
509
666
  Datadog::AppSec::WAF::LibDDWAF.ddwaf_result_free(result_obj) if result_obj
510
667
  end
668
+
669
+ private
670
+
671
+ def validate!
672
+ @valid = true
673
+ end
674
+
675
+ def invalidate!
676
+ @valid = false
677
+ end
678
+
679
+ def valid?
680
+ @valid
681
+ end
682
+
683
+ def valid!
684
+ return if valid?
685
+
686
+ fail LibDDWAF::Error, "Attempt to use an invalid instance: #{inspect}"
687
+ end
688
+
689
+ def retained
690
+ @retained ||= []
691
+ end
692
+
693
+ def retain(object)
694
+ retained << object
695
+ end
696
+
697
+ def release(object)
698
+ retained.delete(object)
699
+ end
511
700
  end
512
701
  end
513
702
  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)
@@ -0,0 +1,13 @@
1
+ module Datadog
2
+ module AppSec
3
+ module WAF
4
+ module VERSION
5
+ BASE_STRING: ::String
6
+
7
+ STRING: ::String
8
+
9
+ MINIMUM_RUBY_VERSION: ::String
10
+ end
11
+ end
12
+ end
13
+ end