Skip to main content

libddwaf/
builder.rs

1use std::ptr::null_mut;
2
3use crate::object::{AsRawMutObject, WafArray, WafMap, WafOwnedDefaultAllocator};
4use crate::{Config, Handle};
5
6/// A builder for [`Handle`]s.
7///
8/// This is used to maintain a live view over mutable configuration, and is best
9/// suited for cases where the Waf's configuration evolves regularly, such as
10/// through remote configuration.
11#[repr(transparent)]
12pub struct Builder {
13    raw: libddwaf_sys::ddwaf_builder,
14}
15impl Builder {
16    const OBFUSCATOR_KEY: &str = "datadog/0/ASM_DD/0/config";
17
18    /// Creates a new [`Builder`] instance using the provided [`Config`]. Returns [`None`] if the
19    /// builder's initialization fails.
20    #[must_use]
21    pub fn new(config: Option<&Config>) -> Option<Self> {
22        let mut builder = Builder {
23            raw: unsafe { libddwaf_sys::ddwaf_builder_init() },
24        };
25        if builder.raw.is_null() {
26            return None;
27        }
28
29        if let Some(config) = config {
30            let config_obj = config.as_waf_object();
31            let res = builder.add_or_update_config(Self::OBFUSCATOR_KEY, &config_obj, None);
32            if !res {
33                debug_assert!(false, "Failed to add or update obfuscator config");
34                return None;
35            }
36        }
37
38        Some(builder)
39    }
40
41    /// Adds or updates the configuration for the given path.
42    ///
43    /// Returns true if the ruleset was successfully added or updated. Any warning/error information
44    /// is conveyed through the provided diagnostics object.
45    ///
46    /// # Panics
47    /// Panics if the provided `path` is longer than [`u32::MAX`] bytes.
48    #[must_use]
49    pub fn add_or_update_config(
50        &mut self,
51        path: &str,
52        ruleset: &impl AsRef<libddwaf_sys::ddwaf_object>,
53        mut diagnostics: Option<&mut WafOwnedDefaultAllocator<WafMap>>,
54    ) -> bool {
55        debug_assert!(
56            !path.is_empty(),
57            concat!(
58                "path cannot be empty (",
59                stringify!(bindings::ddwaf_builder_add_or_update_config),
60                " would always fail)"
61            )
62        );
63        let path_len = u32::try_from(path.len()).expect("path is too long");
64        if let Some(ref mut diagnostics) = diagnostics {
65            // drop the old diagnostics if we're reusing it
66            let _ = std::mem::take(*diagnostics);
67        }
68        unsafe {
69            libddwaf_sys::ddwaf_builder_add_or_update_config(
70                self.raw,
71                path.as_ptr().cast(),
72                path_len,
73                ruleset.as_ref(),
74                diagnostics.map_or(null_mut(), |o| std::ptr::from_mut(o.as_raw_mut()).cast()),
75            )
76        }
77    }
78
79    /// Removes the configuration for the given path if some exists.
80    ///
81    /// Returns true if some configuration was indeed removed.
82    ///
83    /// # Panics
84    /// Panics if the provided `path` is longer than [`u32::MAX`] bytes.
85    pub fn remove_config(&mut self, path: &str) -> bool {
86        let path_len = u32::try_from(path.len()).expect("path is too long");
87        unsafe {
88            libddwaf_sys::ddwaf_builder_remove_config(self.raw, path.as_ptr().cast(), path_len)
89        }
90    }
91
92    /// Returns the number of configuration paths currently loaded in this [`Builder`], optionally
93    /// filtered by a regular expression.
94    ///
95    /// # Panics
96    /// Panics if the provided `filter` regular expression is longer than [`u32::MAX`] bytes.
97    #[must_use]
98    pub fn config_paths_count(&mut self, filter: Option<&'_ str>) -> u32 {
99        let filter = filter.unwrap_or("");
100        let filter_len = u32::try_from(filter.len()).expect("filter is too long");
101        unsafe {
102            libddwaf_sys::ddwaf_builder_get_config_paths(
103                self.raw,
104                null_mut(),
105                filter.as_ptr().cast(),
106                filter_len,
107            )
108        }
109    }
110
111    /// Returns the configuration paths currently loaded in this [`Builder`], optionally filtered by
112    /// a regular expression.
113    ///
114    /// # Panics
115    /// Panics if the provided `filter` regular expression is longer than [`u32::MAX`] bytes.
116    #[must_use]
117    pub fn config_paths(&mut self, filter: Option<&'_ str>) -> WafOwnedDefaultAllocator<WafArray> {
118        // SAFETY: ddwaf_builder_get_config_paths uses the default allocator
119        let mut res = WafOwnedDefaultAllocator::<WafArray>::default();
120        let filter = filter.unwrap_or("");
121        let filter_len = u32::try_from(filter.len()).expect("filter is too long");
122        let _ = unsafe {
123            libddwaf_sys::ddwaf_builder_get_config_paths(
124                self.raw,
125                res.as_raw_mut(),
126                filter.as_ptr().cast(),
127                filter_len,
128            )
129        };
130        res
131    }
132
133    /// Builds a new [`Handle`] from the current configuration in this [`Builder`].
134    ///
135    /// Returns [`None`] if the builder fails to create a new [`Handle`], meaning the current
136    /// configuration contains no active instructions (no rules nor processors are available).
137    #[must_use]
138    pub fn build(&mut self) -> Option<Handle> {
139        let raw = unsafe { libddwaf_sys::ddwaf_builder_build_instance(self.raw) };
140        if raw.is_null() {
141            return None;
142        }
143        Some(Handle { raw })
144    }
145}
146impl Drop for Builder {
147    fn drop(&mut self) {
148        unsafe { libddwaf_sys::ddwaf_builder_destroy(self.raw) }
149    }
150}
151
152// SAFETY: no thread-local data and no data can be changed under us if we have an owning handle
153unsafe impl Send for Builder {}
154// SAFETY: changes are only made through exclusive references
155unsafe impl Sync for Builder {}