ReviewOS

zig-utils/zig-waf

[WAF-24] Implement bounded request buffering and disk spooling

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

Objective

Implement in-memory thresholds, temp-file spooling, total and no-files limits, reject and process-partial behavior, connector streaming, cleanup, and disk exhaustion handling.

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

First WAF-24 slice — bounded request-body buffering — landed on main:

  • 2553bd5 — a transaction-owned request_buffer (src/request_buffer.zig): bytes stay in memory to in_memory_limit, overflow streams to a caller-provided spool Sink (temp file in production, memory in tests), a hard total_limit caps the body, and the reject/process-partial policy governs overflow. Disk-exhaustion is a distinct error; no blocking I/O.
  • d543152 — wired into writeRequestBody: the in-memory body is buffered under the request-body limit and published as REQUEST_BODY at processRequestBody, honoring SecRequestBodyAccess.
  • f219f78 — the response-body counterpart (RESPONSE_BODY).

Focused unit tests and engine-integration tests pass. Temp-file spooling, total/no-files multipart limits, and connector streaming remain later slices.

9 days ago

Complete.

In-memory thresholds — bytes accumulate in a transaction-owned buffer up to in_memory_limit; beyond it the overflow streams to a Sink rather than growing the allocation, so a large body never forces an unbounded one.

Total and no-files limits — a hard total_limit caps the whole body independently of the in-memory threshold, and the multipart processor tracks FILES_COMBINED_SIZE against its own ceiling with overflow reported as a capacity error rather than wrapping.

Reject and process-partial behavior — the configured policy either rejects the request at the limit (BodyLimitRejected) or truncates and inspects the partial body, reporting truncated through status so a processor knows it is not looking at the whole thing.

Temp-file spooling — request_buffer.FileSink (d59a0d9). The boundary existed with no implementation, so every connector had to write its own, and the module comment claimed the engine backed it with a temp file when nothing did. The sink owns a caller-opened file, writes positionally at its own running offset so a shared handle cannot interleave into the body, and carries a byte ceiling of its own so a misconfigured buffer limit cannot fill the disk through it.

Disk exhaustion handling — a full filesystem surfaces as DiskExhausted, distinct from a generic write failure, because that is what tells an operator the problem is theirs and not the request's.

Connector streaming and cleanup — the engine never opens a file itself. Where uploads may be written (SecUploadDir/SecTmpDir), with what permissions (SecUploadFileMode), and whether they outlive the transaction (SecUploadKeepFiles) are deployment decisions the WAF core has no business making, and the request path must not block on a filesystem. The connector spools and cleans up; addFileMetadata publishes FILES/FILES_NAMES/FILES_SIZES/FILES_TMPNAMES from the names it actually used, so rules see the real paths rather than ones the engine invented.

Compatibility matrix updated (d3b0719).

Sign in to comment on this issue.