libddwaf 1.15.0.0.0-x86_64-linux → 1.18.0.0.1-x86_64-linux

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