libddwaf/
lib.rs

1#![deny(
2    clippy::correctness,
3    clippy::pedantic,
4    clippy::perf,
5    clippy::style,
6    clippy::suspicious
7)]
8#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
9
10//! Rust bindings for the [`libddwaf` library](https://github.com/DataDog/libddwaf).
11//!
12//! # Basic Use
13//!
14//! The following high-level steps are typically used:
15//! 1. Create a new [Builder]
16//! 2. Add new configurations to it using [`Builder::add_or_update_config`]
17//!     * Rulesets are often parsed from JSON documents using `serde_json`, via
18//!       the `serde` feature.
19//! 3. Call [`Builder::build`] to obtain a new [`Handle`]
20//! 4. For any service request:
21//!     1. Call [`Handle::new_context`] to obtain a new [`Context`]
22//!     2. Call [`Context::run`] as appropriate with the necessary address data
23//!
24//! ```rust
25//! use libddwaf::{
26//!     object::*,
27//!     waf_array,
28//!     waf_map,
29//!     Builder,
30//!     Config,
31//!     RunResult,
32//! };
33//!
34//! let mut builder = Builder::new(&Config::default())
35//!     .expect("Failed to build WAF instance");
36//! let rule_set = waf_map!{
37//!     /* Typically obtained by parsing a rules file using the serde feature */
38//!     ("rules", waf_array!{ waf_map!{
39//!         ("id", "1"),
40//!         ("name", "rule 1"),
41//!         ("tags", waf_map!{ ("type", "flow1"), ("category", "test") }),
42//!         ("conditions", waf_array!{ waf_map!{
43//!             ("operator", "match_regex"),
44//!             ("parameters", waf_map!{
45//!                 ("regex", ".*"),
46//!                 ("inputs", waf_array!{ waf_map!{ ("address", "arg1" )} }),
47//!             }),
48//!         } }),
49//!         ("on_match", waf_array!{ "block" })
50//!     } }),
51//! };
52//! let mut diagnostics = WafOwned::<WafMap>::default();
53//! if !builder.add_or_update_config("config/file/logical/path", &rule_set, Some(&mut diagnostics)) {
54//!     panic!("Failed to add or update config!");
55//! }
56//! let waf = builder.build().expect("Failed to build WAF instance");
57//!
58//! // For each new request to be monitored...
59//! let mut waf_ctx = waf.new_context();
60//! let data = waf_map!{
61//!     ("arg1", "value1"),
62//! };
63//! match waf_ctx.run(Some(data), None, std::time::Duration::from_millis(1)) {
64//!     // Deal with the result as appropriate...
65//!     Ok(RunResult::Match(res)) => {
66//!         assert!(!res.timeout());
67//!         assert!(res.keep());
68//!         assert!(res.duration() >= std::time::Duration::default());
69//!         assert_eq!(res.events().expect("Expected events").len(), 1);
70//!         assert_eq!(res.actions().expect("Expected actions").len(), 1);
71//!         assert_eq!(res.attributes().expect("Expected attributes").len(), 0);
72//!     },
73//!     Err(e) => panic!("Error while running the in-app WAF: {e}"),
74//!     _ => panic!("Unexpected result"),
75//! }
76//! ```
77
78use std::ffi::CStr;
79
80#[cfg(feature = "serde")]
81pub mod serde;
82
83pub mod log;
84pub mod object;
85mod private;
86
87macro_rules! forward {
88    ($($name:ident),*) => {
89        $(
90            mod $name;
91            #[doc(inline = true)]
92            pub use $name::*;
93        )*
94    };
95}
96
97forward!(builder, config, context, handle);
98
99/// Returns the version of the underlying `libddwaf` library.
100#[must_use]
101pub fn get_version() -> &'static CStr {
102    let ptr = unsafe { libddwaf_sys::ddwaf_get_version() };
103    if ptr.is_null() {
104        unsafe { CStr::from_ptr("\0".as_ptr().cast()) }
105    } else {
106        unsafe { CStr::from_ptr(ptr) }
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use crate::get_version;
113
114    #[test]
115    fn test_get_version() {
116        assert_eq!(
117            env!("CARGO_PKG_VERSION"),
118            get_version()
119                .to_str()
120                .expect("Failed to convert version to str")
121        );
122    }
123}