libddwaf 1.15.0.0.0-x86_64-linux → 1.18.0.0.0-x86_64-linux

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: 680463b022230fdf677f2d0e9ac140ca487bb225b23f73c2bbf02219125eaf39
4
- data.tar.gz: a00a6cdc1987bd1544626864f1ef371ed922a333e82982516e123702e1a20e60
3
+ metadata.gz: d4b9dc918f767d7f4320f1028abe05bf12694598a1c2d79855310f57e757c204
4
+ data.tar.gz: 129101ead82a9771636006ce64e4d9352b5d0746c0dd411adbfdf6164e0c91ed
5
5
  SHA512:
6
- metadata.gz: 2283e51cee5a5d2fcf760f4b2cca60db1278f216d236ea575c8a3ba2509c5000249e6f1056b0aae6d5c1b9af13023f29b26a7b2f88099d65ad30a5635800f31d
7
- data.tar.gz: 4ffe31cade9cd4f555e7dadb04aa5a11ca3004b19e2e4249fc43df9d9c7c057ca1cf3c8274b6ca524c6bd37f19508f38ea836b4861b7fdeb252cbe29a1931880
6
+ metadata.gz: da26d9ea842942b28de110f04fcbce3d8b16acd068083736df070313aa122195d72f73c88b6fb5298e3b857c3609a233bf479999e17acc38ebf34da67879e0f4
7
+ data.tar.gz: b5344119847906e408839017242bf9d8511c9047abe3e0cc5a2b8aa941dee0d0191947f0b1ecbf1b853d1007943ae60d98cbd4dab1188cb2a5236bff4cb0d2df
@@ -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