libddwaf 1.14.0.0.0-x86_64-darwin → 1.18.0.0.0-x86_64-darwin

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 01aa45a7f4d814627c8ba5673090c1e649fea6eca2bab27610c7419bbcf47e57
4
- data.tar.gz: 8c9b9ee8a288f523a2aec7dc30790b61e596a9cd7f2862bbf5130e116a01f7c3
3
+ metadata.gz: d698f36c04a2f93451be0473edac063b8c5f777813d046645ec967ef662e81b4
4
+ data.tar.gz: 0b1cf88a7a4857f5924dd40a7159e53211b57c1ce915c237dfe28e20e9cf1dbe
5
5
  SHA512:
6
- metadata.gz: cae407afb5eb7df290143746428a86db76da82a48ccbdfff9fee763ddf61e728cdb47d345c545e2e2efcc852f670931344a687696a722131ab463d85c21dc9e7
7
- data.tar.gz: 1d58b866a525292d2aa88602b015a2bcd8730ea644549888bc137bb8aaafa927d03913683fe6ed357324f6e3fce29ac519e57ea28f1c72ddc0b52a4c70252f63
6
+ metadata.gz: '086d24f3178b44c45484d75bc39b4b53a9b32b0f574d12a35f8c4b684cf0d1c81c7f3905fb2c804f0b93f3405cbe6161d9951723050b1dea929841d13d6e9844'
7
+ data.tar.gz: 3222b0bb8ea92b1ad842392dd4badcc1739a36b53247a5fbf4bff943d9c230e525586868747ec67007fa930e8c97a7213266cade60a37d157debabdf8758430e
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