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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/lib/datadog/appsec/waf/context.rb +126 -0
- data/lib/datadog/appsec/waf/converter.rb +172 -0
- data/lib/datadog/appsec/waf/handle.rb +108 -0
- data/lib/datadog/appsec/waf/lib_ddwaf.rb +313 -0
- data/lib/datadog/appsec/waf/result.rb +33 -0
- data/lib/datadog/appsec/waf/version.rb +2 -2
- data/lib/datadog/appsec/waf.rb +17 -679
- data/vendor/libddwaf/libddwaf-1.18.0-linux-x86_64/lib/libddwaf.so +0 -0
- metadata +9 -4
- data/vendor/libddwaf/libddwaf-1.15.0-linux-x86_64/lib/libddwaf.so +0 -0
data/lib/datadog/appsec/waf.rb
CHANGED
@@ -1,458 +1,26 @@
|
|
1
|
-
|
2
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
292
|
-
|
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
|
-
|
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
|
34
|
+
def logger
|
467
35
|
@logger
|
468
36
|
end
|
469
37
|
|
470
|
-
def
|
38
|
+
def logger=(logger)
|
471
39
|
unless @log_callback
|
472
|
-
log_callback = method(:log_callback)
|
473
|
-
|
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
|