libddwaf 1.3.0.2.0-x86_64-darwin → 1.5.1.0.1-x86_64-darwin

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: cd64af8282e39c80685231bc47387ed3c685dce9a927e05ce8dfadc995dcc7a0
4
- data.tar.gz: f3db575f44ce5bb8ecebf1732b77aa4ea5ae0350f053402f03f833a00280e869
3
+ metadata.gz: e0a596d9f94ee5aed556adfa96b6e005e5e49754c4bbcfb9b58becc61e97fd41
4
+ data.tar.gz: 7c4d3f254272fcca470bb6d6d57c4b08ee9a7af6dc0a3e8a691d73399ed2eab1
5
5
  SHA512:
6
- metadata.gz: 3418e4f2bfb0b9c73cce9c7bc6fa8594fbdec009e01d59b210480484306982e7ae798c3ffdc25984cc1fc8dea4a50fc771fa7c924eae2af37719b4e029ad1372
7
- data.tar.gz: 7fcb30f7a97d1c31abb2c26746eb8b60f637cdcbcfb05fe6fcfbe3fcc1fff73336c3d6c429e2f2c579cd4d12ce01e3b30b16f18c8287c31f8fc0b6454e357898
6
+ metadata.gz: c39d0828579f5d75ccdf6bd73e986fde409abdb0aa82d47fe8a667efa79e316c109fd65691471718fc0e2dc03ef6f6aea5b36b4518cdd9cd8149760372f86f60
7
+ data.tar.gz: '085b54fe799d706da464a5b4488d25c2b2e3529d1db235d1900a5b0e0567f0f92c12246d51350b1a6e97dfcc83a4a655794c2bfc498295535179483d54be6d65'
data/CHANGELOG.md ADDED
@@ -0,0 +1,79 @@
1
+ # 2023-02-01 v1.5.1.0.1
2
+
3
+ - Fix incorrect size in input string limit
4
+ - Fix object freeing on `update_rule_data` and `toggle_rules`
5
+
6
+ # 2022-10-04 v1.5.1.0.0
7
+
8
+ - Update to libddwaf 1.5.1
9
+ - Add live rule data update API
10
+ - Add live rule toggle API
11
+ - Add libddwaf boolean type support
12
+ - Add Ruby to libddwaf object conversion limits
13
+ - Add Ruby to libddwaf object converter optional coercion of scalars to string
14
+ - Add static type checking via RBS+Steep
15
+ - Change version to return a string
16
+ - Change free function to be passed as config instead of context init argument
17
+ - Change result to include action list
18
+ - Change return code from action to status
19
+ - Change handle and context freeing model from GC-based to explicit
20
+ - Fix double-free upon finalization of retained C objects
21
+ - Fix context crash by retaining necessary C objects
22
+
23
+ # 2022-05-20 v1.3.0.2.0
24
+
25
+ - Fix multibyte string handling
26
+ - Support JRuby
27
+
28
+ # 2022-04-29 v1.3.0.1.0
29
+
30
+ Promotion of v1.3.0.1.0.beta1 to stable
31
+
32
+ # 2022-04-25 v1.3.0.1.0.beta1
33
+
34
+ - Add obfuscator configuration
35
+ - Add nested object limit configuration
36
+ - Add report ruleset information
37
+
38
+ # 2022-04-29 v1.3.0.0.0
39
+
40
+ - Promote v1.3.0.0.0.beta1 to stable
41
+
42
+ # 2022-04-20 v1.3.0.0.0.beta1
43
+
44
+ - Update to libddwaf 1.3.0
45
+
46
+ # 2022-03-18 v1.2.1.0.0.beta1
47
+
48
+ - Update to libddwaf 1.2.1
49
+ - Fix incorrect types for a few binding functions
50
+
51
+ # 2022-03-04 v1.0.14.2.1.beta1
52
+
53
+ - Fix incorrect return code
54
+ - Fix passing nil in libddwaf object containers
55
+
56
+ # 2022-02-07 v1.0.14.2.0.beta1
57
+
58
+ - Change Datadog::Security to Datadog::AppSec
59
+
60
+ # 2022-02-01 v1.0.14.1.0.beta2
61
+
62
+ - Add support for Ruby 3.1
63
+
64
+ # 2021-12-14 v1.0.14.1.0.beta1
65
+
66
+ - Fix sequential runs on a single context by retaining C input data objects
67
+
68
+ # 2021-11-24 v1.0.14.0.0.beta1
69
+
70
+ - Update to libddwaf 1.0.14
71
+
72
+ # 2021-11-24 v1.0.13.0.0.beta1
73
+
74
+ - Add ruby platform fallback for unsupported platforms
75
+ - Update to libddwaf 1.0.13
76
+
77
+ # 2021-10-13 v1.0.12.0.0.beta1
78
+
79
+ - 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.1"
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,
@@ -190,12 +203,13 @@ module Datadog
190
203
  end
191
204
 
192
205
  class Obfuscator < ::FFI::Struct
193
- layout :key_regex, :pointer, # :charptr
194
- :value_regex, :pointer # :charptr
206
+ layout :key_regex, :pointer, # should be :charptr
207
+ :value_regex, :pointer # should be :charptr
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,108 @@ 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
+ 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)
589
+ end
444
590
  end
445
591
 
446
- Result = Struct.new(:action, :data, :total_runtime, :timeout)
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
447
603
 
448
604
  class Context
449
605
  attr_reader :context_obj
450
606
 
451
607
  def initialize(handle)
452
608
  handle_obj = handle.handle_obj
453
- free_func = Datadog::AppSec::WAF::LibDDWAF::ObjectNoFree
609
+ retain(handle)
454
610
 
455
- @context_obj = Datadog::AppSec::WAF::LibDDWAF.ddwaf_context_init(handle_obj, free_func)
611
+ @context_obj = Datadog::AppSec::WAF::LibDDWAF.ddwaf_context_init(handle_obj)
456
612
  if @context_obj.null?
457
613
  fail LibDDWAF::Error, 'Could not create context'
458
614
  end
459
615
 
460
- @input_objs = []
461
-
462
- ObjectSpace.define_finalizer(self, Context.finalizer(context_obj, @input_objs))
616
+ validate!
463
617
  end
464
618
 
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)
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)
471
626
  end
627
+
628
+ Datadog::AppSec::WAF::LibDDWAF.ddwaf_context_destroy(context_obj)
472
629
  end
473
630
 
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
- }
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
483
637
 
484
- def run(input, timeout = DEFAULT_TIMEOUT_US)
485
- input_obj = Datadog::AppSec::WAF.ruby_to_object(input)
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)
486
642
  if input_obj.null?
487
643
  fail LibDDWAF::Error, "Could not convert input: #{input.inspect}"
488
644
  end
@@ -493,21 +649,60 @@ module Datadog
493
649
  end
494
650
 
495
651
  # retain C objects in memory for subsequent calls to run
496
- @input_objs << input_obj
652
+ retain(input_obj)
497
653
 
498
654
  code = Datadog::AppSec::WAF::LibDDWAF.ddwaf_run(@context_obj, input_obj, result_obj, timeout)
499
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
+
500
662
  result = Result.new(
501
- ACTION_MAP_OUT[code],
663
+ RESULT_CODE[code],
502
664
  (JSON.parse(result_obj[:data]) if result_obj[:data] != nil),
503
665
  result_obj[:total_runtime],
504
666
  result_obj[:timeout],
667
+ actions,
505
668
  )
506
669
 
507
- [ACTION_MAP_OUT[code], result]
670
+ [RESULT_CODE[code], result]
508
671
  ensure
509
672
  Datadog::AppSec::WAF::LibDDWAF.ddwaf_result_free(result_obj) if result_obj
510
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
511
706
  end
512
707
  end
513
708
  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.1
5
5
  platform: x86_64-darwin
6
6
  authors:
7
7
  - Datadog, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 1980-01-01 00:00:00.000000000 Z
11
+ date: 2023-02-01 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,8 +42,8 @@ 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-darwin-x86_64/lib/libddwaf.dylib
45
- homepage: https://github.com/DataDog/libddwaf
45
+ - vendor/libddwaf/libddwaf-1.5.1-darwin-x86_64/lib/libddwaf.dylib
46
+ homepage: https://github.com/DataDog/libddwaf-rb
46
47
  licenses:
47
48
  - BSD-3-Clause
48
49
  metadata:
@@ -62,7 +63,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
62
63
  - !ruby/object:Gem::Version
63
64
  version: 2.0.0
64
65
  requirements: []
65
- rubygems_version: 3.2.26
66
+ rubygems_version: 3.4.4
66
67
  signing_key:
67
68
  specification_version: 4
68
69
  summary: Datadog WAF