Skip to content

Add waf.test contract: dry-run a zone draft or expression against a sample request#129

Merged
acoshift merged 2 commits into
mainfrom
waf-test
Jul 11, 2026
Merged

Add waf.test contract: dry-run a zone draft or expression against a sample request#129
acoshift merged 2 commits into
mainfrom
waf-test

Conversation

@acoshift

Copy link
Copy Markdown
Member

Summary

PR 1 of the waf.test feature (design doc: PLAN-waf-test.md, workspace root) — the api-repo contract only. CEL compilation/evaluation lands in apiserver (PR 2) with engine parity via parapet/pkg/waf.

  • WAF.Test interface method + waf.test client method (client/waf.go)
  • WAFTest: expression mode XOR zone-draft mode (draft reuses the waf.set structural contract via validWAFRules/validWAFLimits)
  • WAFTestRequest: synthetic sample request with structural Valid() — path required + leading /, RFC 7230 method token, scheme http|https, header/cookie name grammar + size caps, country/asn passthrough (no GeoIP lookup server-side)
  • WAFTestResult + per-rule/per-limit results (Evaluated/Terminal/Counted semantics documented) and a one-row Table()
  • WAFTest* constants in constraint.go

Impossible-in-prod sample states are rejected so a dry run can never match in ways production traffic can't: a headers["host"] entry (net/http moves Host into r.Host), case-duplicate header names (canonicalized to one entry in prod; last-write-wins map order would also make the result nondeterministic), and a non-IP ip (X-Real-IP is proxy-set from a real peer). Query must be raw (no leading ?), ASN is capped at 32 bits.

Tests

  • go build ./... && go vet ./... && go test ./... — all pass (63 tests), gofmt clean
  • New waf_test.go coverage: both modes valid, limits-only draft, mode XOR, every rejection path above, casing-not-normalized contract, Table() including - placeholders on pass

Post-merge

  • apiserver (PR 2) re-pins github.com/deploys-app/api to this merge
  • At launch (with PR 2), seed the per-(project,action) API quota '*' row for waf.test

acoshift added 2 commits July 10, 2026 23:36
…ample request

WAF.Test + WAFTest/WAFTestRequest/WAFTestResult (+ per-rule/per-limit
result types), structural Valid() (mode XOR, sample-request caps, host
header rejected in favor of the host field), WAFTest* constraint
constants, and the waf.test client method. CEL compilation/evaluation
lives in apiserver (engine parity via parapet/pkg/waf), not here.
- reject case-insensitive duplicate header names (impossible prod state,
  and last-write-wins map order would make the dry run nondeterministic)
- require ip to parse as an IP when set (same impossible-state class as
  the banned host header)
- reject a leading ? in query (enforce the documented contract)
- cap asn at 32 bits (WAFTestMaxASN)
- lift the method length cap into WAFTestMaxMethodLength
- render - for empty rule/status cells in the result table
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant