fix(audit-2026-06-11/H-1): request-body ceilings (Caddy proxy + email-inbound webhook)

- Caddy request_body { max_size 12MB } in both Caddyfiles — a hard
  ceiling replacing Caddy's multi-GB default; 12MB clears the
  orchestrator's 10 MiB user-route read. Validated with caddy validate.
- email-inbound router gets an explicit DefaultBodyLimit::max(1MB):
  the public unauthenticated webhook previously rode Axum's 2MB
  extractor default; tightened so a flood can't force large
  allocation + JSON parse per request.

Admin / execute handlers keep Axum's 2MB extractor default (already
bounded); the user-route path keeps its explicit 10 MiB manual read.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-12 18:29:55 +02:00
parent d9fb0e7f85
commit 3ac1022c33
3 changed files with 26 additions and 0 deletions

View File

@@ -32,6 +32,15 @@
}
:80 {
# Audit 2026-06-11 (H-1) — hard request-body ceiling at the proxy.
# Without this, Caddy forwards bodies up to its multi-GB default to
# picloud. 12 MB sits just above the orchestrator's 10 MiB user-route
# read so legitimate large invokes pass; admin / email handlers are
# bounded far tighter by their own Axum extractor limits.
request_body {
max_size 12MB
}
# Baseline headers applied to every response.
header {
X-Content-Type-Options "nosniff"

View File

@@ -19,6 +19,15 @@
{$PICLOUD_DOMAIN} {
encode zstd gzip
# Audit 2026-06-11 (H-1) — hard request-body ceiling at the proxy.
# Without this, Caddy forwards bodies up to its multi-GB default to
# picloud. 12 MB sits just above the orchestrator's 10 MiB user-route
# read so legitimate large invokes pass; admin / email handlers are
# bounded far tighter by their own Axum extractor limits.
request_body {
max_size 12MB
}
# Baseline headers on every response. HSTS is unconditional in prod.
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

View File

@@ -169,12 +169,20 @@ pub struct EmailInboundState {
pub bad_sig_limiter: Arc<BadSignatureLimiter>,
}
/// Per-request body cap for the inbound webhook. Inbound email events
/// are small JSON envelopes (from/to/subject/body); 1 MiB is generous.
/// Audit 2026-06-11 (H-1) — this public, unauthenticated endpoint
/// otherwise rode Axum's 2 MB extractor default; pin it explicitly and
/// tighter so a flood can't force large allocations + JSON parses.
const INBOUND_BODY_LIMIT_BYTES: usize = 1024 * 1024;
pub fn email_inbound_router(state: EmailInboundState) -> Router {
Router::new()
.route(
"/email-inbound/{app_id}/{trigger_id}",
post(receive_inbound_email),
)
.layer(axum::extract::DefaultBodyLimit::max(INBOUND_BODY_LIMIT_BYTES))
.with_state(state)
}