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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef259ceefea0ae14550d17992796b271b7a27b21894fd4ed2dc3442202f35aa5
|
4
|
+
data.tar.gz: 5c54316bea2fadafd230d5539daa64b234b5341e4309c5045ebbfa9deb46a317
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd44f90381f700db4037fb1f8870b1c58be8b130f7b73a2e3c72083992fbe7ab55511dfd25c54dfbebc7686d9371c2fdffd0ef1e57f7f65e229bfcac6510bb10
|
7
|
+
data.tar.gz: 19bafde328e0122ddbb6db37746c0d983743be8135345ab2bbd4c0c82843632a1c52599ece27c90d55022e85ed5fca5f81b256cd049effe9bc23c6495066c275
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,14 @@
|
|
1
|
+
# 2025-02-20 v.1.18.0.0.1
|
2
|
+
|
3
|
+
- Fixed memory-leak in `Datadog::AppSec::WAF::Context#run` when non-empty ephemeral data passed
|
4
|
+
|
1
5
|
# 2024-10-29 v.1.15.0.0.0
|
6
|
+
|
2
7
|
- Update to libddwaf 1.15.0
|
3
8
|
- Changed `Datadog::AppSec::WAF::Context#run` interface to accommodate ephemeral data ([Breaking change](https://github.com/DataDog/libddwaf/blob/master/CHANGELOG.md#v1150-unstable))
|
4
9
|
|
5
10
|
# 2023-09-11 v.1.14.0.0.0
|
11
|
+
|
6
12
|
- Update to libddwaf 1.14.0
|
7
13
|
- Add support for `Float` and `Nil` scalar values when converting from ruby to WAF Object and vice versa.
|
8
14
|
|
@@ -0,0 +1,126 @@
|
|
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
|
+
retained.clear
|
39
|
+
LibDDWAF.ddwaf_context_destroy(context_obj)
|
40
|
+
end
|
41
|
+
|
42
|
+
def run(persistent_data, ephemeral_data, timeout = LibDDWAF::DDWAF_RUN_TIMEOUT)
|
43
|
+
valid!
|
44
|
+
|
45
|
+
persistent_data_obj = Converter.ruby_to_object(
|
46
|
+
persistent_data,
|
47
|
+
max_container_size: LibDDWAF::DDWAF_MAX_CONTAINER_SIZE,
|
48
|
+
max_container_depth: LibDDWAF::DDWAF_MAX_CONTAINER_DEPTH,
|
49
|
+
max_string_length: LibDDWAF::DDWAF_MAX_STRING_LENGTH,
|
50
|
+
coerce: false
|
51
|
+
)
|
52
|
+
if persistent_data_obj.null?
|
53
|
+
raise LibDDWAF::Error, "Could not convert persistent data: #{persistent_data.inspect}"
|
54
|
+
end
|
55
|
+
|
56
|
+
# retain C objects in memory for subsequent calls to run
|
57
|
+
retain(persistent_data_obj)
|
58
|
+
|
59
|
+
ephemeral_data_obj = Converter.ruby_to_object(
|
60
|
+
ephemeral_data,
|
61
|
+
max_container_size: LibDDWAF::DDWAF_MAX_CONTAINER_SIZE,
|
62
|
+
max_container_depth: LibDDWAF::DDWAF_MAX_CONTAINER_DEPTH,
|
63
|
+
max_string_length: LibDDWAF::DDWAF_MAX_STRING_LENGTH,
|
64
|
+
coerce: false
|
65
|
+
)
|
66
|
+
if ephemeral_data_obj.null?
|
67
|
+
raise LibDDWAF::Error, "Could not convert ephemeral data: #{ephemeral_data.inspect}"
|
68
|
+
end
|
69
|
+
|
70
|
+
result_obj = LibDDWAF::Result.new
|
71
|
+
raise LibDDWAF::Error, 'Could not create result object' if result_obj.null?
|
72
|
+
|
73
|
+
code = LibDDWAF.ddwaf_run(@context_obj, persistent_data_obj, ephemeral_data_obj, result_obj, timeout)
|
74
|
+
|
75
|
+
result = Result.new(
|
76
|
+
RESULT_CODE[code],
|
77
|
+
Converter.object_to_ruby(result_obj[:events]),
|
78
|
+
result_obj[:total_runtime],
|
79
|
+
result_obj[:timeout],
|
80
|
+
Converter.object_to_ruby(result_obj[:actions]),
|
81
|
+
Converter.object_to_ruby(result_obj[:derivatives])
|
82
|
+
)
|
83
|
+
|
84
|
+
[RESULT_CODE[code], result]
|
85
|
+
ensure
|
86
|
+
LibDDWAF.ddwaf_result_free(result_obj) if result_obj
|
87
|
+
LibDDWAF.ddwaf_object_free(ephemeral_data_obj) if ephemeral_data_obj
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# FIXME: Rename into something which reflect that it's impossible to run
|
93
|
+
# libddwaf on finalized context (closed)
|
94
|
+
def validate!
|
95
|
+
@valid = true
|
96
|
+
end
|
97
|
+
|
98
|
+
def invalidate!
|
99
|
+
@valid = false
|
100
|
+
end
|
101
|
+
|
102
|
+
def valid?
|
103
|
+
@valid
|
104
|
+
end
|
105
|
+
|
106
|
+
def valid!
|
107
|
+
return if valid?
|
108
|
+
|
109
|
+
raise LibDDWAF::Error, "Attempt to use an invalid instance: #{inspect}"
|
110
|
+
end
|
111
|
+
|
112
|
+
def retained
|
113
|
+
@retained ||= []
|
114
|
+
end
|
115
|
+
|
116
|
+
def retain(object)
|
117
|
+
retained << object
|
118
|
+
end
|
119
|
+
|
120
|
+
def release(object)
|
121
|
+
retained.delete(object)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
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
|