ReviewOS

zig-utils/zig-waf

[WAF-19] Integrate zig-injection SQLi and XSS operators

#20
Closed chris opened this 9 days ago ·
9 days ago

Objective

Implement detectSQLi and detectXSS exclusively through released zig-injection, including fingerprints, captures, errors, and parity fixtures.

Deliverables

  • Implement the complete scoped behavior described above.
  • Keep request-path work independent from UI, PostgreSQL, and blocking audit I/O.
  • Use Zig 0.17-dev and dependencies pinned through Pantry; do not add Git submodules.
  • Keep public APIs explicit about ownership, resource limits, errors, security policy, and compatibility.
  • Add focused unit, integration, differential, fuzz, benchmark, and documentation coverage as applicable.

Acceptance criteria

  • The scoped implementation and tests are merged on main in small conventional commits.
  • Zig formatting, focused tests, and the relevant CI suite pass.
  • Compatibility claims have executable evidence and no unexplained skips or silent fallbacks.
  • Untrusted inputs have deterministic bounds; hot paths have benchmark evidence.
  • Documentation and the machine-readable compatibility matrix are updated.

Relationships

Blocked by

Blocks

Baselines

  • ModSecurity 3.0.16
  • Coraza 3.7.0
  • OWASP CRS 4.28.0
  • libinjection 4.0.0
  • Zig 0.17-dev resolved and locked through Pantry
9 days ago

Initial native adapter shipped in 53afd60: local zig-injection package wiring plus a reusable allocation-free SQLi operator that preserves reason, confidence, fingerprint, and inconclusive state. Full issue completion remains blocked on INJECT-11 and SecLang operator integration.

9 days ago

Dependency update: WAF-11 #12 is complete at 19b64bc with exact hosted CI green. This issue can now consume the immutable typed IR, stable diagnostics, prefilters/macro programs, fingerprints, shared payloads, and explicit WAF plan ownership contract.

9 days ago

WAF-19 — the zig-injection SQLi and XSS operators — is implemented with full libinjection detectors (no heuristics or placeholders).

zig-injection (published as 0.1.0):

  • 1c62a84 — a faithful, allocation-free port of the libinjection HTML5 XSS detector (tokenizer + black tag/attr/URL/event tables), validated against the pinned libinjection XSS differential vectors.
  • 593add5 — replaced the interim heuristic with the full libinjection SQLi port: byte-parser tokenizer, two- and three-token folding, the 9352-entry keyword/fingerprint table, and the blacklist/whitelist checks across five ANSI/MySQL quote contexts. Validated byte-exact against the pinned libinjection sqli (54) and folding (118) corpora.
  • 0188f05 — a versioned C ABI (zig_injection_is_sqli/is_xss) and static library.

zig-waf:

  • 38ecc78 — @detectSQLi and @detectXSS operators over the new libinjection API, validated against the retained Coraza detectSQLi (5) and detectXSS (3) corpora; matrix entry operator.detect-sqli-xss marked implemented.
  • 2130488 — pins the zig-injection 0.1.0 release.

Both detectors are allocation-free with reusable per-worker scratch. The compatibility claim now has executable byte-exact evidence against libinjection itself, not just the small Coraza operator corpus.

9 days ago

Complete.

Exclusively through released zig-injection — src/operators.zig imports the injection module and nothing else does detection. The package is pinned by hash in build.zig.zon from the Pantry registry (zig_injection-0.1.0), so there is no vendored copy and no local fork to drift.

Fingerprints — SqlInjection.evaluate returns the fingerprint alongside the verdict rather than reducing the detector to a boolean, and the state is per-worker scratch holding no cross-request data.

Captures — this was genuinely missing and is now implemented (8a86d73). ModSecurity 3.0.16 stores the fingerprint in TX.0 when the rule carries capture (src/operators/detect_sqli.cc); zig-waf computed it and discarded it, so a rule reading %{TX.0} saw nothing.

A fingerprint is a summary of the input's token shape, not a substring of it, so it cannot be a capture range the way a regex group is. Added derived captures — text an operator produced rather than sliced out of the matched value — bounded exactly like a matched value, since an operator is not a trusted source of unbounded text. A rule without capture leaves TX.0 untouched, as upstream does. Asserted against the detector's own output rather than a transcribed fingerprint, and through %{TX.0} in a setvar so it is visible to rule logic and not only to the audit record.

Errors — upstream's LIBINJECTION_RESULT_ERROR path (fail-safe match, TX.0 = the input) is reachable only through its test-only override hook in libinjection_adapter.cc; real libinjection_sqli returns true or false. There is therefore nothing to mirror, and the released zig-injection's boolean result is faithful rather than lossy. Recorded here rather than left as an apparent omission.

Parity fixtures — tests/fixtures/operators/coraza/detectSQLi.json and detectXSS.json, digest-pinned in tests/operator_evidence.zig alongside the other operator corpora, so a fixture cannot be edited to make a failing case pass without the digest changing.

End-to-end evidence: real OWASP CRS 4.28 blocks SQLi and XSS through these operators (#38).

Sign in to comment on this issue.