From dbae7a0ce07da6dd97c1a00fc523dcc772d00ee9 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 4 Sep 2026 16:46:37 +0200 Subject: [PATCH] fix: reject an oversized guest bearer before building the cache key The length guard ran after the cache key was built from the token (a copy and a hash of the whole bearer), so cap it before that work. Also correct the strip_prefix comment: it prevents a repeated-prefix bearer from reducing to a valid token cached under a non-canonical key, not the oversized case the length guard handles. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01VF3v6LA9399gNphmZaHYG3 --- backend/windmill-api-auth/src/auth.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/backend/windmill-api-auth/src/auth.rs b/backend/windmill-api-auth/src/auth.rs index 6d96112ec5..d1d0295d4d 100644 --- a/backend/windmill-api-auth/src/auth.rs +++ b/backend/windmill-api-auth/src/auth.rs @@ -175,6 +175,13 @@ impl AuthCache { if is_no_auth() { return Some(OptJobAuthed { authed: no_auth_admin_authed(), job_id: None }); } + // Reject an oversized guest bearer before the cache key is built from it: the key + // copies and hashes the whole token, so the cap should bound that work too. + if token.starts_with(windmill_common::guest_jwt::BEARER_PREFIX) + && token.len() > windmill_common::guest_jwt::MAX_GUEST_JWT_LEN + { + return None; + } let key = ( w_id.as_ref().unwrap_or(&"".to_string()).to_string(), token.to_string(), @@ -222,12 +229,9 @@ impl AuthCache { let Some(w_id) = w_id.as_deref() else { return None; }; - // The cache keys on the whole bearer, so bound it here before verify or caching; - // strip exactly one prefix, or repeated prefixes would shrink an oversized - // bearer past the length check while it is still cached at full size. - if token.len() > windmill_common::guest_jwt::MAX_GUEST_JWT_LEN { - return None; - } + // Strip exactly one prefix: `trim_start_matches` would strip repeated prefixes, + // so `jwt_guest_jwt_guest_` would reduce to a valid token that verifies and + // is then cached under the full, non-canonical bearer key. let jwt = token .strip_prefix(windmill_common::guest_jwt::BEARER_PREFIX) .unwrap_or(token);