Skip to main content

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//!     RunnableContext,
32//!     RunResult,
33//! };
34//!
35//! let mut builder = Builder::new(Some(&Config::default()))
36//!     .expect("Failed to build WAF instance");
37//! let rule_set = waf_map!{
38//!     /* Typically obtained by parsing a rules file using the serde feature */
39//!     ("rules", waf_array!{ waf_map!{
40//!         ("id", "1"),
41//!         ("name", "rule 1"),
42//!         ("tags", waf_map!{ ("type", "flow1"), ("category", "test") }),
43//!         ("conditions", waf_array!{ waf_map!{
44//!             ("operator", "match_regex"),
45//!             ("parameters", waf_map!{
46//!                 ("regex", ".*"),
47//!                 ("inputs", waf_array!{ waf_map!{ ("address", "arg1" )} }),
48//!             }),
49//!         } }),
50//!         ("on_match", waf_array!{ "block" })
51//!     } }),
52//! };
53//! let mut diagnostics = WafOwnedDefaultAllocator::<WafMap>::default();
54//! if !builder.add_or_update_config("config/file/logical/path", &rule_set, Some(&mut diagnostics)) {
55//!     panic!("Failed to add or update config!");
56//! }
57//! let waf = builder.build().expect("Failed to build WAF instance");
58//!
59//! // For each new request to be monitored...
60//! let mut waf_ctx = waf.new_context();
61//! let data = waf_map!{
62//!     ("arg1", "value1"),
63//! };
64//! match waf_ctx.run(data, std::time::Duration::from_millis(1)) {
65//!     // Deal with the result as appropriate...
66//!     Ok(RunResult::Match(res)) => {
67//!         assert!(!res.timeout());
68//!         assert!(res.keep());
69//!         assert!(res.duration() >= std::time::Duration::default());
70//!         assert_eq!(res.events().expect("Expected events").len(), 1);
71//!         assert_eq!(res.actions().expect("Expected actions").len(), 1);
72//!         assert_eq!(res.attributes().expect("Expected attributes").len(), 0);
73//!     },
74//!     Err(e) => panic!("Error while running the in-app WAF: {e}"),
75//!     _ => panic!("Unexpected result"),
76//! }
77//! ```
78
79use std::ffi::CStr;
80
81#[cfg(feature = "serde")]
82pub mod serde;
83
84pub mod log;
85pub mod object;
86mod private;
87
88macro_rules! forward {
89    ($($name:ident),*) => {
90        $(
91            mod $name;
92            #[doc(inline)]
93            pub use $name::*;
94        )*
95    };
96}
97
98forward!(builder, config, context, handle);
99
100/// Returns the version of the underlying `libddwaf` library.
101#[must_use]
102pub fn version() -> &'static CStr {
103    let ptr = unsafe { libddwaf_sys::ddwaf_get_version() };
104    if ptr.is_null() {
105        unsafe { CStr::from_ptr("\0".as_ptr().cast()) }
106    } else {
107        unsafe { CStr::from_ptr(ptr) }
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    #[test]
114    #[cfg(not(miri))]
115    fn test_version() {
116        use crate::version;
117
118        if std::env::var("LIBDDWAF_PREFIX").is_ok() {
119            eprintln!("Skipping test_get_version: LIBDDWAF_PREFIX is set");
120            return;
121        }
122
123        assert_eq!(
124            env!("CARGO_PKG_VERSION"),
125            version()
126                .to_str()
127                .expect("Failed to convert version to str")
128        );
129    }
130}