libddwaf 1.15.0.0.0 → 1.18.0.0.1

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: 5c43525cce0eabb3c9e5f98ca93309313d81dceeb0f905b07ec98039dcff622e
4
- data.tar.gz: 61db3b159cbebdfa9b93c6d09c974538765ef046077586550b14cfebcd219033
3
+ metadata.gz: 4fad2f4be2e60e15913e130d28130af2f3d3d6d40aea05dba4a1d3d967cc4c12
4
+ data.tar.gz: aac3e2f4ae75f5a9387341997ab07861f1b8ee8d0ae63cc6ad97bd99b421b132
5
5
  SHA512:
6
- metadata.gz: fbda3101720816799e5857a4efaaf7d97522f085b3be2fbd10451856db83624270893700fe41f0eb0af3a9c6758557c230417f7688441d30ba98c2503df999c7
7
- data.tar.gz: 796c08137efa8662676e83552cb0b37f75bd7f80ef2b6f73875c2c77ba28f2c2ca1705b515661ea3e96fc72c2df9ef6598dc49af22951df41f90a2a133c175f4
6
+ metadata.gz: 83b667e79adbefbf6d52c3dd425ff2bd973d1a9e9a32c3ca817e08963c86ab9bbf3002e54d7a57fbbfc6e86a0e6254350d6b02e700e4af84852ce8a9d1936ab6
7
+ data.tar.gz: e242033561aa9e4dfd3d66c44170ea5595f27499ee6fe30bfbff66bd7af61edb5c2986494bbb4b986a7bf4b005da243b9df66ce7d82c78bc8b215cc03f86913c
@@ -1,17 +1,12 @@
1
1
  name: Build docker image
2
2
  description:
3
- Github Actions does not support setting the platform for the container yet.
4
- This action builds the image for the specified architecture and libc.
3
+ This action builds the image for the specified ruby version and libc.
5
4
 
6
5
  inputs:
7
6
  ruby-version:
8
7
  description: Ruby version
9
8
  required: true
10
9
 
11
- arch:
12
- description: Build architecture
13
- required: true
14
-
15
10
  libc:
16
11
  description: Which libc is used
17
12
  required: true
@@ -29,11 +24,6 @@ outputs:
29
24
  runs:
30
25
  using: "composite"
31
26
  steps:
32
- - name: Set up QEMU
33
- uses: docker/setup-qemu-action@v3
34
- with:
35
- platforms: ${{ inputs.arch }}
36
-
37
27
  - name: Set up Docker Buildx
38
28
  uses: docker/setup-buildx-action@v3
39
29
 
@@ -48,10 +38,9 @@ runs:
48
38
  tags: libddwaf-rb-test:latest
49
39
  cache-from: type=gha
50
40
  cache-to: type=gha,mode=max
51
- platforms: linux/${{ inputs.arch }}
52
41
 
53
42
  - name: Set run-cmd output
54
43
  id: set-run-cmd
55
44
  shell: bash
56
45
  run: |
57
- echo "run-cmd=docker run --platform linux/${{ inputs.arch }} -v gems:/usr/local/bundle -v ${{ github.workspace }}:/libddwaf-rb -w /libddwaf-rb libddwaf-rb-test:latest" >> "$GITHUB_OUTPUT"
46
+ echo "run-cmd=docker run -v gems:/usr/local/bundle -v ${{ github.workspace }}:/libddwaf-rb -w /libddwaf-rb libddwaf-rb-test:latest" >> "$GITHUB_OUTPUT"
@@ -7,17 +7,16 @@ jobs:
7
7
  strategy:
8
8
  fail-fast: false
9
9
  matrix:
10
- os: [ubuntu-24.04]
10
+ os: [ubuntu-24.04, ubuntu-24.04-arm]
11
11
  ruby: ["3.3"]
12
- arch: [amd64, arm64]
13
12
  libc: [gnu]
14
13
  include:
15
- - arch: amd64
14
+ - os: ubuntu-24.04
16
15
  platform: x86_64-linux
17
- - arch: arm64
16
+ - os: ubuntu-24.04-arm
18
17
  platform: aarch64-linux
19
18
 
20
- name: Test build without fetching libddwaf (Ruby ${{ matrix.ruby }}, ${{ matrix.arch }}, ${{ matrix.libc }})
19
+ name: Test build without fetching libddwaf (Ruby ${{ matrix.ruby }}, ${{ matrix.os }}, ${{ matrix.libc }})
21
20
  runs-on: ${{ matrix.os }}
22
21
 
23
22
  steps:
@@ -29,7 +28,6 @@ jobs:
29
28
  uses: ./.github/actions/docker-build-ruby
30
29
  with:
31
30
  ruby-version: ${{ matrix.ruby }}
32
- arch: ${{ matrix.arch }}
33
31
  libc: ${{ matrix.libc }}
34
32
 
35
33
  - name: Bundle install
@@ -52,14 +50,13 @@ jobs:
52
50
  strategy:
53
51
  fail-fast: false
54
52
  matrix:
55
- os: [ubuntu-24.04]
53
+ os: [ubuntu-24.04, ubuntu-24.04-arm]
56
54
  ruby: ["3.3", "9.4"]
57
- arch: [amd64, arm64]
58
55
  libc: [gnu, musl]
59
56
  include:
60
- - arch: amd64
57
+ - os: ubuntu-24.04
61
58
  platform: x86_64-linux:llvm
62
- - arch: arm64
59
+ - os: ubuntu-24.04-arm
63
60
  platform: aarch64-linux:llvm
64
61
  - ruby: 3.3
65
62
  jruby: false
@@ -69,7 +66,7 @@ jobs:
69
66
  - ruby: 9.4
70
67
  libc: musl
71
68
 
72
- name: Test gem build (${{ matrix.jruby == true && 'Jruby' || 'Ruby'}} ${{ matrix.ruby }}, ${{ matrix.arch }}, ${{ matrix.libc }})
69
+ name: Test gem build (${{ matrix.jruby == true && 'Jruby' || 'Ruby'}} ${{ matrix.ruby }}, ${{ matrix.os }}, ${{ matrix.libc }})
73
70
  runs-on: ${{ matrix.os }}
74
71
 
75
72
  steps:
@@ -81,7 +78,6 @@ jobs:
81
78
  uses: ./.github/actions/docker-build-ruby
82
79
  with:
83
80
  ruby-version: ${{ matrix.ruby }}
84
- arch: ${{ matrix.arch }}
85
81
  libc: ${{ matrix.libc }}
86
82
  jruby: ${{ matrix.jruby }}
87
83
 
@@ -7,17 +7,16 @@ jobs:
7
7
  strategy:
8
8
  fail-fast: false
9
9
  matrix:
10
- os: [ubuntu-24.04]
11
- ruby: ["2.5", "2.6", "2.7", "3.0", "3.1", "3.2", "3.3"]
12
- arch: [amd64, arm64]
10
+ os: [ubuntu-24.04, ubuntu-24.04-arm]
11
+ ruby: ["2.5", "2.6", "2.7", "3.0", "3.1", "3.2", "3.3", "3.4"]
13
12
  libc: [gnu, musl]
14
13
  include:
15
- - arch: amd64
14
+ - os: ubuntu-24.04
16
15
  platform: x86_64-linux
17
- - arch: arm64
16
+ - os: ubuntu-24.04-arm
18
17
  platform: aarch64-linux
19
18
 
20
- name: Test (Ruby ${{ matrix.ruby }}, ${{ matrix.arch }}, ${{ matrix.libc }})
19
+ name: Test (Ruby ${{ matrix.ruby }}, ${{ matrix.os }}, ${{ matrix.libc }})
21
20
  runs-on: ${{ matrix.os }}
22
21
 
23
22
  steps:
@@ -29,7 +28,6 @@ jobs:
29
28
  uses: ./.github/actions/docker-build-ruby
30
29
  with:
31
30
  ruby-version: ${{ matrix.ruby }}
32
- arch: ${{ matrix.arch }}
33
31
  libc: ${{ matrix.libc }}
34
32
 
35
33
  - name: Bundle install
@@ -44,20 +42,22 @@ jobs:
44
42
  - name: Run specs
45
43
  run: ${{ steps.build-image.outputs.run-cmd }} bundle exec rake spec
46
44
 
45
+ - name: Run stress tests
46
+ run: ${{ steps.build-image.outputs.run-cmd }} bundle exec rake spec:stress_tests
47
+
47
48
  test-jruby-linux:
48
49
  strategy:
49
50
  fail-fast: false
50
51
  matrix:
51
- os: [ubuntu-24.04]
52
+ os: [ubuntu-24.04, ubuntu-24.04-arm]
52
53
  jruby: ["9.3", "9.4"]
53
- arch: [amd64, arm64]
54
54
  include:
55
- - arch: amd64
55
+ - os: ubuntu-24.04
56
56
  platform: x86_64-linux
57
- - arch: arm64
57
+ - os: ubuntu-24.04-arm
58
58
  platform: aarch64-linux
59
59
 
60
- name: Test (Jruby ${{ matrix.jruby }}, ${{ matrix.arch }})
60
+ name: Test (Jruby ${{ matrix.jruby }}, ${{ matrix.os }})
61
61
  runs-on: ${{ matrix.os }}
62
62
 
63
63
  steps:
@@ -70,7 +70,6 @@ jobs:
70
70
  with:
71
71
  ruby-version: ${{ matrix.jruby }}
72
72
  jruby: true
73
- arch: ${{ matrix.arch }}
74
73
  libc: gnu
75
74
 
76
75
  - name: Bundle install
@@ -85,6 +84,9 @@ jobs:
85
84
  - name: Run specs
86
85
  run: ${{ steps.build-image.outputs.run-cmd }} bundle exec rake spec
87
86
 
87
+ - name: Run stress tests
88
+ run: ${{ steps.build-image.outputs.run-cmd }} bundle exec rake spec:stress_tests
89
+
88
90
  test-darwin:
89
91
  strategy:
90
92
  fail-fast: false
@@ -116,3 +118,6 @@ jobs:
116
118
 
117
119
  - name: Run specs
118
120
  run: bundle exec rake spec
121
+
122
+ - name: Run stress tests
123
+ run: bundle exec rake spec:stress_tests
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