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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 680463b022230fdf677f2d0e9ac140ca487bb225b23f73c2bbf02219125eaf39
4
- data.tar.gz: a00a6cdc1987bd1544626864f1ef371ed922a333e82982516e123702e1a20e60
3
+ metadata.gz: ef259ceefea0ae14550d17992796b271b7a27b21894fd4ed2dc3442202f35aa5
4
+ data.tar.gz: 5c54316bea2fadafd230d5539daa64b234b5341e4309c5045ebbfa9deb46a317
5
5
  SHA512:
6
- metadata.gz: 2283e51cee5a5d2fcf760f4b2cca60db1278f216d236ea575c8a3ba2509c5000249e6f1056b0aae6d5c1b9af13023f29b26a7b2f88099d65ad30a5635800f31d
7
- data.tar.gz: 4ffe31cade9cd4f555e7dadb04aa5a11ca3004b19e2e4249fc43df9d9c7c057ca1cf3c8274b6ca524c6bd37f19508f38ea836b4861b7fdeb252cbe29a1931880
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