Documentation
¶
Overview ¶
Package libddwaf provides Go bindings for libddwaf — Datadog's in-app Web Application Firewall evaluation engine.
The package offers a high-level API built around three core types:
- Builder: constructs a Handle from one or more ruleset fragments.
- Handle: holds a compiled ruleset; creates per-request [Context]s.
- Context: evaluates input data against the ruleset.
- Subcontext: scopes ephemeral data to a shorter lifetime within a Context.
A typical usage pattern:
builder, err := libddwaf.NewBuilder()
if err != nil { /* handle */ }
defer builder.Close()
if _, err := builder.AddOrUpdateConfig("/rules", ruleset); err != nil { /* handle */ }
handle, err := builder.Build()
if err != nil { /* handle */ }
defer handle.Close()
ctx, err := handle.NewContext(context.Background())
if err != nil { /* handle */ }
defer ctx.Close()
result, err := ctx.Run(context.Background(), libddwaf.RunAddressData{Data: input})
subCtx, err := ctx.NewSubcontext(context.Background())
// ...
result, err = subCtx.Run(context.Background(), libddwaf.RunAddressData{Data: ephemeral})
For supported platforms, loading mechanics, and CGO/purego trade-offs, see the repository README.
Index ¶
- Constants
- func DecodeObject(obj *WAFObject) (any, error)deprecated
- func Load() (bool, error)
- func Usable() (bool, error)
- func Version() string
- type ArrayBuilder
- type Builder
- func (b *Builder) AddDefaultRecommendedRuleset() (Diagnostics, error)
- func (b *Builder) AddOrUpdateConfig(path string, fragment any) (Diagnostics, error)
- func (b *Builder) Build() (*Handle, error)
- func (b *Builder) Close()
- func (b *Builder) ConfigPaths(filter string) ([]string, error)
- func (b *Builder) RemoveConfig(path string) bool
- func (b *Builder) RemoveDefaultRecommendedRuleset() bool
- type Context
- type Diagnostics
- type Encodable
- type Encoder
- func (e *Encoder) Array(parent *WAFObject, capacityHint int) *ArrayBuilder
- func (e *Encoder) Map(parent *WAFObject, capacityHint int) *MapBuilder
- func (e *Encoder) Timeout() bool
- func (e *Encoder) WriteLiteralString(obj *WAFObject, str string)
- func (e *Encoder) WriteString(obj *WAFObject, str string)
- type EncoderConfig
- type EncoderOption
- type Feature
- type Handle
- type MapBuilder
- type Result
- type RunAddressData
- type Subcontext
- type TruncationReason
- type Truncations
- type WAFObject
- type WAFObjectKV
- type WAFObjectType
Constants ¶
const ( // EncodeTimeKey is the key used to track the time spent encoding the address data reported in [Result.TimerStats]. EncodeTimeKey timer.Key = "encode" // DurationTimeKey is the key used to track the time spent in libddwaf ddwaf_run C function reported in [Result.TimerStats]. DurationTimeKey timer.Key = "duration" // DecodeTimeKey is the key used to track the time spent decoding the address data reported in [Result.TimerStats]. DecodeTimeKey timer.Key = "decode" )
const ( // AppsecFieldTag is the struct tag recognized by the encoder when // deciding how to process struct fields (for example, `ddwaf:"ignore"`). AppsecFieldTag = "ddwaf" // AppsecFieldTagValueIgnore is the struct tag value that causes the // annotated field to be skipped during encoding. AppsecFieldTagValueIgnore = "ignore" )
Variables ¶
This section is empty.
Functions ¶
func DecodeObject
deprecated
func Load ¶
Load loads libddwaf's dynamic library. The dynamic library is opened only once by the first call to this function and internally stored globally. No function is currently provided in this API to unload it.
This function is automatically called by NewBuilder, and most users need not explicitly call it. It is however useful in order to explicitly check for the status of the WAF library's initialization.
The function returns true when libddwaf was successfully loaded, along with an error value. An error might still be returned even though the WAF load was successful: in such cases the error is indicative that some non-critical features are not available; but the WAF may still be used.
On macOS, subsequent calls return the cached result from the first call. This is intentional: macOS does not support dlopen-ing the same library twice in the same process lifetime.
func Usable ¶
Usable returns true if the WAF is usable, false and an error otherwise.
If the WAF is usable, an error value may still be returned and should be treated as a warning (it is non-blocking).
The following conditions are checked:
- The WAF library has been loaded successfully (you need to call Load first for this case to be taken into account)
- The WAF library has not been manually disabled with the `datadog.no_waf` go build tag
- The WAF library is not in an unsupported OS/Arch
- The WAF library is not in an unsupported Go version
Types ¶
type ArrayBuilder ¶
type ArrayBuilder struct {
// contains filtered or unexported fields
}
ArrayBuilder accumulates WAFObject entries and commits them to a parent WAFObject on Close. It is not safe for concurrent use.
func (*ArrayBuilder) Close ¶
func (b *ArrayBuilder) Close()
Close commits the accumulated entries to the parent WAFObject. If any entries were skipped, a ContainerTooLarge truncation is recorded. Calling Close more than once is safe and has no additional effect.
func (*ArrayBuilder) DropLast ¶
func (b *ArrayBuilder) DropLast()
DropLast removes the most recently appended entry. No-op if the builder has no entries. Use this to undo a NextValue call when encoding the value fails.
func (*ArrayBuilder) NextValue ¶
func (b *ArrayBuilder) NextValue() *WAFObject
NextValue appends a new slot to the array and returns a pointer to the WAFObject for the caller to populate. Returns nil when the builder is at MaxContainerSize capacity. The caller must populate the slot or call DropLast before calling NextValue again.
func (*ArrayBuilder) Skip ¶
func (b *ArrayBuilder) Skip()
Skip records that an array element was intentionally omitted. The count is used by Close to record a ContainerTooLarge truncation if any were skipped.
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder manages an evolving WAF configuration. Builder is not thread-safe. Concurrent use panics under `ci` builds.
func NewBuilder ¶
NewBuilder creates a new Builder instance. The caller must call Builder.Close when it is no longer needed.
func (*Builder) AddDefaultRecommendedRuleset ¶
func (b *Builder) AddDefaultRecommendedRuleset() (Diagnostics, error)
AddDefaultRecommendedRuleset adds the default recommended ruleset to the receiving Builder, and returns the Diagnostics produced in the process.
func (*Builder) AddOrUpdateConfig ¶
func (b *Builder) AddOrUpdateConfig(path string, fragment any) (Diagnostics, error)
AddOrUpdateConfig adds or updates a configuration fragment to this Builder. Returns the Diagnostics produced by adding or updating this configuration.
func (*Builder) Build ¶
Build creates a new Handle instance that uses the current configuration. Returns an error if the builder is not initialized or the C library fails to build the handle. The caller is responsible for calling Handle.Close when the handle is no longer needed.
func (*Builder) Close ¶
func (b *Builder) Close()
Close releases all resources associated with this builder.
func (*Builder) ConfigPaths ¶
ConfigPaths returns the list of currently loaded configuration paths.
func (*Builder) RemoveConfig ¶
RemoveConfig removes the configuration associated with the given path from this Builder. Returns true if the removal was successful.
func (*Builder) RemoveDefaultRecommendedRuleset ¶
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).
type Context ¶
type Context struct {
// Timer registers the time spent in the WAF and go-libddwaf. It is created alongside the Context using the options
// passed in to NewContext. Once its time budget is exhausted, each new call to Context.Run will return a timeout error.
Timer timer.NodeTimer
// contains filtered or unexported fields
}
Context is a WAF execution context. It allows running the WAF incrementally when calling it multiple times to run its rules every time new addresses become available. Each request must have its own Context. New Context instances can be created by calling Handle.NewContext.
Subcontexts can be created via Context.NewSubcontext. Data passed to a subcontext is stored and persists across multiple calls to Run on that subcontext, until the subcontext is closed.
Concurrency ¶
Context.Run may be called concurrently; same-Context Run calls serialize internally via a per-instance mutex. Context.Run may run concurrently with Subcontext.Run calls under the same Context.
Context.Close waits for all in-flight Run and NewSubcontext operations to complete before destroying the underlying ddwaf_context.
func (*Context) Close ¶
func (context *Context) Close()
Close disposes of the context: it destroys the underlying ddwaf_context, releases associated data, and decreases the reference count of the Handle created for this Context.
Close cascades to subcontexts: any still-open Subcontext derived from this Context via Context.NewSubcontext is fully torn down as part of Close — its underlying ddwaf_subcontext is destroyed and its pinned input data released. (The underlying ddwaf_context_destroy does not itself cascade, so go-libddwaf destroys each live subcontext explicitly before destroying the context.) Calling Subcontext.Close afterwards remains safe and becomes a no-op.
Close blocks until all in-flight Run and NewSubcontext operations complete, and is safe to call more than once.
func (*Context) NewSubcontext ¶
func (context *Context) NewSubcontext(ctx context.Context) (*Subcontext, error)
NewSubcontext creates a subcontext derived from this context. The provided ctx only scopes the construction call itself and is not retained after NewSubcontext returns; per-run deadlines and cancellation must be supplied to Context.Run via its own ctx argument. Data passed to the subcontext's Run() is stored and persists across multiple calls to Run on that subcontext, but does not persist in the parent context. When the subcontext is closed, its data is released.
A subcontext gets its own timer whose initial budget is snapshotted from the caller's remaining budget when the subcontext is created.
Usage:
subCtx, err := ctx.NewSubcontext(context.Background())
if err != nil {
return err
}
defer subCtx.Close()
result, err := subCtx.Run(context.Background(), RunAddressData{Data: data})
func (*Context) Run ¶
func (context *Context) Run(ctx context.Context, addressData RunAddressData) (res Result, err error)
Run encodes the given RunAddressData values and runs them against the WAF rules. Callers must check the returned Result object even when an error is returned, as the WAF might have been able to match some rules and generate events or actions before the error was reached.
Deadline precedence is the minimum of ctx's deadline and the remaining Context.Timer budget. If ctx fires first, Run returns ctx.Err(). If the timer budget fires first, Run returns waferrors.ErrTimeout.
func (*Context) Truncations ¶
func (context *Context) Truncations() Truncations
Truncations returns the truncations that occurred while encoding address data for WAF execution. The returned value is a snapshot; subsequent Run calls may accumulate more truncations.
type Diagnostics ¶
type Diagnostics struct {
// Rules contains information about the loaded rules.
Rules *Feature
// CustomRules contains information about the loaded custom rules.
CustomRules *Feature
// Actions contains information about the loaded actions.
Actions *Feature
// Exclusions contains information about the loaded exclusions.
Exclusions *Feature
// RulesOverrides contains information about the loaded rules overrides.
RulesOverrides *Feature
// RulesData contains information about the loaded rules data.
RulesData *Feature
// ExclusionData contains information about the loaded exclusion data.
ExclusionData *Feature
// Processors contains information about the loaded processors.
Processors *Feature
// ProcessorOverrides contains information about the loaded processor overrides.
ProcessorOverrides *Feature
// Scanners contains information about the loaded scanners.
Scanners *Feature
// Version is the version of the parsed ruleset if available.
Version string
}
Diagnostics stores the information as provided by the WAF about WAF rules parsing and loading. It is returned by Builder.AddOrUpdateConfig.
func (*Diagnostics) EachFeature ¶
func (d *Diagnostics) EachFeature(cb func(string, *Feature))
EachFeature calls the provided callback for each (non-nil) feature in this diagnostics object.
func (*Diagnostics) TopLevelError ¶
func (d *Diagnostics) TopLevelError() error
TopLevelError returns the list of top-level errors reported by the WAF on any of the Diagnostics entries, rolled up into a single error value. Returns nil if no top-level errors were reported. Individual, item-level errors might still exist.
type Encodable ¶
Encodable represents a type that can encode itself into a WAFObject.
The encoder writes into obj using the provided *Encoder for size limits, pinning, timer, and truncation accounting.
Best-effort encoding philosophy: implementers should self-recover from input parsing errors by leaving obj as invalid (zero value) or calling [WAFObject.SetInvalid]. Only fatal conditions should be returned as errors:
- waferrors.ErrTimeout when Encoder.Timeout returns true
- waferrors.ErrMaxDepthExceeded when depth budget is exhausted
The depth parameter is the remaining recursion budget. Implementers must decrement and check before recursing into nested structures.
The *Encoder is owned by the caller; implementations must NOT retain the pointer past return. Truncations accumulate in enc.Truncations (shared across nested calls).
type Encoder ¶
type Encoder struct {
Config EncoderConfig
Truncations Truncations
}
Encoder is the public encoding helper exposed to Encodable implementers. It provides string-writing utilities, map/array builders, timer checking, and truncation accounting.
func (*Encoder) Array ¶
func (e *Encoder) Array(parent *WAFObject, capacityHint int) *ArrayBuilder
Array returns a new ArrayBuilder that will commit its entries into parent. capacityHint pre-allocates the backing slice (capped at MaxContainerSize); pass 0 when the eventual size is unknown.
func (*Encoder) Map ¶
func (e *Encoder) Map(parent *WAFObject, capacityHint int) *MapBuilder
Map returns a new MapBuilder that will commit its entries into parent. capacityHint pre-allocates the backing slice (capped at MaxContainerSize); pass 0 when the eventual size is unknown.
func (*Encoder) WriteLiteralString ¶
func (*Encoder) WriteString ¶
type EncoderConfig ¶
type EncoderConfig struct {
// Pinner is used to pin the data referenced by the encoded wafObjects.
Pinner *runtime.Pinner
// Timer makes sure the encoder doesn't spend too much time doing its job.
Timer timer.Timer
// MaxContainerSize is the maximum number of elements in a container (list, map, struct) that will be encoded.
MaxContainerSize uint16
// MaxStringSize is the maximum length of a string that will be encoded.
MaxStringSize uint16
// MaxObjectDepth is the maximum depth of the object that will be encoded.
MaxObjectDepth uint16
}
EncoderConfig provides configuration for an [encoder], including pinning, timing, and size limits for strings, containers, and object depth.
type EncoderOption ¶
type EncoderOption func(*EncoderConfig)
EncoderOption mutates an EncoderConfig created by [newEncoderConfig].
func WithTimer ¶
func WithTimer(timer timer.Timer) EncoderOption
WithTimer configures [newEncoderConfig] to use the provided timer.
func WithUnlimitedLimits ¶
func WithUnlimitedLimits() EncoderOption
WithUnlimitedLimits configures [newEncoderConfig] to disable encoder truncation limits.
type Feature ¶
type Feature struct {
// Errors is a map of parsing errors to a list of unique identifiers from the elements which
// failed loading due to this specific error.
Errors map[string][]string
// Warnings is a map of parsing warnings to a list of unique identifiers from the elements which
// resulted in this specific warning.
Warnings map[string][]string
// Error is the single error which prevented parsing this feature.
Error string
// Loaded is a list of the unique identifiers from successfully loaded elements.
Loaded []string
// Failed is a list of the unique identifiers from the elements which couldn't be loaded.
Failed []string
// Skipped is a list of the unique identifiers from the elements which were skipped.
Skipped []string
}
Feature stores the information as provided by the WAF about loaded and failed rules for a specific feature of the WAF ruleset.
type Handle ¶
type Handle struct {
// contains filtered or unexported fields
}
Handle represents an instance of the WAF for a given ruleset. It is obtained from Builder.Build; and must be disposed of by calling Handle.Close once no longer in use.
The reference count equals the number of alive Context values plus the number of alive Subcontext values. Each Handle.NewContext call retains the handle, each Context.Close releases it, each Context.NewSubcontext call retains it, and each Subcontext.Close releases it.
Library lifetime boundary: [Handle.retain] and Handle.Close only govern the lifetime of the underlying C ddwaf_handle. They do not keep the libddwaf shared library loaded. That shared library is a process-wide singleton loaded once via purego.Dlopen (see internal/bindings) and is never Dlclosed during normal operation. As a consequence, symbols such as bindings.Lib.ContextDestroy or bindings.Lib.SubcontextDestroy remain resolvable for the entire lifetime of the process, and Context/SubContext teardown paths that call into bindings.Lib after the Handle's refcount has reached zero are safe with respect to the library itself.
func (*Handle) Actions ¶
Actions returns the list of actions the WAF has been configured to monitor based on the input ruleset.
func (*Handle) Addresses ¶
Addresses returns the list of addresses the WAF has been configured to monitor based on the input ruleset.
func (*Handle) Close ¶
func (handle *Handle) Close()
Close decrements the reference counter of this Handle, possibly allowing it to be destroyed and all the resources associated with it to be released.
func (*Handle) NewContext ¶
func (handle *Handle) NewContext(ctx context.Context, timerOptions ...timer.Option) (*Context, error)
NewContext returns a new WAF context for the given WAF handle. The provided ctx only scopes the construction call itself and is not retained after NewContext returns; per-run deadlines and cancellation must be supplied to Context.Run via its own ctx argument. An error is returned when the WAF handle was released or when the WAF context couldn't be created.
type MapBuilder ¶
type MapBuilder struct {
// contains filtered or unexported fields
}
MapBuilder accumulates WAFObjectKV entries and commits them to a parent WAFObject on Close. It is not safe for concurrent use.
func (*MapBuilder) Close ¶
func (b *MapBuilder) Close()
Close commits the accumulated entries to the parent WAFObject. If any entries were skipped, a ContainerTooLarge truncation is recorded. Calling Close more than once is safe and has no additional effect.
func (*MapBuilder) DropLast ¶
func (b *MapBuilder) DropLast()
DropLast removes the most recently appended entry. No-op if the builder has no entries. Use this to undo a NextValue call when encoding the value fails.
func (*MapBuilder) NextValue ¶
func (b *MapBuilder) NextValue(key string) *WAFObject
NextValue appends a new key-value slot to the map and returns a pointer to the value WAFObject for the caller to populate. Returns nil when the builder is at MaxContainerSize capacity. The caller must populate the value or call DropLast before calling NextValue again.
func (*MapBuilder) Skip ¶
func (b *MapBuilder) Skip()
Skip records that a key-value pair was intentionally omitted (e.g. due to an encoding error). The count is used by Close to record a ContainerTooLarge truncation if any entries were skipped.
type Result ¶
type Result struct {
// Events is the list of events the WAF detected, together with any relevant
// details. These are typically forwarded as opaque objects to the Datadog
// backend.
Events []any
// Derivatives is the set of key-value pairs generated by the WAF, and which
// need to be reported on the trace to provide additional data to the Datadog
// backend.
Derivatives map[string]any
// Actions is the set of actions the WAF decided on when evaluating rules
// against the provided address data. It maps action types to their dynamic
// parameter values.
Actions map[string]any
// TimerStats records the time spent in the different parts of the run.
TimerStats map[timer.Key]time.Duration
// Keep is true if the WAF instructs the trace to be kept with manual priority.
Keep bool
}
Result stores the multiple values returned by a call to Context.Run.
func (*Result) HasActions ¶
HasActions returns true if the Result holds at least 1 action.
func (*Result) HasDerivatives ¶
HasDerivatives returns true if the Result holds at least 1 derivative.
type RunAddressData ¶
type RunAddressData struct {
// Data is passed to the WAF and persists across multiple calls to Run
// until the context or subcontext closes.
Data map[string]any
// TimerKey tracks time spent in the WAF for this run.
// Leave it empty to start a new timer with unlimited budget.
TimerKey timer.Key
}
RunAddressData provides address data to the Context.Run method. Fields tagged `ddwaf:"ignore"` are omitted when encoding Go structs to the WAF-compatible format.
Data passed to Run persists for the lifetime of the context or subcontext. Use NewSubcontext() for shorter-lived data.
type Subcontext ¶
Subcontext is a derived ephemeral evaluation scope. Spawned via Context.NewSubcontext. Data passed to a Subcontext's Run persists for the Subcontext's lifetime and is released when the Subcontext is closed.
Concurrency ¶
Subcontext.Run may be called concurrently across different Subcontexts of the same parent; same-Subcontext Run calls serialize internally.
Subcontext.Close should be called before its parent Context.Close for clean teardown; if the parent is already closed, Subcontext.Close is still safe (it skips the underlying destroy).
func (*Subcontext) Close ¶
func (s *Subcontext) Close()
Close disposes of the underlying subcontext.
func (*Subcontext) Run ¶
func (s *Subcontext) Run(ctx context.Context, addressData RunAddressData) (res Result, err error)
Run encodes the given RunAddressData values and runs them against the WAF rules.
func (*Subcontext) Truncations ¶
func (s *Subcontext) Truncations() Truncations
Truncations returns the truncations that occurred while encoding address data for WAF execution.
type TruncationReason ¶
type TruncationReason uint8
TruncationReason is a flag representing reasons why some input was not encoded in full.
const ( // StringTooLong indicates a string exceeded the maximum string length configured. The truncation // values indicate the actual length of truncated strings. StringTooLong TruncationReason = 1 << iota // ContainerTooLarge indicates a container (list, map, struct) exceeded the maximum number of // elements configured. The truncation values indicate the actual number of elements in the // truncated container. ContainerTooLarge // ObjectTooDeep indicates an overall object exceeded the maximum encoding depths configured. The // truncation values indicate an estimated actual depth of the truncated object. The value is // guaranteed to be less than or equal to the actual depth (it may not be more). ObjectTooDeep )
func (TruncationReason) String ¶
func (reason TruncationReason) String() string
type Truncations ¶
Truncations accumulates truncation events that occurred during encoding.
func (Truncations) AsMap ¶
func (t Truncations) AsMap() map[TruncationReason][]int
func (Truncations) IsEmpty ¶
func (t Truncations) IsEmpty() bool
func (*Truncations) Merge ¶
func (t *Truncations) Merge(other Truncations)
func (*Truncations) Record ¶
func (t *Truncations) Record(reason TruncationReason, size int)
type WAFObject ¶
WAFObject is a 16-byte value type matching the C ddwaf_object union. It can be constructed as a zero value (which is invalid by default) and mutated in place via Set* methods.
type WAFObjectKV ¶
type WAFObjectKV = bindings.WAFObjectKV
WAFObjectKV is a key-value pair used in v2 maps. Its zero value is two invalid WAFObjects.
type WAFObjectType ¶
type WAFObjectType = bindings.WAFObjectType
WAFObjectType is the type discriminator for a WAFObject.
const ( WAFInvalidType WAFObjectType = bindings.WAFInvalidType WAFNilType WAFObjectType = bindings.WAFNilType WAFBoolType WAFObjectType = bindings.WAFBoolType WAFIntType WAFObjectType = bindings.WAFIntType WAFUintType WAFObjectType = bindings.WAFUintType WAFFloatType WAFObjectType = bindings.WAFFloatType WAFStringType WAFObjectType = bindings.WAFStringType WAFLiteralStringType WAFObjectType = bindings.WAFLiteralStringType WAFSmallStringType WAFObjectType = bindings.WAFSmallStringType WAFArrayType WAFObjectType = bindings.WAFArrayType WAFMapType WAFObjectType = bindings.WAFMapType )
Type constants for WAFObject.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
invariant
Package invariant provides a runtime-invariant assertion helper that panics when the condition is violated and the build is compiled with the "ci" build tag.
|
Package invariant provides a runtime-invariant assertion helper that panics when the condition is violated and the build is compiled with the "ci" build tag. |
|
lib
Package lib provides a built-in WAF library version for the relevant runtime platform.
|
Package lib provides a built-in WAF library version for the relevant runtime platform. |
|
unsafeutil
Package unsafeutil provides helpers for unsafe pointer operations at the FFI boundary.
|
Package unsafeutil provides helpers for unsafe pointer operations at the FFI boundary. |