libddwaf 1.22.0.0.2-aarch64-linux → 1.24.1.0.3-aarch64-linux

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: 1c04a4590ede43aeca708d2452c9118b34a46c9820322dc7feb19dc98f982d28
4
- data.tar.gz: d37d7f1c4016d9979e4c4b9ad886d51ef1b2b2b373dc2c1ca2b6381cd6f897f4
3
+ metadata.gz: 94f0f27a08d18b3e1d41ad75c96edd62c01d1d22c2d69e5d6c8f2f4f98bd8172
4
+ data.tar.gz: 732d929052278441c9b5d95cd7f9ad1a52917d4af267b5803d7e90e7760d5532
5
5
  SHA512:
6
- metadata.gz: 9b9c07adb0eb5ff437b3f738b76c1792cfb5c639b4df2a04ceb6ecbbc77672390e9899c64fed615356af1a5abb560799a1b90e8ecfcaa4f5cce2273e6fbace03
7
- data.tar.gz: 11678d6e41a6fcc95c114637ca55d71f77f1c1761d84f6d2e9733cf8bbbc4bb820c6447b1ebe25a834134c882c83f2a482b5e391227e255746a020abde24af8d
6
+ metadata.gz: 43139856cbb68e3be10d6d35aa412783a9396f2038f48f39ec297f2db13046e0185fd2a24e581bf2cb2b0294d9e461a4d9416023208f4468fe58b7778d4c875e
7
+ data.tar.gz: d1f5f32f20ad0abbb544e66dec7e81f655fd1892eca8a3d89f0be771abbab85eacecc50c08fa590e754cbf2b3b48089798c6e28edf84b2b400d84b4c67879284
data/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ # Unreleased v1.23.0.0.0
2
+
3
+ ## Added
4
+
5
+ - Add `HandleBuilder` class for managing libddwaf configuration and building WAF handles
6
+ - Add Error classes:
7
+ - `LibDDWAFError` for libddwaf errors
8
+ - `ConversionError` for conversion errors
9
+ - `InstanceFinalizedError` that is raised when attempting to run methods on finalized instances
10
+
11
+ ## Changed
12
+
13
+ - Change `Handle` instantiation - now it should be done using `HandleBuilder#build_handle` method
14
+ - Change `Context` instantiation - now is should be done using `Handle#build_context` method
15
+ - Change configuration of Limits and obfuscation - it is now done when initializing `HandleBuilder`
16
+ - Change `Context#run` to return a `Result` object instead of an tuple with code and result
17
+
18
+ ## Removed
19
+
20
+ - Remove `WAF::Handle#merge` method - the configuration is now handled by `HandleBuilder`
21
+
1
22
  # 2025-02-20 v.1.18.0.0.1
2
23
 
3
24
  - Fixed memory-leak in `Datadog::AppSec::WAF::Context#run` when non-empty ephemeral data passed
@@ -12,7 +33,6 @@
12
33
  - Update to libddwaf 1.14.0
13
34
  - Add support for `Float` and `Nil` scalar values when converting from ruby to WAF Object and vice versa.
14
35
 
15
-
16
36
  # 2023-08-29 v.1.11.0.0.0
17
37
 
18
38
  - Update to libddwaf 1.11.0
@@ -21,7 +41,6 @@
21
41
  - Changed `Datadog::AppSec::WAF::Result#data` to `Datadog::AppSec::WAF::Result#events`. (Breaking change)
22
42
  The schema of the events variable can be found [here](https://github.com/DataDog/libddwaf/blob/master/schema/events.json)
23
43
 
24
-
25
44
  # 2023-08-28 v.1.10.0.0.0
26
45
 
27
46
  - Update to libddwaf 1.10.0
data/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # libddwaf Ruby bindings
2
+
3
+ ``libddwaf-rb`` is library exposing the libddwaf C++ library to Ruby, packaging it in a multiplatform gem.
4
+
5
+ For the libddwaf implementation, see this repository:
6
+ - [``libddwaf``: libddwaf](https://github.com/DataDog/libddwaf.git)
7
+
8
+
9
+
10
+ ## Rake tasks
11
+
12
+ ### Outline
13
+
14
+ A typical workflow is as follows:
15
+
16
+ ```
17
+ rake fetch # fetch prebuilt libddwaf binaries tarball in vendor/libddwaf
18
+ rake extract # extract downloaded tarball in vendor/libddwaf
19
+ rake spec # run rspec
20
+ rake binary # build the gem
21
+ ```
22
+
23
+ Note that each depends on the previous one, but `fetch` and `extract` are lazy, which proves useful to produce manual builds.
24
+
25
+ ### Platform selection
26
+
27
+ By default the above will automatically use the local Ruby platform.
28
+
29
+ Since libddwaf binary builds are available upstream, it's possible to build gems for any platform on any other platform. To that end `fetch`, `extract`, and `binary` can take an argument to specify the Ruby platform for which these operations should apply:
30
+
31
+ ```
32
+ rake fetch[x86_64-linux-musl]
33
+ rake extract[x86_64-linux-musl]
34
+ rake binary[x86_64-linux-musl]
35
+ ```
36
+
37
+ Of course you can't force the platform for `rspec` since that requires running code; see the Docker section below for ways to achieve that.
38
+
39
+ Note that zsh gives special meaning to brackets, therefore you may need to quote the argument:
40
+
41
+ ```
42
+ rake 'fetch[x86_64-linux-musl]'
43
+ ```
44
+
45
+ Available platforms are:
46
+
47
+ ```
48
+ x86_64-linux-musl # Alpine build: targets musl-based Linux
49
+ x86_64-linux-gnu # Debian build: targets glibc-based Linux
50
+ x86_64-linux # Portable build: targets multiple linux libc
51
+ x86_64-darwin # Darwin build: targets macOS
52
+ aarch64-linux-musl # Same as above, for ARMv8
53
+ aarch64-linux-gnu # Same as above, for ARMv8
54
+ aarch64-linux # Same as above, for ARMv8
55
+ arm64-darwin # Same as above, for Apple Silicon
56
+ java # JRuby build, universal
57
+ ```
58
+
59
+ Note: since it is not (yet) possible to package gems for the `java` Ruby platform any other way than `java`, it has to package all the native architectures.
60
+
61
+ In addition, options can be specified for the portable build:
62
+
63
+ ```
64
+ rake binary[x86_64-linux:gnu+musl] # Combined build: combine musl and glibc builds, selecting one at runtime
65
+ rake binary[x86_64-linux:llvm] # Hybrid build: linked to llvm static libs and built against a musl sysroot
66
+ ```
67
+
68
+ See upstream libddwaf for details about the [hybrid portable build](https://github.com/DataDog/libddwaf/blob/master/docker/libddwaf/README.md).
69
+
70
+ ## Testing with Docker
71
+
72
+ Unless using Docker for Mac, remember to enable foreign CPU emulation via QEMU:
73
+
74
+ ```
75
+ # aarch64 on x86_64 hardware
76
+ docker run --privileged --rm tonistiigi/binfmt --install arm64
77
+ # x86_64 on aarch64 hardware
78
+ docker run --privileged --rm tonistiigi/binfmt --install amd64
79
+ ```
80
+
81
+ Then you can substitute e.g `--platform linux/x86_64` with `--platform linux/aarch64` below.
82
+
83
+ ### GNU (Debian)
84
+
85
+ ```
86
+ # this is too old for aarch64
87
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.1 sh -c 'rm -fv Gemfile.lock && gem install bundler -v "~> 1.17" && bundle install && bundle exec rake spec'
88
+ # these are fine for aarch64
89
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.2 sh -c 'rm -fv Gemfile.lock && gem install bundler -v "~> 1.17" && bundle install && bundle exec rake spec'
90
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.3 sh -c 'rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
91
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.4 sh -c 'rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
92
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.5 sh -c 'rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
93
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.6 sh -c 'rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
94
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.7 sh -c 'rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
95
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:3.0 sh -c 'rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
96
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:3.1 sh -c 'rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
97
+ ```
98
+
99
+ ### musl (Alpine)
100
+
101
+ ```
102
+ # these are too old for aarch64
103
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.1-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler -v "~> 1.17" && bundle install && bundle exec rake spec'
104
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.2-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler -v "~> 1.17" && bundle install && bundle exec rake spec'
105
+ # these are fine for aarch64
106
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.3-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
107
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.4-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
108
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.5-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
109
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.6-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
110
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.7-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
111
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:3.0-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
112
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:3.1-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
113
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:3.1-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
114
+ ```
115
+
116
+ ### JRuby
117
+
118
+ ```
119
+ # these are too old for aarch64
120
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" jruby:9.2.8.0 sh -c 'apt-get update && apt-get install -y build-essential git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
121
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" jruby:9.3.0.0 sh -c 'apt-get update && apt-get install -y build-essential git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
122
+ # this is fine for aarch64
123
+ docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" jruby:9.3.4.0 sh -c 'apt-get update && apt-get install -y build-essential git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
124
+ ```
@@ -14,20 +14,16 @@ module Datadog
14
14
  ddwaf_err_invalid_argument: :err_invalid_argument
15
15
  }.freeze
16
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!
17
+ def initialize(context_ptr)
18
+ @context_ptr = context_ptr
27
19
  end
28
20
 
29
- def finalize
30
- invalidate!
21
+ # Destroys the WAF context and sets the pointer to nil.
22
+ #
23
+ # The instance becomes unusable after this method is called.
24
+ def finalize!
25
+ context_ptr_to_destroy = @context_ptr
26
+ @context_ptr = nil
31
27
 
32
28
  retained.each do |retained_obj|
33
29
  next unless retained_obj.is_a?(LibDDWAF::Object)
@@ -36,11 +32,17 @@ module Datadog
36
32
  end
37
33
 
38
34
  retained.clear
39
- LibDDWAF.ddwaf_context_destroy(context_obj)
35
+ LibDDWAF.ddwaf_context_destroy(context_ptr_to_destroy)
40
36
  end
41
37
 
38
+ # Runs the WAF context with the given persistent and ephemeral data.
39
+ #
40
+ # @raise [ConversionError] if the conversion of persistent or ephemeral data fails
41
+ # @raise [LibDDWAFError] if libddwaf could not create the result object
42
+ #
43
+ # @return [Result] the result of the WAF run
42
44
  def run(persistent_data, ephemeral_data, timeout = LibDDWAF::DDWAF_RUN_TIMEOUT)
43
- valid!
45
+ ensure_pointer_presence!
44
46
 
45
47
  persistent_data_obj = Converter.ruby_to_object(
46
48
  persistent_data,
@@ -50,7 +52,7 @@ module Datadog
50
52
  coerce: false
51
53
  )
52
54
  if persistent_data_obj.null?
53
- raise LibDDWAF::Error, "Could not convert persistent data: #{persistent_data.inspect}"
55
+ raise ConversionError, "Could not convert persistent data: #{persistent_data.inspect}"
54
56
  end
55
57
 
56
58
  # retain C objects in memory for subsequent calls to run
@@ -64,15 +66,15 @@ module Datadog
64
66
  coerce: false
65
67
  )
66
68
  if ephemeral_data_obj.null?
67
- raise LibDDWAF::Error, "Could not convert ephemeral data: #{ephemeral_data.inspect}"
69
+ raise ConversionError, "Could not convert ephemeral data: #{ephemeral_data.inspect}"
68
70
  end
69
71
 
70
72
  result_obj = LibDDWAF::Result.new
71
- raise LibDDWAF::Error, 'Could not create result object' if result_obj.null?
73
+ raise LibDDWAFError, "Could not create result object" if result_obj.null?
72
74
 
73
- code = LibDDWAF.ddwaf_run(@context_obj, persistent_data_obj, ephemeral_data_obj, result_obj, timeout)
75
+ code = LibDDWAF.ddwaf_run(@context_ptr, persistent_data_obj, ephemeral_data_obj, result_obj, timeout)
74
76
 
75
- result = Result.new(
77
+ Result.new(
76
78
  RESULT_CODE[code],
77
79
  Converter.object_to_ruby(result_obj[:events]),
78
80
  result_obj[:total_runtime],
@@ -80,8 +82,6 @@ module Datadog
80
82
  Converter.object_to_ruby(result_obj[:actions]),
81
83
  Converter.object_to_ruby(result_obj[:derivatives])
82
84
  )
83
-
84
- [RESULT_CODE[code], result]
85
85
  ensure
86
86
  LibDDWAF.ddwaf_result_free(result_obj) if result_obj
87
87
  LibDDWAF.ddwaf_object_free(ephemeral_data_obj) if ephemeral_data_obj
@@ -89,24 +89,10 @@ module Datadog
89
89
 
90
90
  private
91
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?
92
+ def ensure_pointer_presence!
93
+ return if @context_ptr
108
94
 
109
- raise LibDDWAF::Error, "Attempt to use an invalid instance: #{inspect}"
95
+ raise InstanceFinalizedError, "Cannot use WAF context after it has been finalized"
110
96
  end
111
97
 
112
98
  def retained
@@ -7,66 +7,67 @@ module Datadog
7
7
  module Converter
8
8
  module_function
9
9
 
10
- # rubocop:disable Metrics/MethodLength,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
10
+ # standard:disable Metrics/MethodLength,Metrics/CyclomaticComplexity
11
11
  def ruby_to_object(val, max_container_size: nil, max_container_depth: nil, max_string_length: nil, coerce: true)
12
12
  case val
13
13
  when Array
14
14
  obj = LibDDWAF::Object.new
15
15
  res = LibDDWAF.ddwaf_object_array(obj)
16
- if res.null?
17
- raise LibDDWAF::Error, "Could not convert into object: #{val}"
18
- end
16
+ raise ConversionError, "Could not convert into object: #{val}" if res.null?
19
17
 
20
18
  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
19
+ unless max_container_depth == 0
20
+ val.each.with_index do |e, i|
21
+ member = Converter.ruby_to_object(
22
+ 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
+ )
28
+ e_res = LibDDWAF.ddwaf_object_array_add(obj, member)
29
+ raise ConversionError, "Could not add to array object: #{e.inspect}" unless e_res
30
+
31
+ break val if max_index && i >= max_index
32
+ end
33
+ end
32
34
 
33
35
  obj
34
36
  when Hash
35
37
  obj = LibDDWAF::Object.new
36
38
  res = LibDDWAF.ddwaf_object_map(obj)
37
- if res.null?
38
- raise LibDDWAF::Error, "Could not convert into object: #{val}"
39
- end
39
+ raise ConversionError, "Could not convert into object: #{val}" if res.null?
40
40
 
41
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}"
42
+ unless max_container_depth == 0
43
+ val.each.with_index do |e, i|
44
+ # for Steep, which doesn't handle |(k, v), i|
45
+ k = e[0]
46
+ v = e[1]
47
+
48
+ k = k.to_s[0, max_string_length] if max_string_length
49
+ member = Converter.ruby_to_object(
50
+ v,
51
+ max_container_size: max_container_size,
52
+ max_container_depth: (max_container_depth - 1 if max_container_depth),
53
+ max_string_length: max_string_length,
54
+ coerce: coerce
55
+ )
56
+ kv_res = LibDDWAF.ddwaf_object_map_addl(obj, k.to_s, k.to_s.bytesize, member)
57
+ raise ConversionError, "Could not add to map object: #{k.inspect} => #{v.inspect}" unless kv_res
58
+
59
+ break val if max_index && i >= max_index
55
60
  end
56
-
57
- break val if max_index && i >= max_index
58
- end unless max_container_depth == 0
61
+ end
59
62
 
60
63
  obj
61
64
  when String
62
65
  obj = LibDDWAF::Object.new
63
- encoded_val = val.to_s.encode('utf-8', invalid: :replace, undef: :replace)
66
+ encoded_val = val.to_s.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
64
67
  val = encoded_val[0, max_string_length] if max_string_length
65
68
  str = val.to_s
66
69
  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
+ raise ConversionError, "Could not convert into object: #{val.inspect}" if res.null?
70
71
 
71
72
  obj
72
73
  when Symbol
@@ -74,67 +75,58 @@ module Datadog
74
75
  val = val.to_s[0, max_string_length] if max_string_length
75
76
  str = val.to_s
76
77
  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
78
+ raise ConversionError, "Could not convert into object: #{val.inspect}" if res.null?
80
79
 
81
80
  obj
82
81
  when Integer
83
82
  obj = LibDDWAF::Object.new
84
83
  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}"
84
+ LibDDWAF.ddwaf_object_string(obj, val.to_s)
85
+ elsif val < 0
86
+ LibDDWAF.ddwaf_object_signed(obj, val)
87
+ else
88
+ LibDDWAF.ddwaf_object_unsigned(obj, val)
93
89
  end
90
+ raise ConversionError, "Could not convert into object: #{val.inspect}" if res.null?
94
91
 
95
92
  obj
96
93
  when Float
97
94
  obj = LibDDWAF::Object.new
98
95
  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}"
96
+ LibDDWAF.ddwaf_object_string(obj, val.to_s)
97
+ else
98
+ LibDDWAF.ddwaf_object_float(obj, val)
105
99
  end
100
+ raise ConversionError, "Could not convert into object: #{val.inspect}" if res.null?
106
101
 
107
102
  obj
108
103
  when TrueClass, FalseClass
109
104
  obj = LibDDWAF::Object.new
110
105
  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}"
106
+ LibDDWAF.ddwaf_object_string(obj, val.to_s)
107
+ else
108
+ LibDDWAF.ddwaf_object_bool(obj, val)
117
109
  end
110
+ raise ConversionError, "Could not convert into object: #{val.inspect}" if res.null?
118
111
 
119
112
  obj
120
113
  when NilClass
121
114
  obj = LibDDWAF::Object.new
122
115
  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}"
116
+ LibDDWAF.ddwaf_object_string(obj, "")
117
+ else
118
+ LibDDWAF.ddwaf_object_null(obj)
129
119
  end
120
+ raise ConversionError, "Could not convert into object: #{val.inspect}" if res.null?
130
121
 
131
122
  obj
132
123
  else
133
- Converter.ruby_to_object('')
124
+ Converter.ruby_to_object("")
134
125
  end
135
126
  end
136
- # rubocop:enable Metrics/MethodLength,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
127
+ # standard:enable Metrics/MethodLength,Metrics/CyclomaticComplexity
137
128
 
129
+ # standard:disable Metrics/MethodLength,Metrics/CyclomaticComplexity
138
130
  def object_to_ruby(obj)
139
131
  case obj[:type]
140
132
  when :ddwaf_obj_invalid, :ddwaf_obj_null
@@ -170,6 +162,7 @@ module Datadog
170
162
  end
171
163
  end
172
164
  end
165
+ # standard:enable Metrics/MethodLength,Metrics/CyclomaticComplexity
173
166
  end
174
167
  end
175
168
  end
@@ -0,0 +1,19 @@
1
+ module Datadog
2
+ module AppSec
3
+ module WAF
4
+ Error = Class.new(StandardError)
5
+ InstanceFinalizedError = Class.new(Error)
6
+ ConversionError = Class.new(Error)
7
+
8
+ class LibDDWAFError < Error
9
+ attr_reader :diagnostics
10
+
11
+ def initialize(msg, diagnostics: nil)
12
+ @diagnostics = diagnostics
13
+
14
+ super(msg)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -6,101 +6,53 @@ module Datadog
6
6
  # Ruby representation of the ddwaf_handle in libddwaf
7
7
  # See https://github.com/DataDog/libddwaf/blob/10e3a1dfc7bc9bb8ab11a09a9f8b6b339eaf3271/BINDING_IMPL_NOTES.md?plain=1#L4-L19
8
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)
9
+ def initialize(handle_ptr)
10
+ @handle_ptr = handle_ptr
11
+ end
36
12
 
37
- if @handle_obj.null?
38
- raise LibDDWAF::Error.new('Could not create handle', diagnostics: @diagnostics)
39
- end
13
+ # Destroys the WAF handle and sets the pointer to nil.
14
+ #
15
+ # The instance becomes unusable after this method is called.
16
+ def finalize!
17
+ handle_ptr_to_destroy = @handle_ptr
18
+ @handle_ptr = nil
40
19
 
41
- validate!
42
- ensure
43
- LibDDWAF.ddwaf_object_free(diagnostics_obj) if diagnostics_obj
44
- LibDDWAF.ddwaf_object_free(rule_obj) if rule_obj
20
+ LibDDWAF.ddwaf_destroy(handle_ptr_to_destroy)
45
21
  end
46
22
 
47
- def finalize
48
- invalidate!
23
+ # Builds a WAF context.
24
+ #
25
+ # @raise [LibDDWAFError] if libddwaf could not create the context.
26
+ # @return [Handle] the WAF handle
27
+ def build_context
28
+ ensure_pointer_presence!
29
+
30
+ context_obj = LibDDWAF.ddwaf_context_init(@handle_ptr)
31
+ raise LibDDWAFError, "Could not create context" if context_obj.null?
49
32
 
50
- LibDDWAF.ddwaf_destroy(handle_obj)
33
+ Context.new(context_obj)
51
34
  end
52
35
 
53
- def required_addresses
54
- valid!
36
+ # Returns the list of known addresses in the WAF handle.
37
+ #
38
+ # @return [Array<String>] the list of known addresses
39
+ def known_addresses
40
+ ensure_pointer_presence!
55
41
 
56
42
  count = LibDDWAF::UInt32Ptr.new
57
- list = LibDDWAF.ddwaf_known_addresses(handle_obj, count)
43
+ list = LibDDWAF.ddwaf_known_addresses(@handle_ptr, count)
58
44
 
59
45
  return [] if count == 0 # list is null
60
46
 
61
47
  list.get_array_of_string(0, count[:value])
62
48
  end
63
49
 
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
50
  private
79
51
 
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?
52
+ def ensure_pointer_presence!
53
+ return if @handle_ptr
102
54
 
103
- raise LibDDWAF::Error, "Attempt to use an invalid instance: #{inspect}"
55
+ raise InstanceFinalizedError, "Cannot use WAF handle after it has been finalized"
104
56
  end
105
57
  end
106
58
  end