libddwaf 1.3.0.2.0-x86_64-linux → 1.5.1.0.0-x86_64-linux

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3feabb5073c208b36d6fe82e1b35c3abe1bb452130018507004d49c763c12af4
4
- data.tar.gz: 3b47d976c8fb5da977d16397702c27ea77398c8a71c1416d10da2737288dcba8
3
+ metadata.gz: 88cf954481690b99c0366c0321e9e61207a400f77f4f34728eed53c02fff002b
4
+ data.tar.gz: d219437677e5808de8d8cc8f1854bc9f9cce1888fe5d48e2ae15392568657db5
5
5
  SHA512:
6
- metadata.gz: 6ba106c7714857bfdf784b7b96a78580f67f80827dbfa7268fa52396142821490fe301066492f5ab88e590c37e6d3f1901f71637c75da9688b0dfa276d6173e1
7
- data.tar.gz: b2c72bd3c8508f9ceff51d4bcfd733366c51a7f29a4df77d2a75901ac5739a2eaf7bb0f98f56e642ce317d1075522a55fa21070f13ca5d1a5088c64617a02294
6
+ metadata.gz: 26628b2cbda3191f21a7f94f81a90826cac2a760e75fb5de9886b953b22cf452f1c48c6b40a246548a96718c25e0349aae13d8934afb3c538608d6c6d24c9e9c
7
+ data.tar.gz: '095b73dd20e5f097cd397e8546e11a1520ea4d1dca5d75c183652aa847e7ed4fa627d9aaaf874527f7eebcdbf64b86ac0d498cf5605dd3d1ccb6531258169e3a'
data/CHANGELOG.md ADDED
@@ -0,0 +1,74 @@
1
+ # 2022-10-04 v1.5.1.0.0
2
+
3
+ - Update to libddwaf 1.5.1
4
+ - Add live rule data update API
5
+ - Add live rule toggle API
6
+ - Add libddwaf boolean type support
7
+ - Add Ruby to libddwaf object conversion limits
8
+ - Add Ruby to libddwaf object converter optional coercion of scalars to string
9
+ - Add static type checking via RBS+Steep
10
+ - Change version to return a string
11
+ - Change free function to be passed as config instead of context init argument
12
+ - Change result to include action list
13
+ - Change return code from action to status
14
+ - Change handle and context freeing model from GC-based to explicit
15
+ - Fix double-free upon finalization of retained C objects
16
+ - Fix context crash by retaining necessary C objects
17
+
18
+ # 2022-05-20 v1.3.0.2.0
19
+
20
+ - Fix multibyte string handling
21
+ - Support JRuby
22
+
23
+ # 2022-04-29 v1.3.0.1.0
24
+
25
+ Promotion of v1.3.0.1.0.beta1 to stable
26
+
27
+ # 2022-04-25 v1.3.0.1.0.beta1
28
+
29
+ - Add obfuscator configuration
30
+ - Add nested object limit configuration
31
+ - Add report ruleset information
32
+
33
+ # 2022-04-29 v1.3.0.0.0
34
+
35
+ - Promote v1.3.0.0.0.beta1 to stable
36
+
37
+ # 2022-04-20 v1.3.0.0.0.beta1
38
+
39
+ - Update to libddwaf 1.3.0
40
+
41
+ # 2022-03-18 v1.2.1.0.0.beta1
42
+
43
+ - Update to libddwaf 1.2.1
44
+ - Fix incorrect types for a few binding functions
45
+
46
+ # 2022-03-04 v1.0.14.2.1.beta1
47
+
48
+ - Fix incorrect return code
49
+ - Fix passing nil in libddwaf object containers
50
+
51
+ # 2022-02-07 v1.0.14.2.0.beta1
52
+
53
+ - Change Datadog::Security to Datadog::AppSec
54
+
55
+ # 2022-02-01 v1.0.14.1.0.beta2
56
+
57
+ - Add support for Ruby 3.1
58
+
59
+ # 2021-12-14 v1.0.14.1.0.beta1
60
+
61
+ - Fix sequential runs on a single context by retaining C input data objects
62
+
63
+ # 2021-11-24 v1.0.14.0.0.beta1
64
+
65
+ - Update to libddwaf 1.0.14
66
+
67
+ # 2021-11-24 v1.0.13.0.0.beta1
68
+
69
+ - Add ruby platform fallback for unsupported platforms
70
+ - Update to libddwaf 1.0.13
71
+
72
+ # 2021-10-13 v1.0.12.0.0.beta1
73
+
74
+ - Initial release
@@ -2,8 +2,8 @@ module Datadog
2
2
  module AppSec
3
3
  module WAF
4
4
  module VERSION
5
- BASE_STRING = '1.3.0'
6
- STRING = "#{BASE_STRING}.2.0"
5
+ BASE_STRING = '1.5.1'
6
+ STRING = "#{BASE_STRING}.0.0"
7
7
  MINIMUM_RUBY_VERSION = '2.1'
8
8
  end
9
9
  end
@@ -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
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: libddwaf
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0.2.0
4
+ version: 1.5.1.0.0
5
5
  platform: x86_64-linux
6
6
  authors:
7
7
  - Datadog, Inc.
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 1980-01-01 00:00:00.000000000 Z
11
+ date: 2022-10-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -33,6 +33,7 @@ executables: []
33
33
  extensions: []
34
34
  extra_rdoc_files: []
35
35
  files:
36
+ - CHANGELOG.md
36
37
  - LICENSE
37
38
  - LICENSE-3rdparty.csv
38
39
  - LICENSE.Apache
@@ -41,14 +42,13 @@ files:
41
42
  - lib/datadog/appsec/waf.rb
42
43
  - lib/datadog/appsec/waf/version.rb
43
44
  - lib/libddwaf.rb
44
- - vendor/libddwaf/libddwaf-1.3.0-linux-gnu-x86_64/lib/libddwaf.so
45
- - vendor/libddwaf/libddwaf-1.3.0-linux-musl-x86_64/lib/libddwaf.so
46
- homepage: https://github.com/DataDog/libddwaf
45
+ - vendor/libddwaf/libddwaf-1.5.1-linux-x86_64/lib/libddwaf.so
46
+ homepage: https://github.com/DataDog/libddwaf-rb
47
47
  licenses:
48
48
  - BSD-3-Clause
49
49
  metadata:
50
50
  allowed_push_host: https://rubygems.org
51
- post_install_message:
51
+ post_install_message:
52
52
  rdoc_options: []
53
53
  require_paths:
54
54
  - lib
@@ -63,8 +63,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
63
63
  - !ruby/object:Gem::Version
64
64
  version: 2.0.0
65
65
  requirements: []
66
- rubygems_version: 3.2.26
67
- signing_key:
66
+ rubygems_version: 3.1.2
67
+ signing_key:
68
68
  specification_version: 4
69
69
  summary: Datadog WAF
70
70
  test_files: []