# coraza-rs
Safe Rust bindings to [OWASP Coraza](https://coraza.io/) Web Application Firewall, based on the [official C bindings](https://github.com/corazawaf/libcoraza).
Coraza is a Go-based WAF compatible with ModSecurity's SecLang. This repository provides Rust crates to embed Coraza into Rust applications.
## Crates
| [`coraza`](coraza/) | Safe, idiomatic Rust API |
| [`coraza-sys`](coraza-sys/) | Raw FFI bindings (auto-generated from C header) |
| [`examples`](examples/) | Usage examples |
## Quick Start
```rust
use coraza::WafConfig;
fn main() {
let waf = WafConfig::new()
.unwrap()
.with_directives("SecRuleEngine DetectionOnly")
.with_directives("SecRequestBodyAccess On")
.build()
.unwrap();
let mut tx = waf.new_transaction();
tx.process_connection("127.0.0.1", 8080, "localhost", 80).unwrap();
tx.process_uri("/path", "GET", "HTTP/1.1").unwrap();
tx.add_request_header("Host", "localhost");
tx.process_request_headers().unwrap();
tx.process_logging();
if let Some(intervention) = tx.intervention() {
eprintln!("Blocked with status {}", intervention.status);
}
}
```
## Build Requirements
- **Rust** 1.75+ (edition 2021)
- **Go** 1.21+ (for compiling the Coraza WAF engine)
- **C compiler** (gcc/clang on Unix, MSVC on Windows)
- **libclang** (for bindgen)
On Ubuntu/Debian:
```sh
sudo apt install golang-go libclang-dev
```
On macOS:
```sh
brew install go llvm
```
## Building
```sh
cargo build
```
The first build compiles the Go WAF engine into a static library and generates Rust FFI bindings. Subsequent builds are fast.
## Examples
```sh
cargo run --bin simple_get
cargo run --bin deny_rule
cargo run --bin with_callbacks
```
### `simple_get`
Minimal WAF usage with DetectionOnly mode — processes a GET request and checks for interventions.
### `deny_rule`
WAF with a deny rule that blocks requests from specific IPs — demonstrates handling a 403 intervention.
### `with_callbacks`
Registers debug log and error callbacks — shows how to capture matched rule details.
## API Overview
### Configuration
```rust
use coraza::{WafConfig, LogLevel};
let waf = WafConfig::new()?
.with_directives("SecRuleEngine On")
.with_directives("SecRequestBodyAccess On")
.with_debug_log_callback(|level, msg, fields| {
eprintln!("[{level}] {msg} {fields}");
})
.with_error_callback(|rule| {
eprintln!("Rule {} matched: {}", rule.rule_id, rule.message);
})
.build()?;
```
### Transaction Processing
Transactions follow Coraza's phase-based lifecycle:
```rust
let mut tx = waf.new_transaction();
// Phase 0: Connection & URI
tx.process_connection("127.0.0.1", 8080, "localhost", 80)?;
tx.process_uri("/path", "GET", "HTTP/1.1")?;
// Phase 1: Request headers
tx.add_request_header("Host", "localhost");
tx.process_request_headers()?; // may return Err(Intervention)
// Phase 2: Request body (if SecRequestBodyAccess On)
tx.append_request_body(b"hello=world")?;
tx.process_request_body()?;
// Phase 3: Response headers
tx.process_response_headers(200, "HTTP/1.1")?;
// Phase 4: Response body (if SecResponseBodyAccess On)
tx.append_response_body(b"<html>...</html>")?;
tx.process_response_body()?;
// Phase 5: Logging (always run, even on interruption)
tx.process_logging();
// Check for rule matches
if let Some(intervention) = tx.intervention() {
eprintln!("Blocked: {}", intervention.status);
}
```
## Thread Safety
| `Waf` | Yes | Yes | Immutable after creation; safe to share for concurrent transaction creation |
| `WafConfig` | Yes | No | Builder consumed by `build()` |
| `Transaction` | Yes | No | Mutable state; must not be shared across threads |
## License
Licensed under the [Apache License, Version 2.0](LICENSE).