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 +4 -4
- data/.github/actions/docker-build-ruby/action.yml +2 -13
- data/.github/workflows/package.yml +8 -12
- data/.github/workflows/test.yml +18 -13
- data/CHANGELOG.md +6 -0
- data/lib/datadog/appsec/waf/context.rb +126 -0
- data/lib/datadog/appsec/waf/converter.rb +172 -0
- data/lib/datadog/appsec/waf/handle.rb +108 -0
- data/lib/datadog/appsec/waf/lib_ddwaf.rb +313 -0
- data/lib/datadog/appsec/waf/result.rb +33 -0
- data/lib/datadog/appsec/waf/version.rb +2 -2
- data/lib/datadog/appsec/waf.rb +17 -679
- data/sig/datadog/appsec/waf/context.rbs +39 -0
- data/sig/datadog/appsec/waf/converter.rbs +11 -0
- data/sig/datadog/appsec/waf/handle.rbs +42 -0
- data/sig/datadog/appsec/waf/lib_ddwaf.rbs +156 -0
- data/sig/datadog/appsec/waf/result.rbs +33 -0
- data/sig/datadog/appsec/waf.rbs +1 -213
- metadata +12 -3
- data/libddwaf-releases.sha256 +0 -126
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4fad2f4be2e60e15913e130d28130af2f3d3d6d40aea05dba4a1d3d967cc4c12
|
4
|
+
data.tar.gz: aac3e2f4ae75f5a9387341997ab07861f1b8ee8d0ae63cc6ad97bd99b421b132
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83b667e79adbefbf6d52c3dd425ff2bd973d1a9e9a32c3ca817e08963c86ab9bbf3002e54d7a57fbbfc6e86a0e6254350d6b02e700e4af84852ce8a9d1936ab6
|
7
|
+
data.tar.gz: e242033561aa9e4dfd3d66c44170ea5595f27499ee6fe30bfbff66bd7af61edb5c2986494bbb4b986a7bf4b005da243b9df66ce7d82c78bc8b215cc03f86913c
|
@@ -1,17 +1,12 @@
|
|
1
1
|
name: Build docker image
|
2
2
|
description:
|
3
|
-
|
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
|
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
|
-
-
|
14
|
+
- os: ubuntu-24.04
|
16
15
|
platform: x86_64-linux
|
17
|
-
-
|
16
|
+
- os: ubuntu-24.04-arm
|
18
17
|
platform: aarch64-linux
|
19
18
|
|
20
|
-
name: Test build without fetching libddwaf (Ruby ${{ matrix.ruby }}, ${{ matrix.
|
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
|
-
-
|
57
|
+
- os: ubuntu-24.04
|
61
58
|
platform: x86_64-linux:llvm
|
62
|
-
-
|
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.
|
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
|
|
data/.github/workflows/test.yml
CHANGED
@@ -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
|
-
-
|
14
|
+
- os: ubuntu-24.04
|
16
15
|
platform: x86_64-linux
|
17
|
-
-
|
16
|
+
- os: ubuntu-24.04-arm
|
18
17
|
platform: aarch64-linux
|
19
18
|
|
20
|
-
name: Test (Ruby ${{ matrix.ruby }}, ${{ matrix.
|
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
|
-
-
|
55
|
+
- os: ubuntu-24.04
|
56
56
|
platform: x86_64-linux
|
57
|
-
-
|
57
|
+
- os: ubuntu-24.04-arm
|
58
58
|
platform: aarch64-linux
|
59
59
|
|
60
|
-
name: Test (Jruby ${{ matrix.jruby }}, ${{ matrix.
|
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
|