-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathbuilder.go
More file actions
178 lines (147 loc) · 5.63 KB
/
builder.go
File metadata and controls
178 lines (147 loc) · 5.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.
package libddwaf
import (
"errors"
"fmt"
"runtime"
"github.com/DataDog/go-libddwaf/v4/internal/bindings"
"github.com/DataDog/go-libddwaf/v4/internal/ruleset"
)
// Builder manages an evolving WAF configuration over time. Its lifecycle is
// typically tied to that of a remote configuration client, as its purpose is to
// keep an up-to-date view of the current coniguration with low overhead. This
// type is not safe for concurrent use, and users should protect it with a mutex
// or similar when sharing it across multiple goroutines. All methods of this
// type are safe to call with a nil receiver.
type Builder struct {
handle bindings.WAFBuilder
defaultLoaded bool
}
// NewBuilder creates a new [Builder] instance. Its lifecycle is typically tied
// to that of a remote configuration client, as its purpose is to keep an
// up-to-date view of the current coniguration with low overhead. Returns nil if
// an error occurs when initializing the builder. The caller is responsible for
// calling [Builder.Close] when the builder is no longer needed.
func NewBuilder(keyObfuscatorRegex string, valueObfuscatorRegex string) (*Builder, error) {
if ok, err := Load(); !ok {
return nil, err
}
var pinner runtime.Pinner
defer pinner.Unpin()
hdl := bindings.Lib.BuilderInit(newConfig(&pinner, keyObfuscatorRegex, valueObfuscatorRegex))
if hdl == 0 {
return nil, errors.New("failed to initialize the WAF builder")
}
return &Builder{handle: hdl}, nil
}
// Close releases all resources associated with this builder.
func (b *Builder) Close() {
if b == nil || b.handle == 0 {
return
}
bindings.Lib.BuilderDestroy(b.handle)
b.handle = 0
}
var (
errUpdateFailed = errors.New("failed to update WAF Builder instance")
errBuilderClosed = errors.New("builder has already been closed")
)
const defaultRecommendedRulesetPath = "::/go-libddwaf/default/recommended.json"
// AddDefaultRecommendedRuleset adds the default recommended ruleset to the
// receiving [Builder], and returns the [Diagnostics] produced in the process.
func (b *Builder) AddDefaultRecommendedRuleset() (Diagnostics, error) {
defaultRuleset, err := ruleset.DefaultRuleset()
defer bindings.Lib.ObjectFree(&defaultRuleset)
if err != nil {
return Diagnostics{}, fmt.Errorf("failed to load default recommended ruleset: %w", err)
}
diag, err := b.addOrUpdateConfig(defaultRecommendedRulesetPath, &defaultRuleset)
if err == nil {
b.defaultLoaded = true
}
return diag, err
}
// RemoveDefaultRecommendedRuleset removes the default recommended ruleset from
// the receiving [Builder]. Returns true if the removal occurred (meaning the
// default recommended ruleset was indeed present in the builder).
func (b *Builder) RemoveDefaultRecommendedRuleset() bool {
if b.RemoveConfig(defaultRecommendedRulesetPath) {
b.defaultLoaded = false
return true
}
return false
}
// AddOrUpdateConfig adds or updates a configuration fragment to this [Builder].
// Returns the [Diagnostics] produced by adding or updating this configuration.
func (b *Builder) AddOrUpdateConfig(path string, fragment any) (Diagnostics, error) {
if b == nil || b.handle == 0 {
return Diagnostics{}, errBuilderClosed
}
if path == "" {
return Diagnostics{}, errors.New("path cannot be blank")
}
var pinner runtime.Pinner
defer pinner.Unpin()
encoder, err := newEncoder(newUnlimitedEncoderConfig(&pinner))
if err != nil {
return Diagnostics{}, fmt.Errorf("could not create encoder: %w", err)
}
frag, err := encoder.Encode(fragment)
if err != nil {
return Diagnostics{}, fmt.Errorf("could not encode the config fragment into a WAF object; %w", err)
}
return b.addOrUpdateConfig(path, frag)
}
// addOrUpdateConfig adds or updates a configuration fragment to this [Builder].
// Returns the [Diagnostics] produced by adding or updating this configuration.
func (b *Builder) addOrUpdateConfig(path string, cfg *bindings.WAFObject) (Diagnostics, error) {
var diagnosticsWafObj bindings.WAFObject
defer bindings.Lib.ObjectFree(&diagnosticsWafObj)
res := bindings.Lib.BuilderAddOrUpdateConfig(b.handle, path, cfg, &diagnosticsWafObj)
var diags Diagnostics
if !diagnosticsWafObj.IsInvalid() {
// The Diagnostics object will be invalid if the config was completely
// rejected.
var err error
diags, err = decodeDiagnostics(&diagnosticsWafObj)
if err != nil {
return diags, fmt.Errorf("failed to decode WAF diagnostics: %w", err)
}
}
if !res {
return diags, errUpdateFailed
}
return diags, nil
}
// RemoveConfig removes the configuration associated with the given path from
// this [Builder]. Returns true if the removal was successful.
func (b *Builder) RemoveConfig(path string) bool {
if b == nil || b.handle == 0 {
return false
}
return bindings.Lib.BuilderRemoveConfig(b.handle, path)
}
// ConfigPaths returns the list of currently loaded configuration paths.
func (b *Builder) ConfigPaths(filter string) []string {
if b == nil || b.handle == 0 {
return nil
}
return bindings.Lib.BuilderGetConfigPaths(b.handle, filter)
}
// Build creates a new [Handle] instance that uses the current configuration.
// Returns nil if an error occurs when building the handle. The caller is
// responsible for calling [Handle.Close] when the handle is no longer needed.
// This function may return nil.
func (b *Builder) Build() *Handle {
if b == nil || b.handle == 0 {
return nil
}
hdl := bindings.Lib.BuilderBuildInstance(b.handle)
if hdl == 0 {
return nil
}
return wrapHandle(hdl)
}