libddwaf 1.15.0.0.0-arm64-darwin → 1.18.0.0.0-arm64-darwin
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/datadog/appsec/waf/context.rb +122 -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 +307 -0
- data/lib/datadog/appsec/waf/result.rb +33 -0
- data/lib/datadog/appsec/waf/version.rb +1 -1
- data/lib/datadog/appsec/waf.rb +17 -679
- data/vendor/libddwaf/libddwaf-1.18.0-darwin-arm64/lib/libddwaf.dylib +0 -0
- metadata +9 -4
- data/vendor/libddwaf/libddwaf-1.15.0-darwin-arm64/lib/libddwaf.dylib +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b529e469cd72c13ad59ec392cbf6b1266daeea44871e388a048a34ec4f0bdf9
|
4
|
+
data.tar.gz: d2c5354d770987587743b2c4ad19b0a38085021c1770118492a2ed2eb6166c51
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f1f32f96ab7ee0c99fd7461f52a3cd184dee1c56ca96915453e1a62349bd2f67948ca1f8ff12c8500332a1fff1108383188d0b84150031e035aba3b9db9f545
|
7
|
+
data.tar.gz: 9eda1af4cd84f265f361177133e100e672a7ffa120e89e5285bb9177b536569fcecad59e85933043593344de4651dea1382791e79221508cf03762684397f551
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module AppSec
|
5
|
+
module WAF
|
6
|
+
# Ruby representation of the ddwaf_context in libddwaf
|
7
|
+
# See https://github.com/DataDog/libddwaf/blob/10e3a1dfc7bc9bb8ab11a09a9f8b6b339eaf3271/BINDING_IMPL_NOTES.md?plain=1#L125-L158
|
8
|
+
class Context
|
9
|
+
RESULT_CODE = {
|
10
|
+
ddwaf_ok: :ok,
|
11
|
+
ddwaf_match: :match,
|
12
|
+
ddwaf_err_internal: :err_internal,
|
13
|
+
ddwaf_err_invalid_object: :err_invalid_object,
|
14
|
+
ddwaf_err_invalid_argument: :err_invalid_argument
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
attr_reader :context_obj
|
18
|
+
|
19
|
+
def initialize(handle)
|
20
|
+
handle_obj = handle.handle_obj
|
21
|
+
retain(handle)
|
22
|
+
|
23
|
+
@context_obj = LibDDWAF.ddwaf_context_init(handle_obj)
|
24
|
+
raise LibDDWAF::Error, 'Could not create context' if @context_obj.null?
|
25
|
+
|
26
|
+
validate!
|
27
|
+
end
|
28
|
+
|
29
|
+
def finalize
|
30
|
+
invalidate!
|
31
|
+
|
32
|
+
retained.each do |retained_obj|
|
33
|
+
next unless retained_obj.is_a?(LibDDWAF::Object)
|
34
|
+
|
35
|
+
LibDDWAF.ddwaf_object_free(retained_obj)
|
36
|
+
end
|
37
|
+
|
38
|
+
LibDDWAF.ddwaf_context_destroy(context_obj)
|
39
|
+
end
|
40
|
+
|
41
|
+
def run(persistent_data, ephemeral_data, timeout = LibDDWAF::DDWAF_RUN_TIMEOUT)
|
42
|
+
valid!
|
43
|
+
|
44
|
+
persistent_data_obj = Converter.ruby_to_object(
|
45
|
+
persistent_data,
|
46
|
+
max_container_size: LibDDWAF::DDWAF_MAX_CONTAINER_SIZE,
|
47
|
+
max_container_depth: LibDDWAF::DDWAF_MAX_CONTAINER_DEPTH,
|
48
|
+
max_string_length: LibDDWAF::DDWAF_MAX_STRING_LENGTH,
|
49
|
+
coerce: false
|
50
|
+
)
|
51
|
+
if persistent_data_obj.null?
|
52
|
+
raise LibDDWAF::Error, "Could not convert persistent data: #{persistent_data.inspect}"
|
53
|
+
end
|
54
|
+
|
55
|
+
# retain C objects in memory for subsequent calls to run
|
56
|
+
retain(persistent_data_obj)
|
57
|
+
|
58
|
+
ephemeral_data_obj = Converter.ruby_to_object(
|
59
|
+
ephemeral_data,
|
60
|
+
max_container_size: LibDDWAF::DDWAF_MAX_CONTAINER_SIZE,
|
61
|
+
max_container_depth: LibDDWAF::DDWAF_MAX_CONTAINER_DEPTH,
|
62
|
+
max_string_length: LibDDWAF::DDWAF_MAX_STRING_LENGTH,
|
63
|
+
coerce: false
|
64
|
+
)
|
65
|
+
if ephemeral_data_obj.null?
|
66
|
+
raise LibDDWAF::Error, "Could not convert ephemeral data: #{ephemeral_data.inspect}"
|
67
|
+
end
|
68
|
+
|
69
|
+
result_obj = LibDDWAF::Result.new
|
70
|
+
raise LibDDWAF::Error, 'Could not create result object' if result_obj.null?
|
71
|
+
|
72
|
+
code = LibDDWAF.ddwaf_run(@context_obj, persistent_data_obj, ephemeral_data_obj, result_obj, timeout)
|
73
|
+
|
74
|
+
result = Result.new(
|
75
|
+
RESULT_CODE[code],
|
76
|
+
Converter.object_to_ruby(result_obj[:events]),
|
77
|
+
result_obj[:total_runtime],
|
78
|
+
result_obj[:timeout],
|
79
|
+
Converter.object_to_ruby(result_obj[:actions]),
|
80
|
+
Converter.object_to_ruby(result_obj[:derivatives])
|
81
|
+
)
|
82
|
+
|
83
|
+
[RESULT_CODE[code], result]
|
84
|
+
ensure
|
85
|
+
LibDDWAF.ddwaf_result_free(result_obj) if result_obj
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def validate!
|
91
|
+
@valid = true
|
92
|
+
end
|
93
|
+
|
94
|
+
def invalidate!
|
95
|
+
@valid = false
|
96
|
+
end
|
97
|
+
|
98
|
+
def valid?
|
99
|
+
@valid
|
100
|
+
end
|
101
|
+
|
102
|
+
def valid!
|
103
|
+
return if valid?
|
104
|
+
|
105
|
+
raise LibDDWAF::Error, "Attempt to use an invalid instance: #{inspect}"
|
106
|
+
end
|
107
|
+
|
108
|
+
def retained
|
109
|
+
@retained ||= []
|
110
|
+
end
|
111
|
+
|
112
|
+
def retain(object)
|
113
|
+
retained << object
|
114
|
+
end
|
115
|
+
|
116
|
+
def release(object)
|
117
|
+
retained.delete(object)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module AppSec
|
5
|
+
module WAF
|
6
|
+
# Module responsible for Ruby-to-C and C-to-Ruby conversions
|
7
|
+
module Converter
|
8
|
+
module_function
|
9
|
+
|
10
|
+
# rubocop:disable Metrics/MethodLength,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
11
|
+
def ruby_to_object(val, max_container_size: nil, max_container_depth: nil, max_string_length: nil, coerce: true)
|
12
|
+
case val
|
13
|
+
when Array
|
14
|
+
obj = LibDDWAF::Object.new
|
15
|
+
res = LibDDWAF.ddwaf_object_array(obj)
|
16
|
+
if res.null?
|
17
|
+
raise LibDDWAF::Error, "Could not convert into object: #{val}"
|
18
|
+
end
|
19
|
+
|
20
|
+
max_index = max_container_size - 1 if max_container_size
|
21
|
+
val.each.with_index do |e, i| # rubocop:disable Style/MultilineIfModifier
|
22
|
+
member = Converter.ruby_to_object(e,
|
23
|
+
max_container_size: max_container_size,
|
24
|
+
max_container_depth: (max_container_depth - 1 if max_container_depth),
|
25
|
+
max_string_length: max_string_length,
|
26
|
+
coerce: coerce)
|
27
|
+
e_res = LibDDWAF.ddwaf_object_array_add(obj, member)
|
28
|
+
raise LibDDWAF::Error, "Could not add to array object: #{e.inspect}" unless e_res
|
29
|
+
|
30
|
+
break val if max_index && i >= max_index
|
31
|
+
end unless max_container_depth == 0
|
32
|
+
|
33
|
+
obj
|
34
|
+
when Hash
|
35
|
+
obj = LibDDWAF::Object.new
|
36
|
+
res = LibDDWAF.ddwaf_object_map(obj)
|
37
|
+
if res.null?
|
38
|
+
raise LibDDWAF::Error, "Could not convert into object: #{val}"
|
39
|
+
end
|
40
|
+
|
41
|
+
max_index = max_container_size - 1 if max_container_size
|
42
|
+
val.each.with_index do |e, i| # rubocop:disable Style/MultilineIfModifier
|
43
|
+
# for Steep, which doesn't handle |(k, v), i|
|
44
|
+
k, v = e[0], e[1] # rubocop:disable Style/ParallelAssignment
|
45
|
+
|
46
|
+
k = k.to_s[0, max_string_length] if max_string_length
|
47
|
+
member = Converter.ruby_to_object(v,
|
48
|
+
max_container_size: max_container_size,
|
49
|
+
max_container_depth: (max_container_depth - 1 if max_container_depth),
|
50
|
+
max_string_length: max_string_length,
|
51
|
+
coerce: coerce)
|
52
|
+
kv_res = LibDDWAF.ddwaf_object_map_addl(obj, k.to_s, k.to_s.bytesize, member)
|
53
|
+
unless kv_res
|
54
|
+
raise LibDDWAF::Error, "Could not add to map object: #{k.inspect} => #{v.inspect}"
|
55
|
+
end
|
56
|
+
|
57
|
+
break val if max_index && i >= max_index
|
58
|
+
end unless max_container_depth == 0
|
59
|
+
|
60
|
+
obj
|
61
|
+
when String
|
62
|
+
obj = LibDDWAF::Object.new
|
63
|
+
encoded_val = val.to_s.encode('utf-8', invalid: :replace, undef: :replace)
|
64
|
+
val = encoded_val[0, max_string_length] if max_string_length
|
65
|
+
str = val.to_s
|
66
|
+
res = LibDDWAF.ddwaf_object_stringl(obj, str, str.bytesize)
|
67
|
+
if res.null?
|
68
|
+
raise LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
|
69
|
+
end
|
70
|
+
|
71
|
+
obj
|
72
|
+
when Symbol
|
73
|
+
obj = LibDDWAF::Object.new
|
74
|
+
val = val.to_s[0, max_string_length] if max_string_length
|
75
|
+
str = val.to_s
|
76
|
+
res = LibDDWAF.ddwaf_object_stringl(obj, str, str.bytesize)
|
77
|
+
if res.null?
|
78
|
+
raise LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
|
79
|
+
end
|
80
|
+
|
81
|
+
obj
|
82
|
+
when Integer
|
83
|
+
obj = LibDDWAF::Object.new
|
84
|
+
res = if coerce
|
85
|
+
LibDDWAF.ddwaf_object_string(obj, val.to_s)
|
86
|
+
elsif val < 0
|
87
|
+
LibDDWAF.ddwaf_object_signed(obj, val)
|
88
|
+
else
|
89
|
+
LibDDWAF.ddwaf_object_unsigned(obj, val)
|
90
|
+
end
|
91
|
+
if res.null?
|
92
|
+
raise LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
|
93
|
+
end
|
94
|
+
|
95
|
+
obj
|
96
|
+
when Float
|
97
|
+
obj = LibDDWAF::Object.new
|
98
|
+
res = if coerce
|
99
|
+
LibDDWAF.ddwaf_object_string(obj, val.to_s)
|
100
|
+
else
|
101
|
+
LibDDWAF.ddwaf_object_float(obj, val)
|
102
|
+
end
|
103
|
+
if res.null?
|
104
|
+
raise LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
|
105
|
+
end
|
106
|
+
|
107
|
+
obj
|
108
|
+
when TrueClass, FalseClass
|
109
|
+
obj = LibDDWAF::Object.new
|
110
|
+
res = if coerce
|
111
|
+
LibDDWAF.ddwaf_object_string(obj, val.to_s)
|
112
|
+
else
|
113
|
+
LibDDWAF.ddwaf_object_bool(obj, val)
|
114
|
+
end
|
115
|
+
if res.null?
|
116
|
+
raise LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
|
117
|
+
end
|
118
|
+
|
119
|
+
obj
|
120
|
+
when NilClass
|
121
|
+
obj = LibDDWAF::Object.new
|
122
|
+
res = if coerce
|
123
|
+
LibDDWAF.ddwaf_object_string(obj, '')
|
124
|
+
else
|
125
|
+
LibDDWAF.ddwaf_object_null(obj)
|
126
|
+
end
|
127
|
+
if res.null?
|
128
|
+
raise LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
|
129
|
+
end
|
130
|
+
|
131
|
+
obj
|
132
|
+
else
|
133
|
+
Converter.ruby_to_object('')
|
134
|
+
end
|
135
|
+
end
|
136
|
+
# rubocop:enable Metrics/MethodLength,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
137
|
+
|
138
|
+
def object_to_ruby(obj)
|
139
|
+
case obj[:type]
|
140
|
+
when :ddwaf_obj_invalid, :ddwaf_obj_null
|
141
|
+
nil
|
142
|
+
when :ddwaf_obj_bool
|
143
|
+
obj[:valueUnion][:boolean]
|
144
|
+
when :ddwaf_obj_string
|
145
|
+
obj[:valueUnion][:stringValue].read_bytes(obj[:nbEntries])
|
146
|
+
when :ddwaf_obj_signed
|
147
|
+
obj[:valueUnion][:intValue]
|
148
|
+
when :ddwaf_obj_unsigned
|
149
|
+
obj[:valueUnion][:uintValue]
|
150
|
+
when :ddwaf_obj_float
|
151
|
+
obj[:valueUnion][:f64]
|
152
|
+
when :ddwaf_obj_array
|
153
|
+
(0...obj[:nbEntries]).each.with_object([]) do |i, a|
|
154
|
+
ptr = obj[:valueUnion][:array] + i * LibDDWAF::Object.size
|
155
|
+
e = Converter.object_to_ruby(LibDDWAF::Object.new(ptr))
|
156
|
+
a << e # steep:ignore
|
157
|
+
end
|
158
|
+
when :ddwaf_obj_map
|
159
|
+
(0...obj[:nbEntries]).each.with_object({}) do |i, h|
|
160
|
+
ptr = obj[:valueUnion][:array] + i * Datadog::AppSec::WAF::LibDDWAF::Object.size
|
161
|
+
o = Datadog::AppSec::WAF::LibDDWAF::Object.new(ptr)
|
162
|
+
l = o[:parameterNameLength]
|
163
|
+
k = o[:parameterName].read_bytes(l)
|
164
|
+
v = Converter.object_to_ruby(LibDDWAF::Object.new(ptr))
|
165
|
+
h[k] = v # steep:ignore
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module AppSec
|
5
|
+
module WAF
|
6
|
+
# Ruby representation of the ddwaf_handle in libddwaf
|
7
|
+
# See https://github.com/DataDog/libddwaf/blob/10e3a1dfc7bc9bb8ab11a09a9f8b6b339eaf3271/BINDING_IMPL_NOTES.md?plain=1#L4-L19
|
8
|
+
class Handle
|
9
|
+
attr_reader :handle_obj, :diagnostics, :config
|
10
|
+
|
11
|
+
def initialize(rule, limits: {}, obfuscator: {})
|
12
|
+
rule_obj = Converter.ruby_to_object(rule)
|
13
|
+
if rule_obj.null? || rule_obj[:type] == :ddwaf_object_invalid
|
14
|
+
raise LibDDWAF::Error, "Could not convert object #{rule.inspect}"
|
15
|
+
end
|
16
|
+
|
17
|
+
config_obj = Datadog::AppSec::WAF::LibDDWAF::Config.new
|
18
|
+
if config_obj.null?
|
19
|
+
raise LibDDWAF::Error, 'Could not create config struct'
|
20
|
+
end
|
21
|
+
|
22
|
+
config_obj[:limits][:max_container_size] = limits[:max_container_size] || LibDDWAF::DEFAULT_MAX_CONTAINER_SIZE
|
23
|
+
config_obj[:limits][:max_container_depth] = limits[:max_container_depth] || LibDDWAF::DEFAULT_MAX_CONTAINER_DEPTH
|
24
|
+
config_obj[:limits][:max_string_length] = limits[:max_string_length] || LibDDWAF::DEFAULT_MAX_STRING_LENGTH
|
25
|
+
config_obj[:obfuscator][:key_regex] = FFI::MemoryPointer.from_string(obfuscator[:key_regex]) if obfuscator[:key_regex]
|
26
|
+
config_obj[:obfuscator][:value_regex] = FFI::MemoryPointer.from_string(obfuscator[:value_regex]) if obfuscator[:value_regex]
|
27
|
+
config_obj[:free_fn] = LibDDWAF::ObjectNoFree
|
28
|
+
|
29
|
+
@config = config_obj
|
30
|
+
|
31
|
+
diagnostics_obj = LibDDWAF::Object.new
|
32
|
+
|
33
|
+
@handle_obj = LibDDWAF.ddwaf_init(rule_obj, config_obj, diagnostics_obj)
|
34
|
+
|
35
|
+
@diagnostics = Converter.object_to_ruby(diagnostics_obj)
|
36
|
+
|
37
|
+
if @handle_obj.null?
|
38
|
+
raise LibDDWAF::Error.new('Could not create handle', diagnostics: @diagnostics)
|
39
|
+
end
|
40
|
+
|
41
|
+
validate!
|
42
|
+
ensure
|
43
|
+
LibDDWAF.ddwaf_object_free(diagnostics_obj) if diagnostics_obj
|
44
|
+
LibDDWAF.ddwaf_object_free(rule_obj) if rule_obj
|
45
|
+
end
|
46
|
+
|
47
|
+
def finalize
|
48
|
+
invalidate!
|
49
|
+
|
50
|
+
LibDDWAF.ddwaf_destroy(handle_obj)
|
51
|
+
end
|
52
|
+
|
53
|
+
def required_addresses
|
54
|
+
valid!
|
55
|
+
|
56
|
+
count = LibDDWAF::UInt32Ptr.new
|
57
|
+
list = LibDDWAF.ddwaf_known_addresses(handle_obj, count)
|
58
|
+
|
59
|
+
return [] if count == 0 # list is null
|
60
|
+
|
61
|
+
list.get_array_of_string(0, count[:value])
|
62
|
+
end
|
63
|
+
|
64
|
+
def merge(data)
|
65
|
+
data_obj = Converter.ruby_to_object(data, coerce: false)
|
66
|
+
diagnostics_obj = LibDDWAF::Object.new
|
67
|
+
new_handle = LibDDWAF.ddwaf_update(handle_obj, data_obj, diagnostics_obj)
|
68
|
+
|
69
|
+
return if new_handle.null?
|
70
|
+
|
71
|
+
diagnostics = Converter.object_to_ruby(diagnostics_obj)
|
72
|
+
new_from_handle(new_handle, diagnostics, config)
|
73
|
+
ensure
|
74
|
+
LibDDWAF.ddwaf_object_free(data_obj) if data_obj
|
75
|
+
LibDDWAF.ddwaf_object_free(diagnostics_obj) if diagnostics_obj
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def new_from_handle(handle_object, diagnostics, config)
|
81
|
+
obj = Handle.allocate
|
82
|
+
obj.instance_variable_set(:@handle_obj, handle_object)
|
83
|
+
obj.instance_variable_set(:@diagnostics, diagnostics)
|
84
|
+
obj.instance_variable_set(:@config, config)
|
85
|
+
obj
|
86
|
+
end
|
87
|
+
|
88
|
+
def validate!
|
89
|
+
@valid = true
|
90
|
+
end
|
91
|
+
|
92
|
+
def invalidate!
|
93
|
+
@valid = false
|
94
|
+
end
|
95
|
+
|
96
|
+
def valid?
|
97
|
+
@valid
|
98
|
+
end
|
99
|
+
|
100
|
+
def valid!
|
101
|
+
return if valid?
|
102
|
+
|
103
|
+
raise LibDDWAF::Error, "Attempt to use an invalid instance: #{inspect}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|