Skip to content

Light WAF v0.3.0

Choose a tag to compare

@0x00spor3 0x00spor3 released this 05 Jul 14:15

Light WAF v0.3.0 — Reload-surviving module injection (.module_factory)

A fast, modular Layer-7 Web Application Firewall written in Rust, operating as a
reverse proxy: it inspects every HTTP request, accumulates a CRS-style anomaly score,
and decides Allow / Block (403) / Reject (400 | 429) before forwarding to the backend.

This is a small, additive release focused on the embedding surface. There are no
changes to detection
— the corpus verdicts are identical to v0.2.0. The whole workspace is
bumped to 0.3.0 together (uniform versioning), even though only waf-proxy's public surface grew.

What's new

ProxyBuilder::module_factory — injected modules survive a reload (the headline)

Before 0.3, extra detection modules injected through the embedding builder
(.modules(..) / .add_module(..)) were built once and then silently dropped on a
config reload
(the core rebuilt the pipeline with an empty extra set). An embedder's
premium modules vanished on the first SIGHUP.

The new seam fixes that:

  • .module_factory(F) takes a Fn() -> Result<Vec<Box<dyn WafModule>>, _> + Send + Sync.
    The core runs it at bind and again on every config reload (in place of the old empty
    set), so injected modules survive a SIGHUP and are re-init'd — e.g. a changed
    GraphQL SDL or gRPC descriptor is re-read on reload.
  • Whole-set fallibility. The factory returns a Result as a unit: if a rebuild fails (a
    schema file went invalid on disk, a signed plugin no longer verifies), that reload is
    aborted and the last-good modules are kept — never an unprotected window, exactly like a
    rejected config. (Box<dyn WafModule> is not cloneable, so the whole-abort is precisely what
    preserves the working modules.)

WafModule itself is unchanged; this is a builder seam, not a trait change. The static
.modules(..) / .add_module(..) methods still exist (built once, boot-only) for tests and
simple embeds. See BOUNDARY.md
§4–§5 (additive builder evolution) and ARCHITECTURE.md §3/§9.

Compatibility

  • Additive builder surface. .module_factory and the public ModuleFactory type alias are
    new; existing Proxy::builder(..).build() and Proxy::bind(..) are unchanged.
  • One minor breaking edge: waf_proxy::config::LoadError gained a ModuleFactory(String)
    variant (a reload where the factory failed) and is now #[non_exhaustive]. Code that
    matched it exhaustively must add a _ => arm; most callers only Display it and are
    unaffected. This is the 0.x minor-version slot for such a change, and #[non_exhaustive]
    makes future additions non-breaking.
  • Detection unchanged — validation corpus 10/10, 0% false positives at PL3, same
    recall as v0.2.0 (the seam is a pure addition, not a detection change).
  • clippy -D warnings clean across the workspace; full test suite green, including two new
    integration tests proving an injected module survives a reload and that a factory error
    aborts the reload without dropping protection.

Install / upgrade

# the binary (command: `waf`)
cargo install waf-proxy        # picks up 0.3.0

# or use the core as a library (open-core, no fork — embed via Proxy::builder + seams)
cargo add waf-core@0.3

Published crates

Crate Role
waf-core Base types, WafModule/Decision, StateStore seam
waf-normalizer Decode + NFKC + parsing + limits
waf-pipeline Phased orchestrator + anomaly scoring
waf-detection Detection modules + fast-path prefilter
waf-wasm Proxy-Wasm runtime (wasmi)
waf-proxy The binary (waf) + embeddable lib (ProxyBuilder)

License

Open source under Apache-2.0. The open-source vs. enterprise boundary is documented
normatively in BOUNDARY.md.