libddwaf 1.14.0.0.0-arm64-darwin → 1.18.0.0.0-arm64-darwin

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: a8504c955727644f532a1bf86f97e4531780bc12aaf617972f77f6d47858d624
4
- data.tar.gz: b934852060eef0354e1be5d500aaa8409c70d31dc12f417cd7bec33241617009
3
+ metadata.gz: 9b529e469cd72c13ad59ec392cbf6b1266daeea44871e388a048a34ec4f0bdf9
4
+ data.tar.gz: d2c5354d770987587743b2c4ad19b0a38085021c1770118492a2ed2eb6166c51
5
5
  SHA512:
6
- metadata.gz: 6fafdc08a74b09f2faed8a3b74de401e16fe52d085bcb74250fd59d3ed8eb5cb7e137eb9a25e9e99a91e891d20aa14ced288ae6956fbb5de1d8874a86acedf05
7
- data.tar.gz: 1818e719ec5dc119ff56944c7c25323880c336235886b6df146f5b4e68b61b72271bb340f64f55c9b5b1e6b342c4ed3b9fbae581a8d37671c7b2e74233e0e9e2
6
+ metadata.gz: 6f1f32f96ab7ee0c99fd7461f52a3cd184dee1c56ca96915453e1a62349bd2f67948ca1f8ff12c8500332a1fff1108383188d0b84150031e035aba3b9db9f545
7
+ data.tar.gz: 9eda1af4cd84f265f361177133e100e672a7ffa120e89e5285bb9177b536569fcecad59e85933043593344de4651dea1382791e79221508cf03762684397f551
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # 2024-10-29 v.1.15.0.0.0
2
+ - Update to libddwaf 1.15.0
3
+ - 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
+
1
5
  # 2023-09-11 v.1.14.0.0.0
2
6
  - Update to libddwaf 1.14.0
3
7
  - Add support for `Float` and `Nil` scalar values when converting from ruby to WAF Object and vice versa.
@@ -6,7 +10,7 @@
6
10
  # 2023-08-29 v.1.11.0.0.0
7
11
 
8
12
  - Update to libddwaf 1.11.0
9
- - Changed `Datadog::AppSec::WAF::Handle#ruleset_info` to `Datadog::AppSec::WAF::Handle#diagnostics``. (Breaking change)
13
+ - Changed `Datadog::AppSec::WAF::Handle#ruleset_info` to `Datadog::AppSec::WAF::Handle#diagnostics`. (Breaking change)
10
14
  The schema of the diagnostics variable can be found [here](https://github.com/DataDog/libddwaf/blob/master/schema/diagnostics.json)
11
15
  - Changed `Datadog::AppSec::WAF::Result#data` to `Datadog::AppSec::WAF::Result#events`. (Breaking change)
12
16
  The schema of the events variable can be found [here](https://github.com/DataDog/libddwaf/blob/master/schema/events.json)
@@ -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