libddwaf 1.15.0.0.2 → 1.18.0.0.0

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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug.md +32 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +17 -0
  4. data/.github/actions/docker-build-ruby/Dockerfile +5 -0
  5. data/.github/actions/docker-build-ruby/Dockerfile.alpine +7 -0
  6. data/.github/actions/docker-build-ruby/Dockerfile.jruby +6 -0
  7. data/.github/actions/docker-build-ruby/action.yml +57 -0
  8. data/.github/workflows/lint.yml +34 -0
  9. data/.github/workflows/package.yml +134 -0
  10. data/.github/workflows/test.yml +118 -0
  11. data/.gitignore +10 -0
  12. data/.steepignore +4 -0
  13. data/CONTRIBUTING.md +84 -0
  14. data/Steepfile +21 -0
  15. data/lib/datadog/appsec/waf/context.rb +122 -0
  16. data/lib/datadog/appsec/waf/converter.rb +172 -0
  17. data/lib/datadog/appsec/waf/handle.rb +108 -0
  18. data/lib/datadog/appsec/waf/lib_ddwaf.rb +307 -0
  19. data/lib/datadog/appsec/waf/result.rb +33 -0
  20. data/lib/datadog/appsec/waf/version.rb +2 -2
  21. data/lib/datadog/appsec/waf.rb +17 -682
  22. data/libddwaf.gemspec +26 -29
  23. data/shell.nix +35 -0
  24. data/sig/datadog/appsec/waf/context.rbs +39 -0
  25. data/sig/datadog/appsec/waf/converter.rbs +11 -0
  26. data/sig/datadog/appsec/waf/handle.rbs +42 -0
  27. data/sig/datadog/appsec/waf/lib_ddwaf.rbs +156 -0
  28. data/sig/datadog/appsec/waf/result.rbs +33 -0
  29. data/sig/datadog/appsec/waf.rbs +1 -213
  30. metadata +27 -7
  31. data/vendor/libddwaf/libddwaf-1.15.0-darwin-arm64/lib/libddwaf.dylib +0 -0
  32. data/vendor/libddwaf/libddwaf-1.15.0-darwin-x86_64/lib/libddwaf.dylib +0 -0
  33. data/vendor/libddwaf/libddwaf-1.15.0-linux-aarch64/lib/libddwaf.so +0 -0
  34. data/vendor/libddwaf/libddwaf-1.15.0-linux-x86_64/lib/libddwaf.so +0 -0
@@ -1,461 +1,26 @@
1
- require 'ffi'
2
- require 'json'
1
+ # frozen_string_literal: true
2
+
3
+ require 'datadog/appsec/waf/lib_ddwaf'
4
+
5
+ require 'datadog/appsec/waf/converter'
6
+ require 'datadog/appsec/waf/result'
7
+ require 'datadog/appsec/waf/context'
8
+ require 'datadog/appsec/waf/handle'
3
9
  require 'datadog/appsec/waf/version'
4
10
 
5
11
  module Datadog
6
12
  module AppSec
7
- # rubocop:disable Metrics/ModuleLength
8
13
  module WAF
9
- module LibDDWAF
10
- class Error < StandardError
11
- attr_reader :diagnostics
12
-
13
- def initialize(msg, diagnostics: nil)
14
- @diagnostics = diagnostics
15
- end
16
- end
17
-
18
- extend ::FFI::Library
19
-
20
- def self.local_os
21
- if RUBY_ENGINE == 'jruby'
22
- os_name = java.lang.System.get_property('os.name')
23
-
24
- os = case os_name
25
- when /linux/i then 'linux'
26
- when /mac/i then 'darwin'
27
- else raise Error, "unsupported JRuby os.name: #{os_name.inspect}"
28
- end
29
-
30
- return os
31
- end
32
-
33
- Gem::Platform.local.os
34
- end
35
-
36
- def self.local_version
37
- return nil unless local_os == 'linux'
38
-
39
- # Old rubygems don't handle non-gnu linux correctly
40
- return $1 if RUBY_PLATFORM =~ /linux-(.+)$/
41
-
42
- 'gnu'
43
- end
44
-
45
- def self.local_cpu
46
- if RUBY_ENGINE == 'jruby'
47
- os_arch = java.lang.System.get_property('os.arch')
48
-
49
- cpu = case os_arch
50
- when 'amd64' then 'x86_64'
51
- when 'aarch64' then 'aarch64'
52
- else raise Error, "unsupported JRuby os.arch: #{os_arch.inspect}"
53
- end
54
-
55
- return cpu
56
- end
57
-
58
- Gem::Platform.local.cpu
59
- end
60
-
61
- def self.source_dir
62
- __dir__ || raise('__dir__ is nil: eval?')
63
- end
64
-
65
- def self.vendor_dir
66
- File.join(source_dir, '../../../vendor')
67
- end
68
-
69
- def self.libddwaf_vendor_dir
70
- File.join(vendor_dir, 'libddwaf')
71
- end
72
-
73
- def self.shared_lib_triplet(version: local_version)
74
- version ? "#{local_os}-#{version}-#{local_cpu}" : "#{local_os}-#{local_cpu}"
75
- end
76
-
77
- def self.libddwaf_dir
78
- default = File.join(libddwaf_vendor_dir,
79
- "libddwaf-#{Datadog::AppSec::WAF::VERSION::BASE_STRING}-#{shared_lib_triplet}")
80
- candidates = [
81
- default
82
- ]
83
-
84
- if local_os == 'linux'
85
- candidates << File.join(libddwaf_vendor_dir,
86
- "libddwaf-#{Datadog::AppSec::WAF::VERSION::BASE_STRING}-#{shared_lib_triplet(version: nil)}")
87
- end
88
-
89
- candidates.find { |d| Dir.exist?(d) } || default
90
- end
91
-
92
- def self.shared_lib_extname
93
- Gem::Platform.local.os == 'darwin' ? '.dylib' : '.so'
94
- end
95
-
96
- def self.shared_lib_path
97
- variant = "#{Datadog::AppSec::WAF::VERSION::BASE_STRING}-#{local_os}-#{local_cpu}"
98
- libddwaf_dir = File.join(source_dir, "../../../vendor/libddwaf/libddwaf-#{variant}")
99
-
100
- File.join(libddwaf_dir, "lib", "libddwaf#{shared_lib_extname}")
101
- end
102
-
103
- ffi_lib [shared_lib_path]
104
-
105
- # version
106
-
107
- attach_function :ddwaf_get_version, [], :string
108
-
109
- # ddwaf::object data structure
110
-
111
- DDWAF_OBJ_TYPE = enum :ddwaf_obj_invalid, 0,
112
- :ddwaf_obj_signed, 1 << 0,
113
- :ddwaf_obj_unsigned, 1 << 1,
114
- :ddwaf_obj_string, 1 << 2,
115
- :ddwaf_obj_array, 1 << 3,
116
- :ddwaf_obj_map, 1 << 4,
117
- :ddwaf_obj_bool, 1 << 5,
118
- :ddwaf_obj_float, 1 << 6,
119
- :ddwaf_obj_null, 1 << 7
120
-
121
- typedef DDWAF_OBJ_TYPE, :ddwaf_obj_type
122
-
123
- typedef :pointer, :charptr
124
- typedef :pointer, :charptrptr
125
-
126
- class UInt32Ptr < ::FFI::Struct
127
- layout :value, :uint32
128
- end
129
-
130
- typedef UInt32Ptr.by_ref, :uint32ptr
131
-
132
- class UInt64Ptr < ::FFI::Struct
133
- layout :value, :uint64
134
- end
135
-
136
- typedef UInt64Ptr.by_ref, :uint64ptr
137
-
138
- class SizeTPtr < ::FFI::Struct
139
- layout :value, :size_t
140
- end
141
-
142
- typedef SizeTPtr.by_ref, :sizeptr
143
-
144
- class ObjectValueUnion < ::FFI::Union
145
- layout :stringValue, :charptr,
146
- :uintValue, :uint64,
147
- :intValue, :int64,
148
- :array, :pointer,
149
- :boolean, :bool,
150
- :f64, :double
151
- end
152
-
153
- class Object < ::FFI::Struct
154
- layout :parameterName, :charptr,
155
- :parameterNameLength, :uint64,
156
- :valueUnion, ObjectValueUnion,
157
- :nbEntries, :uint64,
158
- :type, :ddwaf_obj_type
159
- end
160
-
161
- typedef Object.by_ref, :ddwaf_object
162
-
163
- ## setters
164
-
165
- attach_function :ddwaf_object_invalid, [:ddwaf_object], :ddwaf_object
166
- attach_function :ddwaf_object_string, [:ddwaf_object, :string], :ddwaf_object
167
- attach_function :ddwaf_object_stringl, [:ddwaf_object, :charptr, :size_t], :ddwaf_object
168
- attach_function :ddwaf_object_stringl_nc, [:ddwaf_object, :charptr, :size_t], :ddwaf_object
169
- attach_function :ddwaf_object_string_from_unsigned, [:ddwaf_object, :uint64], :ddwaf_object
170
- attach_function :ddwaf_object_string_from_signed, [:ddwaf_object, :int64], :ddwaf_object
171
- attach_function :ddwaf_object_unsigned, [:ddwaf_object, :uint64], :ddwaf_object
172
- attach_function :ddwaf_object_signed, [:ddwaf_object, :int64], :ddwaf_object
173
- attach_function :ddwaf_object_bool, [:ddwaf_object, :bool], :ddwaf_object
174
- attach_function :ddwaf_object_null, [:ddwaf_object], :ddwaf_object
175
- attach_function :ddwaf_object_float, [:ddwaf_object, :double], :ddwaf_object
14
+ module_function
176
15
 
177
- attach_function :ddwaf_object_array, [:ddwaf_object], :ddwaf_object
178
- attach_function :ddwaf_object_array_add, [:ddwaf_object, :ddwaf_object], :bool
179
-
180
- attach_function :ddwaf_object_map, [:ddwaf_object], :ddwaf_object
181
- attach_function :ddwaf_object_map_add, [:ddwaf_object, :string, :pointer], :bool
182
- attach_function :ddwaf_object_map_addl, [:ddwaf_object, :charptr, :size_t, :pointer], :bool
183
- attach_function :ddwaf_object_map_addl_nc, [:ddwaf_object, :charptr, :size_t, :pointer], :bool
184
-
185
- ## getters
186
-
187
- attach_function :ddwaf_object_type, [:ddwaf_object], DDWAF_OBJ_TYPE
188
- attach_function :ddwaf_object_size, [:ddwaf_object], :uint64
189
- attach_function :ddwaf_object_length, [:ddwaf_object], :size_t
190
- attach_function :ddwaf_object_get_key, [:ddwaf_object, :sizeptr], :charptr
191
- attach_function :ddwaf_object_get_string, [:ddwaf_object, :sizeptr], :charptr
192
- attach_function :ddwaf_object_get_unsigned, [:ddwaf_object], :uint64
193
- attach_function :ddwaf_object_get_signed, [:ddwaf_object], :int64
194
- attach_function :ddwaf_object_get_index, [:ddwaf_object, :size_t], :ddwaf_object
195
- attach_function :ddwaf_object_get_bool, [:ddwaf_object], :bool
196
- attach_function :ddwaf_object_get_float, [:ddwaf_object], :double
197
-
198
- ## freeers
199
-
200
- ObjectFree = attach_function :ddwaf_object_free, [:ddwaf_object], :void
201
- ObjectNoFree = ::FFI::Pointer::NULL
202
-
203
- # main handle
204
-
205
- typedef :pointer, :ddwaf_handle
206
- typedef Object.by_ref, :ddwaf_rule
207
-
208
- callback :ddwaf_object_free_fn, [:ddwaf_object], :void
209
-
210
- class Config < ::FFI::Struct
211
- class Limits < ::FFI::Struct
212
- layout :max_container_size, :uint32,
213
- :max_container_depth, :uint32,
214
- :max_string_length, :uint32
215
- end
216
-
217
- class Obfuscator < ::FFI::Struct
218
- layout :key_regex, :pointer, # should be :charptr
219
- :value_regex, :pointer # should be :charptr
220
- end
221
-
222
- layout :limits, Limits,
223
- :obfuscator, Obfuscator,
224
- :free_fn, :pointer #:ddwaf_object_free_fn
225
- end
226
-
227
- typedef Config.by_ref, :ddwaf_config
228
-
229
- attach_function :ddwaf_init, [:ddwaf_rule, :ddwaf_config, :ddwaf_object], :ddwaf_handle
230
- attach_function :ddwaf_update, [:ddwaf_handle, :ddwaf_object, :ddwaf_object], :ddwaf_handle
231
- attach_function :ddwaf_destroy, [:ddwaf_handle], :void
232
-
233
- attach_function :ddwaf_known_addresses, [: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
- # running
245
-
246
- typedef :pointer, :ddwaf_context
247
-
248
- attach_function :ddwaf_context_init, [:ddwaf_handle], :ddwaf_context
249
- attach_function :ddwaf_context_destroy, [:ddwaf_context], :void
250
-
251
- class Result < ::FFI::Struct
252
- layout :timeout, :bool,
253
- :events, Object,
254
- :actions, Object,
255
- :derivatives, Object,
256
- :total_runtime, :uint64
257
- end
258
-
259
- typedef Result.by_ref, :ddwaf_result
260
- typedef :uint64, :timeout_us
261
-
262
- attach_function :ddwaf_run, [:ddwaf_context, :ddwaf_object, :ddwaf_object, :ddwaf_result, :timeout_us], :ddwaf_ret_code, blocking: true
263
- attach_function :ddwaf_result_free, [:ddwaf_result], :void
264
-
265
- # logging
266
-
267
- DDWAF_LOG_LEVEL = enum :ddwaf_log_trace,
268
- :ddwaf_log_debug,
269
- :ddwaf_log_info,
270
- :ddwaf_log_warn,
271
- :ddwaf_log_error,
272
- :ddwaf_log_off
273
- typedef DDWAF_LOG_LEVEL, :ddwaf_log_level
274
-
275
- callback :ddwaf_log_cb, [:ddwaf_log_level, :string, :string, :uint, :charptr, :uint64], :void
276
-
277
- attach_function :ddwaf_set_log_cb, [:ddwaf_log_cb, :ddwaf_log_level], :bool
278
-
279
- DEFAULT_MAX_CONTAINER_SIZE = 256
280
- DEFAULT_MAX_CONTAINER_DEPTH = 20
281
- DEFAULT_MAX_STRING_LENGTH = 16_384 # in bytes, UTF-8 worst case being 4x size in terms of code point)
282
-
283
- DDWAF_MAX_CONTAINER_SIZE = 256
284
- DDWAF_MAX_CONTAINER_DEPTH = 20
285
- DDWAF_MAX_STRING_LENGTH = 4096
286
-
287
- DDWAF_RUN_TIMEOUT = 5000
288
- end
289
-
290
- def self.version
16
+ def version
291
17
  LibDDWAF.ddwaf_get_version
292
18
  end
293
19
 
294
- # rubocop:disable Metrics/MethodLength,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
295
- def self.ruby_to_object(val, max_container_size: nil, max_container_depth: nil, max_string_length: nil, coerce: true)
296
- case val
297
- when Array
298
- obj = LibDDWAF::Object.new
299
- res = LibDDWAF.ddwaf_object_array(obj)
300
- if res.null?
301
- fail LibDDWAF::Error, "Could not convert into object: #{val}"
302
- end
303
-
304
- max_index = max_container_size - 1 if max_container_size
305
- val.each.with_index do |e, i|
306
- member = ruby_to_object(e,
307
- max_container_size: max_container_size,
308
- max_container_depth: (max_container_depth - 1 if max_container_depth),
309
- max_string_length: max_string_length,
310
- coerce: coerce)
311
- e_res = LibDDWAF.ddwaf_object_array_add(obj, member)
312
- unless e_res
313
- fail LibDDWAF::Error, "Could not add to array object: #{e.inspect}"
314
- end
315
-
316
- break val if max_index && i >= max_index
317
- end unless max_container_depth == 0
318
-
319
- obj
320
- when Hash
321
- obj = LibDDWAF::Object.new
322
- res = LibDDWAF.ddwaf_object_map(obj)
323
- if res.null?
324
- fail LibDDWAF::Error, "Could not convert into object: #{val}"
325
- end
326
-
327
- max_index = max_container_size - 1 if max_container_size
328
- val.each.with_index do |e, i|
329
- k, v = e[0], e[1] # for Steep, which doesn't handle |(k, v), i|
330
-
331
- k = k.to_s[0, max_string_length] if max_string_length
332
- member = ruby_to_object(v,
333
- max_container_size: max_container_size,
334
- max_container_depth: (max_container_depth - 1 if max_container_depth),
335
- max_string_length: max_string_length,
336
- coerce: coerce)
337
- kv_res = LibDDWAF.ddwaf_object_map_addl(obj, k.to_s, k.to_s.bytesize, member)
338
- unless kv_res
339
- fail LibDDWAF::Error, "Could not add to map object: #{k.inspect} => #{v.inspect}"
340
- end
341
-
342
- break val if max_index && i >= max_index
343
- end unless max_container_depth == 0
344
-
345
- obj
346
- when String
347
- obj = LibDDWAF::Object.new
348
- encoded_val = val.to_s.encode('utf-8', invalid: :replace, undef: :replace)
349
- val = encoded_val[0, max_string_length] if max_string_length
350
- str = val.to_s
351
- res = LibDDWAF.ddwaf_object_stringl(obj, str, str.bytesize)
352
- if res.null?
353
- fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
354
- end
355
-
356
- obj
357
- when Symbol
358
- obj = LibDDWAF::Object.new
359
- val = val.to_s[0, max_string_length] if max_string_length
360
- str = val.to_s
361
- res = LibDDWAF.ddwaf_object_stringl(obj, str, str.bytesize)
362
- if res.null?
363
- fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
364
- end
365
-
366
- obj
367
- when Integer
368
- obj = LibDDWAF::Object.new
369
- res = if coerce
370
- LibDDWAF.ddwaf_object_string(obj, val.to_s)
371
- elsif val < 0
372
- LibDDWAF.ddwaf_object_signed(obj, val)
373
- else
374
- LibDDWAF.ddwaf_object_unsigned(obj, val)
375
- end
376
- if res.null?
377
- fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
378
- end
379
-
380
- obj
381
- when Float
382
- obj = LibDDWAF::Object.new
383
- res = if coerce
384
- LibDDWAF.ddwaf_object_string(obj, val.to_s)
385
- else
386
- LibDDWAF.ddwaf_object_float(obj, val)
387
- end
388
- if res.null?
389
- fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
390
- end
391
-
392
- obj
393
- when TrueClass, FalseClass
394
- obj = LibDDWAF::Object.new
395
- res = if coerce
396
- LibDDWAF.ddwaf_object_string(obj, val.to_s)
397
- else
398
- LibDDWAF.ddwaf_object_bool(obj, val)
399
- end
400
- if res.null?
401
- fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
402
- end
403
-
404
- obj
405
- when NilClass
406
- obj = LibDDWAF::Object.new
407
- res = if coerce
408
- LibDDWAF.ddwaf_object_string(obj, '')
409
- else
410
- LibDDWAF.ddwaf_object_null(obj)
411
- end
412
- if res.null?
413
- fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
414
- end
415
-
416
- obj
417
- else
418
- ruby_to_object(''.freeze)
419
- end
420
- end
421
- # rubocop:enable Metrics/MethodLength,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
422
-
423
- def self.object_to_ruby(obj)
424
- case obj[:type]
425
- when :ddwaf_obj_invalid, :ddwaf_obj_null
426
- nil
427
- when :ddwaf_obj_bool
428
- obj[:valueUnion][:boolean]
429
- when :ddwaf_obj_string
430
- obj[:valueUnion][:stringValue].read_bytes(obj[:nbEntries])
431
- when :ddwaf_obj_signed
432
- obj[:valueUnion][:intValue]
433
- when :ddwaf_obj_unsigned
434
- obj[:valueUnion][:uintValue]
435
- when :ddwaf_obj_float
436
- obj[:valueUnion][:f64]
437
- when :ddwaf_obj_array
438
- (0...obj[:nbEntries]).each.with_object([]) do |i, a|
439
- ptr = obj[:valueUnion][:array] + i * LibDDWAF::Object.size
440
- e = object_to_ruby(LibDDWAF::Object.new(ptr))
441
- a << e # steep:ignore
442
- end
443
- when :ddwaf_obj_map
444
- (0...obj[:nbEntries]).each.with_object({}) do |i, h|
445
- ptr = obj[:valueUnion][:array] + i * Datadog::AppSec::WAF::LibDDWAF::Object.size
446
- o = Datadog::AppSec::WAF::LibDDWAF::Object.new(ptr)
447
- l = o[:parameterNameLength]
448
- k = o[:parameterName].read_bytes(l)
449
- v = object_to_ruby(LibDDWAF::Object.new(ptr))
450
- h[k] = v # steep:ignore
451
- end
452
- end
453
- end
454
-
455
- def self.log_callback(level, func, file, line, message, len)
456
- return if logger.nil?
20
+ def log_callback(level, func, file, line, message, len)
21
+ return if WAF.logger.nil?
457
22
 
458
- logger.debug do
23
+ WAF.logger.debug do
459
24
  {
460
25
  level: level,
461
26
  func: func,
@@ -466,14 +31,14 @@ module Datadog
466
31
  end
467
32
  end
468
33
 
469
- def self.logger
34
+ def logger
470
35
  @logger
471
36
  end
472
37
 
473
- def self.logger=(logger)
38
+ def logger=(logger)
474
39
  unless @log_callback
475
- log_callback = method(:log_callback)
476
- Datadog::AppSec::WAF::LibDDWAF.ddwaf_set_log_cb(log_callback, :ddwaf_log_trace)
40
+ log_callback = WAF.method(:log_callback)
41
+ LibDDWAF.ddwaf_set_log_cb(log_callback, :ddwaf_log_trace)
477
42
 
478
43
  # retain logging proc if set properly
479
44
  @log_callback = log_callback
@@ -481,236 +46,6 @@ module Datadog
481
46
 
482
47
  @logger = logger
483
48
  end
484
-
485
- RESULT_CODE = {
486
- ddwaf_err_internal: :err_internal,
487
- ddwaf_err_invalid_object: :err_invalid_object,
488
- ddwaf_err_invalid_argument: :err_invalid_argument,
489
- ddwaf_ok: :ok,
490
- ddwaf_match: :match,
491
- }
492
-
493
- class Handle
494
- attr_reader :handle_obj, :diagnostics, :config
495
-
496
- def initialize(rule, limits: {}, obfuscator: {})
497
- rule_obj = Datadog::AppSec::WAF.ruby_to_object(rule)
498
- if rule_obj.null? || rule_obj[:type] == :ddwaf_object_invalid
499
- fail LibDDWAF::Error, "Could not convert object #{rule.inspect}"
500
- end
501
-
502
- config_obj = Datadog::AppSec::WAF::LibDDWAF::Config.new
503
- if config_obj.null?
504
- fail LibDDWAF::Error, 'Could not create config struct'
505
- end
506
-
507
- config_obj[:limits][:max_container_size] = limits[:max_container_size] || LibDDWAF::DEFAULT_MAX_CONTAINER_SIZE
508
- config_obj[:limits][:max_container_depth] = limits[:max_container_depth] || LibDDWAF::DEFAULT_MAX_CONTAINER_DEPTH
509
- config_obj[:limits][:max_string_length] = limits[:max_string_length] || LibDDWAF::DEFAULT_MAX_STRING_LENGTH
510
- config_obj[:obfuscator][:key_regex] = FFI::MemoryPointer.from_string(obfuscator[:key_regex]) if obfuscator[:key_regex]
511
- config_obj[:obfuscator][:value_regex] = FFI::MemoryPointer.from_string(obfuscator[:value_regex]) if obfuscator[:value_regex]
512
- config_obj[:free_fn] = Datadog::AppSec::WAF::LibDDWAF::ObjectNoFree
513
-
514
- @config = config_obj
515
-
516
- diagnostics_obj = Datadog::AppSec::WAF::LibDDWAF::Object.new
517
-
518
- @handle_obj = Datadog::AppSec::WAF::LibDDWAF.ddwaf_init(rule_obj, config_obj, diagnostics_obj)
519
-
520
- @diagnostics = Datadog::AppSec::WAF.object_to_ruby(diagnostics_obj)
521
-
522
- if @handle_obj.null?
523
- fail LibDDWAF::Error.new('Could not create handle', diagnostics: @diagnostics)
524
- end
525
-
526
- validate!
527
- ensure
528
- Datadog::AppSec::WAF::LibDDWAF.ddwaf_object_free(diagnostics_obj) if diagnostics_obj
529
- Datadog::AppSec::WAF::LibDDWAF.ddwaf_object_free(rule_obj) if rule_obj
530
- end
531
-
532
- def finalize
533
- invalidate!
534
-
535
- Datadog::AppSec::WAF::LibDDWAF.ddwaf_destroy(handle_obj)
536
- end
537
-
538
- def required_addresses
539
- valid!
540
-
541
- count = Datadog::AppSec::WAF::LibDDWAF::UInt32Ptr.new
542
- list = Datadog::AppSec::WAF::LibDDWAF.ddwaf_known_addresses(handle_obj, count)
543
-
544
- return [] if count == 0 # list is null
545
-
546
- list.get_array_of_string(0, count[:value])
547
- end
548
-
549
- def merge(data)
550
- data_obj = Datadog::AppSec::WAF.ruby_to_object(data, coerce: false)
551
- diagnostics_obj = LibDDWAF::Object.new
552
- new_handle = Datadog::AppSec::WAF::LibDDWAF.ddwaf_update(handle_obj, data_obj, diagnostics_obj)
553
-
554
- return if new_handle.null?
555
-
556
- diagnostics = Datadog::AppSec::WAF.object_to_ruby(diagnostics_obj)
557
- new_from_handle(new_handle, diagnostics, config)
558
- ensure
559
- Datadog::AppSec::WAF::LibDDWAF.ddwaf_object_free(data_obj) if data_obj
560
- Datadog::AppSec::WAF::LibDDWAF.ddwaf_object_free(diagnostics_obj) if diagnostics_obj
561
- end
562
-
563
- private
564
-
565
- def new_from_handle(handle_object, diagnostics, config)
566
- obj = self.class.allocate
567
- obj.instance_variable_set(:@handle_obj, handle_object)
568
- obj.instance_variable_set(:@diagnostics, diagnostics)
569
- obj.instance_variable_set(:@config, config)
570
- obj
571
- end
572
-
573
- def validate!
574
- @valid = true
575
- end
576
-
577
- def invalidate!
578
- @valid = false
579
- end
580
-
581
- def valid?
582
- @valid
583
- end
584
-
585
- def valid!
586
- return if valid?
587
-
588
- fail LibDDWAF::Error, "Attempt to use an invalid instance: #{inspect}"
589
- end
590
- end
591
-
592
- class Result
593
- attr_reader :status, :events, :total_runtime, :timeout, :actions, :derivatives
594
-
595
- def initialize(status, events, total_runtime, timeout, actions, derivatives)
596
- @status = status
597
- @events = events
598
- @total_runtime = total_runtime
599
- @timeout = timeout
600
- @actions = actions
601
- @derivatives = derivatives
602
- end
603
- end
604
-
605
- class Context
606
- attr_reader :context_obj
607
-
608
- def initialize(handle)
609
- handle_obj = handle.handle_obj
610
- retain(handle)
611
-
612
- @context_obj = Datadog::AppSec::WAF::LibDDWAF.ddwaf_context_init(handle_obj)
613
- if @context_obj.null?
614
- fail LibDDWAF::Error, 'Could not create context'
615
- end
616
-
617
- validate!
618
- end
619
-
620
- def finalize
621
- invalidate!
622
-
623
- retained.each do |retained_obj|
624
- next unless retained_obj.is_a?(Datadog::AppSec::WAF::LibDDWAF::Object)
625
-
626
- Datadog::AppSec::WAF::LibDDWAF.ddwaf_object_free(retained_obj)
627
- end
628
-
629
- Datadog::AppSec::WAF::LibDDWAF.ddwaf_context_destroy(context_obj)
630
- end
631
-
632
- def run(persistent_data, ephemeral_data, timeout = LibDDWAF::DDWAF_RUN_TIMEOUT)
633
- valid!
634
-
635
- persistent_data_obj = Datadog::AppSec::WAF.ruby_to_object(
636
- persistent_data,
637
- max_container_size: LibDDWAF::DDWAF_MAX_CONTAINER_SIZE,
638
- max_container_depth: LibDDWAF::DDWAF_MAX_CONTAINER_DEPTH,
639
- max_string_length: LibDDWAF::DDWAF_MAX_STRING_LENGTH,
640
- coerce: false
641
- )
642
- if persistent_data_obj.null?
643
- fail LibDDWAF::Error, "Could not convert persistent data: #{persistent_data.inspect}"
644
- end
645
-
646
- # retain C objects in memory for subsequent calls to run
647
- retain(persistent_data_obj)
648
-
649
- ephemeral_data_obj = Datadog::AppSec::WAF.ruby_to_object(
650
- ephemeral_data,
651
- max_container_size: LibDDWAF::DDWAF_MAX_CONTAINER_SIZE,
652
- max_container_depth: LibDDWAF::DDWAF_MAX_CONTAINER_DEPTH,
653
- max_string_length: LibDDWAF::DDWAF_MAX_STRING_LENGTH,
654
- coerce: false
655
- )
656
- if ephemeral_data_obj.null?
657
- fail LibDDWAF::Error, "Could not convert ephemeral data: #{ephemeral_data.inspect}"
658
- end
659
-
660
- result_obj = Datadog::AppSec::WAF::LibDDWAF::Result.new
661
- if result_obj.null?
662
- fail LibDDWAF::Error, "Could not create result object"
663
- end
664
-
665
- code = Datadog::AppSec::WAF::LibDDWAF.ddwaf_run(@context_obj, persistent_data_obj, ephemeral_data_obj, result_obj, timeout)
666
-
667
- result = Result.new(
668
- RESULT_CODE[code],
669
- Datadog::AppSec::WAF.object_to_ruby(result_obj[:events]),
670
- result_obj[:total_runtime],
671
- result_obj[:timeout],
672
- Datadog::AppSec::WAF.object_to_ruby(result_obj[:actions]),
673
- Datadog::AppSec::WAF.object_to_ruby(result_obj[:derivatives]),
674
- )
675
-
676
- [RESULT_CODE[code], result]
677
- ensure
678
- Datadog::AppSec::WAF::LibDDWAF.ddwaf_result_free(result_obj) if result_obj
679
- end
680
-
681
- private
682
-
683
- def validate!
684
- @valid = true
685
- end
686
-
687
- def invalidate!
688
- @valid = false
689
- end
690
-
691
- def valid?
692
- @valid
693
- end
694
-
695
- def valid!
696
- return if valid?
697
-
698
- fail LibDDWAF::Error, "Attempt to use an invalid instance: #{inspect}"
699
- end
700
-
701
- def retained
702
- @retained ||= []
703
- end
704
-
705
- def retain(object)
706
- retained << object
707
- end
708
-
709
- def release(object)
710
- retained.delete(object)
711
- end
712
- end
713
49
  end
714
- # rubocop:enable Metrics/ModuleLength
715
50
  end
716
51
  end