Web Application Firewall (WAF) Compatibility
This platform doesn't ship a bundled WAF (ModSecurity, AWS WAF, Cloudflare,
etc.) — that's an infrastructure-layer choice left to the deployment
operator, same as the TLS-terminating proxy it typically sits alongside (see
tls-mtls-certificates.md). This document is
the compatibility checklist for whichever WAF you put in front: which of this
platform's own request/response shapes a default-tuned WAF is most likely to
flag as a false positive, and which headers the app depends on that a WAF
must be configured to pass through untouched.
Traffic shapes a default WAF ruleset may flag
- SSE streams (AI chat completions in Module 5/11, live recon/vuln job
logs in Modules 3/4) are long-lived (up to
proxy_read_timeout 3600sin bothdeploy/nginx/lb.confanddeploy/nginx/tls-termination.conf.example)text/event-streamresponses with noContent-Length. Some WAF rule sets (notably ModSecurity's OWASP Core Rule Set with response-body buffering enabled) will either time out or attempt to buffer the entire response before releasing it, which breaks streaming outright or defeats its purpose. Disable response-body inspection/buffering for the SSE routes specifically (chat completion endpoints, job log-streaming endpoints) — request-body inspection on the same routes is unaffected and should stay on. - Bug bounty finding/report content and AI-generated report text
(Modules 6, 11) routinely contains security payload examples — SQL
injection strings, XSS snippets, command injection sequences — as
legitimate data, not attacks, since this is a pentesting platform. Generic
WAF rules that block requests containing
<script>,UNION SELECT,; rm -rf, etc. in the request body will false-positive heavily on Create/Update Finding, report draft, and payload-library endpoints. Either exclude those specific routes from body-content WAF rules, or run the WAF in detection/log-only mode for this platform rather than blocking mode — a hard block on those routes makes the product unusable for its actual purpose. - Bulk import endpoints (bug bounty program importer, backup restore
flows, plugin/script installation) accept larger-than-typical request
bodies. Confirm the WAF's own request-size limit is raised at least to
match whatever this platform's own limits are configured to (Express's
default body-parser limit, or a
client_max_body_sizeset at the nginx layer) — a WAF with a lower ceiling than the app rejects legitimate large imports before they even reach the app to be validated properly. - Webhook delivery payloads (outbound, from this platform to a
third-party endpoint —
webhook-delivery-queue.service.ts) aren't WAF-relevant on the way out, but if you also run a WAF in front of a webhook receiver you control (e.g. an internal system consuming this platform's webhooks), the same body-content caveat as findings/reports applies if that receiver ever surfaces raw finding data.
Headers the app depends on — must pass through unmodified
X-Forwarded-For/X-Forwarded-Proto:TRUST_PROXY_HOPS(seeapps/api/src/config/env.validation.tsandbootstrap.ts) tells Express exactly how many proxy hops to trust when resolving the real client IP from these headers. A WAF is itself a hop. If you add a WAF in front of the existing TLS-terminating proxy,TRUST_PROXY_HOPSmust increase by one (or by however many hops the WAF adds) to match, orreq.ip— whichIpAllowlistGuard, audit logging, and rate limiting all key off — will resolve to the WAF's own IP instead of the real client's. Verify with/observability/health's logs or a temporary debug log line after any WAF is added or removed, not just after the initial rollout.Authorization: Bearer <jwt>,X-API-Key, andX-Client-Cert-Verify(only whenMTLS_REQUIRED=true, seetls-mtls-certificates.md): none of these should be stripped, rewritten, or added to a WAF's own default sensitive-header redaction/logging exclusion list in a way that also drops them from the forwarded request — a WAF that scrubsAuthorizationheaders from its own logs (a reasonable, even recommended, default) must still forward the original header value to the upstream app; only the WAF's own logs should redact it.X-Request-Id: set byrequestContextStorage/requestIdMiddlewarefor correlating logs across the request's lifetime — not security-sensitive, but a WAF that unconditionally overwrites incomingX-Request-Idvalues breaks the ability to correlate a client-side error report back to server logs for that same request. Prefer a WAF configuration that only sets this header when absent, not one that always overwrites it.
Rate limiting: avoid double-counting
ThrottlerGuard (registered first in app.module.ts's guard chain, before
IpAllowlistGuard and MtlsGuard) already enforces request-rate limits at
the application layer. A WAF's own rate limiting is a legitimate second layer
of defense (it can shed load before it ever reaches the app, which
ThrottlerGuard can't), but the two limits are independent and not
coordinated — a client that trips the WAF's limit gets a WAF-generated error
page (not this platform's structured JSON error shape), and a client that
trips ThrottlerGuard's limit gets the platform's own 429 response.
Configure the WAF's rate limit above ThrottlerGuard's configured
threshold if the goal is "WAF catches abusive traffic before it costs the app
anything," or independently tune it lower if the goal is a coarser
infrastructure-level ceiling — pick one intent rather than leaving both at
default values that might silently conflict (e.g. the WAF blocking a burst
that ThrottlerGuard would have allowed, on a route where bursts are
expected — bulk import, webhook delivery redelivery).
CORS preflight
app.enableCors({ origin: FRONTEND_URL, credentials: true, ... }) in
bootstrap.ts handles CORS at the application layer already. A WAF
positioned in front should let OPTIONS preflight requests through
untouched (no auth-header requirement, no body-content inspection — a
preflight request has no body) so the browser's preflight round-trip
succeeds before the actual request is even sent; blocking or challenging
(e.g. a JS-challenge/CAPTCHA-style WAF feature) an OPTIONS request breaks
every cross-origin call the frontend makes.
Recommended rollout approach
Given the payload false-positive risk above (security-payload strings being core, legitimate product content), run any new WAF in detection/log-only mode against this platform first, review what it would have blocked over a representative period covering normal finding/report creation and bulk import usage, then tune exclusions before switching to blocking mode. This mirrors the same "disclose what's a starting point, verify before trusting it" posture used throughout this module's other security controls.