libddwaf 1.15.0.0.0 → 1.18.0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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