libddwaf 1.5.1.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1054077e5a63048aa75da4146cb795e14c7195ec5da4287d91408b04016c23f2
4
+ data.tar.gz: f149053d529ce5b711431d78aec9692feba59efbf7ce96da341ca4a02a0b4a3c
5
+ SHA512:
6
+ metadata.gz: 6c0cd009021993f6ed49586425e7619b906c8b1c2361174569a60b19bfd5ba01daf7e078eadbabaca8c8264c48b77689dda9cbef7cf38b5cd4c81d6c25912857
7
+ data.tar.gz: adef30898cc0e609f7847462aa768311ed626223ad603fa8e2dd8d94f01383b36fc8e19323f9df54c30a7cd51a0334f1e4319728d8963ee56d34ec6fbf730861
data/CHANGELOG.md ADDED
@@ -0,0 +1,74 @@
1
+ # 2022-10-04 v1.5.1.0.0
2
+
3
+ - Update to libddwaf 1.5.1
4
+ - Add live rule data update API
5
+ - Add live rule toggle API
6
+ - Add libddwaf boolean type support
7
+ - Add Ruby to libddwaf object conversion limits
8
+ - Add Ruby to libddwaf object converter optional coercion of scalars to string
9
+ - Add static type checking via RBS+Steep
10
+ - Change version to return a string
11
+ - Change free function to be passed as config instead of context init argument
12
+ - Change result to include action list
13
+ - Change return code from action to status
14
+ - Change handle and context freeing model from GC-based to explicit
15
+ - Fix double-free upon finalization of retained C objects
16
+ - Fix context crash by retaining necessary C objects
17
+
18
+ # 2022-05-20 v1.3.0.2.0
19
+
20
+ - Fix multibyte string handling
21
+ - Support JRuby
22
+
23
+ # 2022-04-29 v1.3.0.1.0
24
+
25
+ Promotion of v1.3.0.1.0.beta1 to stable
26
+
27
+ # 2022-04-25 v1.3.0.1.0.beta1
28
+
29
+ - Add obfuscator configuration
30
+ - Add nested object limit configuration
31
+ - Add report ruleset information
32
+
33
+ # 2022-04-29 v1.3.0.0.0
34
+
35
+ - Promote v1.3.0.0.0.beta1 to stable
36
+
37
+ # 2022-04-20 v1.3.0.0.0.beta1
38
+
39
+ - Update to libddwaf 1.3.0
40
+
41
+ # 2022-03-18 v1.2.1.0.0.beta1
42
+
43
+ - Update to libddwaf 1.2.1
44
+ - Fix incorrect types for a few binding functions
45
+
46
+ # 2022-03-04 v1.0.14.2.1.beta1
47
+
48
+ - Fix incorrect return code
49
+ - Fix passing nil in libddwaf object containers
50
+
51
+ # 2022-02-07 v1.0.14.2.0.beta1
52
+
53
+ - Change Datadog::Security to Datadog::AppSec
54
+
55
+ # 2022-02-01 v1.0.14.1.0.beta2
56
+
57
+ - Add support for Ruby 3.1
58
+
59
+ # 2021-12-14 v1.0.14.1.0.beta1
60
+
61
+ - Fix sequential runs on a single context by retaining C input data objects
62
+
63
+ # 2021-11-24 v1.0.14.0.0.beta1
64
+
65
+ - Update to libddwaf 1.0.14
66
+
67
+ # 2021-11-24 v1.0.13.0.0.beta1
68
+
69
+ - Add ruby platform fallback for unsupported platforms
70
+ - Update to libddwaf 1.0.13
71
+
72
+ # 2021-10-13 v1.0.12.0.0.beta1
73
+
74
+ - Initial release
data/LICENSE ADDED
@@ -0,0 +1,6 @@
1
+ ## License
2
+
3
+ This work is dual-licensed under Apache 2.0 or BSD3.
4
+ You may select, at your option, one of the above-listed licenses.
5
+
6
+ `SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause`
@@ -0,0 +1,2 @@
1
+ Component,Origin,License,Copyright
2
+ core,https://github.com/ffi/ffi,BSD-3-Clause,"Copyright (c) 2008-2016, Ruby FFI project contributors"
data/LICENSE.Apache ADDED
@@ -0,0 +1,200 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "{}"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2021 Datadog, Inc.
190
+ Licensed under the Apache License, Version 2.0 (the "License");
191
+ you may not use this file except in compliance with the License.
192
+ You may obtain a copy of the License at
193
+
194
+ http://www.apache.org/licenses/LICENSE-2.0
195
+
196
+ Unless required by applicable law or agreed to in writing, software
197
+ distributed under the License is distributed on an "AS IS" BASIS,
198
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
199
+ See the License for the specific language governing permissions and
200
+ limitations under the License.
data/LICENSE.BSD3 ADDED
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2021, Datadog <info@datadoghq.com>
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+ * Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+ * Neither the name of Datadog nor the
12
+ names of its contributors may be used to endorse or promote products
13
+ derived from this software without specific prior written permission.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
19
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/NOTICE ADDED
@@ -0,0 +1,4 @@
1
+ Datadog libddwaf-rb
2
+ Copyright 2021-Present Datadog, Inc.
3
+
4
+ This product includes software developed at Datadog, Inc. (https://www.datadoghq.com/).
@@ -0,0 +1,11 @@
1
+ module Datadog
2
+ module AppSec
3
+ module WAF
4
+ module VERSION
5
+ BASE_STRING = '1.5.1'
6
+ STRING = "#{BASE_STRING}.0.0"
7
+ MINIMUM_RUBY_VERSION = '2.1'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,703 @@
1
+ require 'ffi'
2
+ require 'json'
3
+ require 'datadog/appsec/waf/version'
4
+
5
+ module Datadog
6
+ module AppSec
7
+ module WAF
8
+ module LibDDWAF
9
+ class Error < StandardError
10
+ attr_reader :ruleset_info
11
+
12
+ def initialize(msg, ruleset_info: nil)
13
+ @ruleset_info = ruleset_info
14
+ end
15
+ end
16
+
17
+ extend ::FFI::Library
18
+
19
+ def self.local_os
20
+ if RUBY_ENGINE == 'jruby'
21
+ os_name = java.lang.System.get_property('os.name')
22
+
23
+ os = case os_name
24
+ when /linux/i then 'linux'
25
+ when /mac/i then 'darwin'
26
+ else raise Error, "unsupported JRuby os.name: #{os_name.inspect}"
27
+ end
28
+
29
+ return os
30
+ end
31
+
32
+ Gem::Platform.local.os
33
+ end
34
+
35
+ def self.local_version
36
+ return nil unless local_os == 'linux'
37
+
38
+ # Old rubygems don't handle non-gnu linux correctly
39
+ return $1 if RUBY_PLATFORM =~ /linux-(.+)$/
40
+
41
+ 'gnu'
42
+ end
43
+
44
+ def self.local_cpu
45
+ if RUBY_ENGINE == 'jruby'
46
+ os_arch = java.lang.System.get_property('os.arch')
47
+
48
+ cpu = case os_arch
49
+ when 'amd64' then 'x86_64'
50
+ when 'aarch64' then 'aarch64'
51
+ else raise Error, "unsupported JRuby os.arch: #{os_arch.inspect}"
52
+ end
53
+
54
+ return cpu
55
+ end
56
+
57
+ Gem::Platform.local.cpu
58
+ end
59
+
60
+ def self.source_dir
61
+ __dir__ || raise('__dir__ is nil: eval?')
62
+ end
63
+
64
+ def self.vendor_dir
65
+ File.join(source_dir, '../../../vendor')
66
+ end
67
+
68
+ def self.libddwaf_vendor_dir
69
+ File.join(vendor_dir, 'libddwaf')
70
+ end
71
+
72
+ def self.shared_lib_triplet(version: local_version)
73
+ version ? "#{local_os}-#{version}-#{local_cpu}" : "#{local_os}-#{local_cpu}"
74
+ end
75
+
76
+ def self.libddwaf_dir
77
+ default = File.join(libddwaf_vendor_dir,
78
+ "libddwaf-#{Datadog::AppSec::WAF::VERSION::BASE_STRING}-#{shared_lib_triplet}")
79
+ candidates = [
80
+ default
81
+ ]
82
+
83
+ if local_os == 'linux'
84
+ candidates << File.join(libddwaf_vendor_dir,
85
+ "libddwaf-#{Datadog::AppSec::WAF::VERSION::BASE_STRING}-#{shared_lib_triplet(version: nil)}")
86
+ end
87
+
88
+ candidates.find { |d| Dir.exist?(d) } || default
89
+ end
90
+
91
+ def self.shared_lib_extname
92
+ Gem::Platform.local.os == 'darwin' ? '.dylib' : '.so'
93
+ end
94
+
95
+ def self.shared_lib_path
96
+ File.join(libddwaf_dir, 'lib', "libddwaf#{shared_lib_extname}")
97
+ end
98
+
99
+ ffi_lib [shared_lib_path]
100
+
101
+ # version
102
+
103
+ attach_function :ddwaf_get_version, [], :string
104
+
105
+ # ddwaf::object data structure
106
+
107
+ DDWAF_OBJ_TYPE = enum :ddwaf_obj_invalid, 0,
108
+ :ddwaf_obj_signed, 1 << 0,
109
+ :ddwaf_obj_unsigned, 1 << 1,
110
+ :ddwaf_obj_string, 1 << 2,
111
+ :ddwaf_obj_array, 1 << 3,
112
+ :ddwaf_obj_map, 1 << 4,
113
+ :ddwaf_obj_bool, 1 << 5
114
+ typedef DDWAF_OBJ_TYPE, :ddwaf_obj_type
115
+
116
+ typedef :pointer, :charptr
117
+ typedef :pointer, :charptrptr
118
+
119
+ class UInt32Ptr < ::FFI::Struct
120
+ layout :value, :uint32
121
+ end
122
+
123
+ typedef UInt32Ptr.by_ref, :uint32ptr
124
+
125
+ class UInt64Ptr < ::FFI::Struct
126
+ layout :value, :uint64
127
+ end
128
+
129
+ typedef UInt64Ptr.by_ref, :uint64ptr
130
+
131
+ class SizeTPtr < ::FFI::Struct
132
+ layout :value, :size_t
133
+ end
134
+
135
+ typedef SizeTPtr.by_ref, :sizeptr
136
+
137
+ class ObjectValueUnion < ::FFI::Union
138
+ layout :stringValue, :charptr,
139
+ :uintValue, :uint64,
140
+ :intValue, :int64,
141
+ :array, :pointer,
142
+ :boolean, :bool
143
+ end
144
+
145
+ class Object < ::FFI::Struct
146
+ layout :parameterName, :charptr,
147
+ :parameterNameLength, :uint64,
148
+ :valueUnion, ObjectValueUnion,
149
+ :nbEntries, :uint64,
150
+ :type, :ddwaf_obj_type
151
+ end
152
+
153
+ typedef Object.by_ref, :ddwaf_object
154
+
155
+ ## setters
156
+
157
+ attach_function :ddwaf_object_invalid, [:ddwaf_object], :ddwaf_object
158
+ attach_function :ddwaf_object_string, [:ddwaf_object, :string], :ddwaf_object
159
+ attach_function :ddwaf_object_stringl, [:ddwaf_object, :charptr, :size_t], :ddwaf_object
160
+ attach_function :ddwaf_object_stringl_nc, [:ddwaf_object, :charptr, :size_t], :ddwaf_object
161
+ attach_function :ddwaf_object_unsigned, [:ddwaf_object, :uint64], :ddwaf_object
162
+ attach_function :ddwaf_object_signed, [:ddwaf_object, :int64], :ddwaf_object
163
+ attach_function :ddwaf_object_unsigned_force, [:ddwaf_object, :uint64], :ddwaf_object
164
+ attach_function :ddwaf_object_signed_force, [:ddwaf_object, :int64], :ddwaf_object
165
+ attach_function :ddwaf_object_bool, [:ddwaf_object, :bool], :ddwaf_object
166
+
167
+ attach_function :ddwaf_object_array, [:ddwaf_object], :ddwaf_object
168
+ attach_function :ddwaf_object_array_add, [:ddwaf_object, :ddwaf_object], :bool
169
+
170
+ attach_function :ddwaf_object_map, [:ddwaf_object], :ddwaf_object
171
+ attach_function :ddwaf_object_map_add, [:ddwaf_object, :string, :pointer], :bool
172
+ attach_function :ddwaf_object_map_addl, [:ddwaf_object, :charptr, :size_t, :pointer], :bool
173
+ attach_function :ddwaf_object_map_addl_nc, [:ddwaf_object, :charptr, :size_t, :pointer], :bool
174
+
175
+ ## getters
176
+
177
+ attach_function :ddwaf_object_type, [:ddwaf_object], DDWAF_OBJ_TYPE
178
+ attach_function :ddwaf_object_size, [:ddwaf_object], :uint64
179
+ attach_function :ddwaf_object_length, [:ddwaf_object], :size_t
180
+ attach_function :ddwaf_object_get_key, [:ddwaf_object, :sizeptr], :charptr
181
+ attach_function :ddwaf_object_get_string, [:ddwaf_object, :sizeptr], :charptr
182
+ attach_function :ddwaf_object_get_unsigned, [:ddwaf_object], :uint64
183
+ attach_function :ddwaf_object_get_signed, [:ddwaf_object], :int64
184
+ attach_function :ddwaf_object_get_index, [:ddwaf_object, :size_t], :ddwaf_object
185
+
186
+ ## freeers
187
+
188
+ ObjectFree = attach_function :ddwaf_object_free, [:ddwaf_object], :void
189
+ ObjectNoFree = ::FFI::Pointer::NULL
190
+
191
+ # main handle
192
+
193
+ typedef :pointer, :ddwaf_handle
194
+ typedef Object.by_ref, :ddwaf_rule
195
+
196
+ callback :ddwaf_object_free_fn, [:ddwaf_object], :void
197
+
198
+ class Config < ::FFI::Struct
199
+ class Limits < ::FFI::Struct
200
+ layout :max_container_size, :uint32,
201
+ :max_container_depth, :uint32,
202
+ :max_string_length, :uint32
203
+ end
204
+
205
+ class Obfuscator < ::FFI::Struct
206
+ layout :key_regex, :pointer, # :charptr
207
+ :value_regex, :pointer # :charptr
208
+ end
209
+
210
+ layout :limits, Limits,
211
+ :obfuscator, Obfuscator,
212
+ :free_fn, :pointer #:ddwaf_object_free_fn
213
+ end
214
+
215
+ typedef Config.by_ref, :ddwaf_config
216
+
217
+ class RuleSetInfo < ::FFI::Struct
218
+ layout :loaded, :uint16,
219
+ :failed, :uint16,
220
+ :errors, Object,
221
+ :version, :string
222
+ end
223
+
224
+ typedef RuleSetInfo.by_ref, :ddwaf_ruleset_info
225
+ RuleSetInfoNone = Datadog::AppSec::WAF::LibDDWAF::RuleSetInfo.new(::FFI::Pointer::NULL)
226
+
227
+ attach_function :ddwaf_ruleset_info_free, [:ddwaf_ruleset_info], :void
228
+
229
+ attach_function :ddwaf_init, [:ddwaf_rule, :ddwaf_config, :ddwaf_ruleset_info], :ddwaf_handle
230
+ attach_function :ddwaf_destroy, [:ddwaf_handle], :void
231
+
232
+ attach_function :ddwaf_required_addresses, [:ddwaf_handle, UInt32Ptr], :charptrptr
233
+ attach_function :ddwaf_required_rule_data_ids, [:ddwaf_handle, UInt32Ptr], :charptrptr
234
+
235
+ # updating
236
+
237
+ DDWAF_RET_CODE = enum :ddwaf_err_internal, -3,
238
+ :ddwaf_err_invalid_object, -2,
239
+ :ddwaf_err_invalid_argument, -1,
240
+ :ddwaf_ok, 0,
241
+ :ddwaf_match, 1
242
+ typedef DDWAF_RET_CODE, :ddwaf_ret_code
243
+
244
+ attach_function :ddwaf_update_rule_data, [:ddwaf_handle, :ddwaf_object], :ddwaf_ret_code
245
+ attach_function :ddwaf_toggle_rules, [:ddwaf_handle, :ddwaf_object], :ddwaf_ret_code
246
+
247
+ # running
248
+
249
+ typedef :pointer, :ddwaf_context
250
+
251
+ attach_function :ddwaf_context_init, [:ddwaf_handle], :ddwaf_context
252
+ attach_function :ddwaf_context_destroy, [:ddwaf_context], :void
253
+
254
+ class ResultActions < ::FFI::Struct
255
+ layout :array, :charptrptr,
256
+ :size, :uint32
257
+ end
258
+
259
+ class Result < ::FFI::Struct
260
+ layout :timeout, :bool,
261
+ :data, :string,
262
+ :actions, ResultActions,
263
+ :total_runtime, :uint64
264
+ end
265
+
266
+ typedef Result.by_ref, :ddwaf_result
267
+ typedef :uint64, :timeout_us
268
+
269
+ attach_function :ddwaf_run, [:ddwaf_context, :ddwaf_object, :ddwaf_result, :timeout_us], :ddwaf_ret_code, blocking: true
270
+ attach_function :ddwaf_result_free, [:ddwaf_result], :void
271
+
272
+ # logging
273
+
274
+ DDWAF_LOG_LEVEL = enum :ddwaf_log_trace,
275
+ :ddwaf_log_debug,
276
+ :ddwaf_log_info,
277
+ :ddwaf_log_warn,
278
+ :ddwaf_log_error,
279
+ :ddwaf_log_off
280
+ typedef DDWAF_LOG_LEVEL, :ddwaf_log_level
281
+
282
+ callback :ddwaf_log_cb, [:ddwaf_log_level, :string, :string, :uint, :charptr, :uint64], :void
283
+
284
+ attach_function :ddwaf_set_log_cb, [:ddwaf_log_cb, :ddwaf_log_level], :bool
285
+
286
+ DEFAULT_MAX_CONTAINER_SIZE = 0
287
+ DEFAULT_MAX_CONTAINER_DEPTH = 0
288
+ DEFAULT_MAX_STRING_LENGTH = 0
289
+
290
+ DDWAF_MAX_CONTAINER_SIZE = 256
291
+ DDWAF_MAX_CONTAINER_DEPTH = 20
292
+ DDWAF_MAX_STRING_LENGTH = 4096
293
+
294
+ DDWAF_RUN_TIMEOUT = 5000
295
+ end
296
+
297
+ def self.version
298
+ LibDDWAF.ddwaf_get_version
299
+ end
300
+
301
+ def self.ruby_to_object(val, max_container_size: nil, max_container_depth: nil, max_string_length: nil, coerce: true)
302
+ case val
303
+ when Array
304
+ obj = LibDDWAF::Object.new
305
+ res = LibDDWAF.ddwaf_object_array(obj)
306
+ if res.null?
307
+ fail LibDDWAF::Error, "Could not convert into object: #{val}"
308
+ end
309
+
310
+ max_index = max_container_size - 1 if max_container_size
311
+ val.each.with_index do |e, i|
312
+ member = ruby_to_object(e,
313
+ max_container_size: max_container_size,
314
+ max_container_depth: (max_container_depth - 1 if max_container_depth),
315
+ max_string_length: max_string_length,
316
+ coerce: coerce)
317
+ e_res = LibDDWAF.ddwaf_object_array_add(obj, member)
318
+ unless e_res
319
+ fail LibDDWAF::Error, "Could not add to array object: #{e.inspect}"
320
+ end
321
+
322
+ break val if max_index && i >= max_index
323
+ end unless max_container_depth == 0
324
+
325
+ obj
326
+ when Hash
327
+ obj = LibDDWAF::Object.new
328
+ res = LibDDWAF.ddwaf_object_map(obj)
329
+ if res.null?
330
+ fail LibDDWAF::Error, "Could not convert into object: #{val}"
331
+ end
332
+
333
+ max_index = max_container_size - 1 if max_container_size
334
+ val.each.with_index do |e, i|
335
+ k, v = e[0], e[1] # for Steep, which doesn't handle |(k, v), i|
336
+
337
+ k = k.to_s[0, max_string_length] if max_string_length
338
+ member = ruby_to_object(v,
339
+ max_container_size: max_container_size,
340
+ max_container_depth: (max_container_depth - 1 if max_container_depth),
341
+ max_string_length: max_string_length,
342
+ coerce: coerce)
343
+ kv_res = LibDDWAF.ddwaf_object_map_addl(obj, k.to_s, k.to_s.bytesize, member)
344
+ unless kv_res
345
+ fail LibDDWAF::Error, "Could not add to map object: #{k.inspect} => #{v.inspect}"
346
+ end
347
+
348
+ break val if max_index && i >= max_index
349
+ end unless max_container_depth == 0
350
+
351
+ obj
352
+ when String
353
+ obj = LibDDWAF::Object.new
354
+ val = val.to_s[0, max_string_length] if max_string_length
355
+ str = val.to_s
356
+ res = LibDDWAF.ddwaf_object_stringl(obj, str, str.bytesize)
357
+ if res.null?
358
+ fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
359
+ end
360
+
361
+ obj
362
+ when Symbol
363
+ obj = LibDDWAF::Object.new
364
+ val = val.to_s[0, max_string_length] if max_string_length
365
+ str = val.to_s
366
+ res = LibDDWAF.ddwaf_object_stringl(obj, str, str.bytesize)
367
+ if res.null?
368
+ fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
369
+ end
370
+
371
+ obj
372
+ when Integer
373
+ obj = LibDDWAF::Object.new
374
+ res = if coerce
375
+ LibDDWAF.ddwaf_object_string(obj, val.to_s)
376
+ elsif val < 0
377
+ LibDDWAF.ddwaf_object_signed_force(obj, val)
378
+ else
379
+ LibDDWAF.ddwaf_object_unsigned_force(obj, val)
380
+ end
381
+ if res.null?
382
+ fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
383
+ end
384
+
385
+ obj
386
+ when Float
387
+ obj = LibDDWAF::Object.new
388
+ res = LibDDWAF.ddwaf_object_string(obj, val.to_s)
389
+ if res.null?
390
+ fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
391
+ end
392
+
393
+ obj
394
+ when TrueClass, FalseClass
395
+ obj = LibDDWAF::Object.new
396
+ res = if coerce
397
+ LibDDWAF.ddwaf_object_string(obj, val.to_s)
398
+ else
399
+ LibDDWAF.ddwaf_object_bool(obj, val)
400
+ end
401
+ if res.null?
402
+ fail LibDDWAF::Error, "Could not convert into object: #{val.inspect}"
403
+ end
404
+
405
+ obj
406
+ else
407
+ ruby_to_object(''.freeze)
408
+ end
409
+ end
410
+
411
+ def self.object_to_ruby(obj)
412
+ case obj[:type]
413
+ when :ddwaf_obj_invalid
414
+ nil
415
+ when :ddwaf_obj_bool
416
+ obj[:valueUnion][:boolean]
417
+ when :ddwaf_obj_string
418
+ obj[:valueUnion][:stringValue].read_bytes(obj[:nbEntries])
419
+ when :ddwaf_obj_signed
420
+ obj[:valueUnion][:intValue]
421
+ when :ddwaf_obj_unsigned
422
+ obj[:valueUnion][:uintValue]
423
+ when :ddwaf_obj_array
424
+ (0...obj[:nbEntries]).each.with_object([]) do |i, a|
425
+ ptr = obj[:valueUnion][:array] + i * LibDDWAF::Object.size
426
+ e = object_to_ruby(LibDDWAF::Object.new(ptr))
427
+ a << e
428
+ end
429
+ when :ddwaf_obj_map
430
+ (0...obj[:nbEntries]).each.with_object({}) do |i, h|
431
+ ptr = obj[:valueUnion][:array] + i * Datadog::AppSec::WAF::LibDDWAF::Object.size
432
+ o = Datadog::AppSec::WAF::LibDDWAF::Object.new(ptr)
433
+ l = o[:parameterNameLength]
434
+ k = o[:parameterName].read_bytes(l)
435
+ v = object_to_ruby(LibDDWAF::Object.new(ptr))
436
+ h[k] = v
437
+ end
438
+ end
439
+ end
440
+
441
+ def self.log_callback(level, func, file, line, message, len)
442
+ return if logger.nil?
443
+
444
+ logger.debug do
445
+ {
446
+ level: level,
447
+ func: func,
448
+ file: file,
449
+ line: line,
450
+ message: message.read_bytes(len)
451
+ }.inspect
452
+ end
453
+ end
454
+
455
+ def self.logger
456
+ @logger
457
+ end
458
+
459
+ def self.logger=(logger)
460
+ unless @log_callback
461
+ log_callback = method(:log_callback)
462
+ Datadog::AppSec::WAF::LibDDWAF.ddwaf_set_log_cb(log_callback, :ddwaf_log_trace)
463
+
464
+ # retain logging proc if set properly
465
+ @log_callback = log_callback
466
+ end
467
+
468
+ @logger = logger
469
+ end
470
+
471
+ RESULT_CODE = {
472
+ ddwaf_err_internal: :err_internal,
473
+ ddwaf_err_invalid_object: :err_invalid_object,
474
+ ddwaf_err_invalid_argument: :err_invalid_argument,
475
+ ddwaf_ok: :ok,
476
+ ddwaf_match: :match,
477
+ }
478
+
479
+ class Handle
480
+ attr_reader :handle_obj
481
+
482
+ attr_reader :ruleset_info
483
+
484
+ def initialize(rule, limits: {}, obfuscator: {})
485
+ rule_obj = Datadog::AppSec::WAF.ruby_to_object(rule)
486
+ if rule_obj.null? || rule_obj[:type] == :ddwaf_object_invalid
487
+ fail LibDDWAF::Error, "Could not convert object #{rule.inspect}"
488
+ end
489
+
490
+ config_obj = Datadog::AppSec::WAF::LibDDWAF::Config.new
491
+ if config_obj.null?
492
+ fail LibDDWAF::Error, 'Could not create config struct'
493
+ end
494
+ retain(config_obj)
495
+
496
+ config_obj[:limits][:max_container_size] = limits[:max_container_size] || LibDDWAF::DEFAULT_MAX_CONTAINER_SIZE
497
+ config_obj[:limits][:max_container_depth] = limits[:max_container_depth] || LibDDWAF::DEFAULT_MAX_CONTAINER_DEPTH
498
+ config_obj[:limits][:max_string_length] = limits[:max_string_length] || LibDDWAF::DEFAULT_MAX_STRING_LENGTH
499
+ config_obj[:obfuscator][:key_regex] = FFI::MemoryPointer.from_string(obfuscator[:key_regex]) if obfuscator[:key_regex]
500
+ config_obj[:obfuscator][:value_regex] = FFI::MemoryPointer.from_string(obfuscator[:value_regex]) if obfuscator[:value_regex]
501
+ config_obj[:free_fn] = Datadog::AppSec::WAF::LibDDWAF::ObjectNoFree
502
+
503
+ ruleset_info = LibDDWAF::RuleSetInfo.new
504
+
505
+ @handle_obj = Datadog::AppSec::WAF::LibDDWAF.ddwaf_init(rule_obj, config_obj, ruleset_info)
506
+
507
+ @ruleset_info = {
508
+ loaded: ruleset_info[:loaded],
509
+ failed: ruleset_info[:failed],
510
+ errors: WAF.object_to_ruby(ruleset_info[:errors]),
511
+ version: ruleset_info[:version],
512
+ }
513
+
514
+ if @handle_obj.null?
515
+ fail LibDDWAF::Error.new('Could not create handle', ruleset_info: @ruleset_info)
516
+ end
517
+
518
+ validate!
519
+ ensure
520
+ Datadog::AppSec::WAF::LibDDWAF.ddwaf_ruleset_info_free(ruleset_info) if ruleset_info
521
+ Datadog::AppSec::WAF::LibDDWAF.ddwaf_object_free(rule_obj) if rule_obj
522
+ end
523
+
524
+ def finalize
525
+ invalidate!
526
+
527
+ Datadog::AppSec::WAF::LibDDWAF.ddwaf_destroy(handle_obj)
528
+ end
529
+
530
+ def required_addresses
531
+ valid!
532
+
533
+ count = Datadog::AppSec::WAF::LibDDWAF::UInt32Ptr.new
534
+ list = Datadog::AppSec::WAF::LibDDWAF.ddwaf_required_addresses(handle_obj, count)
535
+
536
+ return [] if count == 0 # list is null
537
+
538
+ list.get_array_of_string(0, count[:value])
539
+ end
540
+
541
+ def update_rule_data(data)
542
+ res = Datadog::AppSec::WAF::LibDDWAF.ddwaf_update_rule_data(@handle_obj, Datadog::AppSec::WAF.ruby_to_object(data, coerce: false))
543
+
544
+ RESULT_CODE[res]
545
+ end
546
+
547
+ def toggle_rules(map)
548
+ res = Datadog::AppSec::WAF::LibDDWAF.ddwaf_toggle_rules(@handle_obj, Datadog::AppSec::WAF.ruby_to_object(map, coerce: false))
549
+
550
+ RESULT_CODE[res]
551
+ end
552
+
553
+ private
554
+
555
+ def validate!
556
+ @valid = true
557
+ end
558
+
559
+ def invalidate!
560
+ @valid = false
561
+ end
562
+
563
+ def valid?
564
+ @valid
565
+ end
566
+
567
+ def valid!
568
+ return if valid?
569
+
570
+ fail LibDDWAF::Error, "Attempt to use an invalid instance: #{inspect}"
571
+ end
572
+
573
+ def retained
574
+ @retained ||= []
575
+ end
576
+
577
+ def retain(object)
578
+ retained << object
579
+ end
580
+
581
+ def release(object)
582
+ retained.delete(object)
583
+ end
584
+ end
585
+
586
+ class Result
587
+ attr_reader :status, :data, :total_runtime, :timeout, :actions
588
+
589
+ def initialize(status, data, total_runtime, timeout, actions)
590
+ @status = status
591
+ @data = data
592
+ @total_runtime = total_runtime
593
+ @timeout = timeout
594
+ @actions = actions
595
+ end
596
+ end
597
+
598
+ class Context
599
+ attr_reader :context_obj
600
+
601
+ def initialize(handle)
602
+ handle_obj = handle.handle_obj
603
+ retain(handle)
604
+
605
+ @context_obj = Datadog::AppSec::WAF::LibDDWAF.ddwaf_context_init(handle_obj)
606
+ if @context_obj.null?
607
+ fail LibDDWAF::Error, 'Could not create context'
608
+ end
609
+
610
+ validate!
611
+ end
612
+
613
+ def finalize
614
+ invalidate!
615
+
616
+ retained.each do |retained_obj|
617
+ next unless retained_obj.is_a?(Datadog::AppSec::WAF::LibDDWAF::Object)
618
+
619
+ Datadog::AppSec::WAF::LibDDWAF.ddwaf_object_free(retained_obj)
620
+ end
621
+
622
+ Datadog::AppSec::WAF::LibDDWAF.ddwaf_context_destroy(context_obj)
623
+ end
624
+
625
+ def run(input, timeout = LibDDWAF::DDWAF_RUN_TIMEOUT)
626
+ valid!
627
+
628
+ max_container_size = LibDDWAF::DDWAF_MAX_CONTAINER_SIZE
629
+ max_container_depth = LibDDWAF::DDWAF_MAX_CONTAINER_DEPTH
630
+ max_string_length = LibDDWAF::DDWAF_MAX_CONTAINER_SIZE
631
+
632
+ input_obj = Datadog::AppSec::WAF.ruby_to_object(input,
633
+ max_container_size: max_container_size,
634
+ max_container_depth: max_container_depth,
635
+ max_string_length: max_string_length)
636
+ if input_obj.null?
637
+ fail LibDDWAF::Error, "Could not convert input: #{input.inspect}"
638
+ end
639
+
640
+ result_obj = Datadog::AppSec::WAF::LibDDWAF::Result.new
641
+ if result_obj.null?
642
+ fail LibDDWAF::Error, "Could not create result object"
643
+ end
644
+
645
+ # retain C objects in memory for subsequent calls to run
646
+ retain(input_obj)
647
+
648
+ code = Datadog::AppSec::WAF::LibDDWAF.ddwaf_run(@context_obj, input_obj, result_obj, timeout)
649
+
650
+ actions = if result_obj[:actions][:size] > 0
651
+ result_obj[:actions][:array].get_array_of_string(0, result_obj[:actions][:size])
652
+ else
653
+ []
654
+ end
655
+
656
+ result = Result.new(
657
+ RESULT_CODE[code],
658
+ (JSON.parse(result_obj[:data]) if result_obj[:data] != nil),
659
+ result_obj[:total_runtime],
660
+ result_obj[:timeout],
661
+ actions,
662
+ )
663
+
664
+ [RESULT_CODE[code], result]
665
+ ensure
666
+ Datadog::AppSec::WAF::LibDDWAF.ddwaf_result_free(result_obj) if result_obj
667
+ end
668
+
669
+ private
670
+
671
+ def validate!
672
+ @valid = true
673
+ end
674
+
675
+ def invalidate!
676
+ @valid = false
677
+ end
678
+
679
+ def valid?
680
+ @valid
681
+ end
682
+
683
+ def valid!
684
+ return if valid?
685
+
686
+ fail LibDDWAF::Error, "Attempt to use an invalid instance: #{inspect}"
687
+ end
688
+
689
+ def retained
690
+ @retained ||= []
691
+ end
692
+
693
+ def retain(object)
694
+ retained << object
695
+ end
696
+
697
+ def release(object)
698
+ retained.delete(object)
699
+ end
700
+ end
701
+ end
702
+ end
703
+ end
data/lib/libddwaf.rb ADDED
@@ -0,0 +1 @@
1
+ require 'datadog/appsec/waf'
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: libddwaf
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.5.1.0.0
5
+ platform: java
6
+ authors:
7
+ - Datadog, Inc.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-10-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ffi
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ description: 'libddwaf packages a WAF implementation in C++, exposed to Ruby
28
+
29
+ '
30
+ email:
31
+ - dev@datadoghq.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - CHANGELOG.md
37
+ - LICENSE
38
+ - LICENSE-3rdparty.csv
39
+ - LICENSE.Apache
40
+ - LICENSE.BSD3
41
+ - NOTICE
42
+ - lib/datadog/appsec/waf.rb
43
+ - lib/datadog/appsec/waf/version.rb
44
+ - lib/libddwaf.rb
45
+ - vendor/libddwaf/libddwaf-1.5.1-darwin-arm64/lib/libddwaf.dylib
46
+ - vendor/libddwaf/libddwaf-1.5.1-darwin-x86_64/lib/libddwaf.dylib
47
+ - vendor/libddwaf/libddwaf-1.5.1-linux-aarch64/lib/libddwaf.so
48
+ - vendor/libddwaf/libddwaf-1.5.1-linux-x86_64/lib/libddwaf.so
49
+ homepage: https://github.com/DataDog/libddwaf-rb
50
+ licenses:
51
+ - BSD-3-Clause
52
+ metadata:
53
+ allowed_push_host: https://rubygems.org
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '2.1'
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: 2.0.0
68
+ requirements: []
69
+ rubygems_version: 3.1.2
70
+ signing_key:
71
+ specification_version: 4
72
+ summary: Datadog WAF
73
+ test_files: []