diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 1ebf946b..b91708ef 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -18,3 +18,7 @@ **Vulnerability:** `crypto.timingSafeEqual` throws an error if buffers are different lengths, which exposes the application to timing attacks because the error throwing takes a different amount of time than a successful byte-by-byte comparison. **Learning:** Always check buffer lengths before calling `timingSafeEqual`. To ensure constant time regardless of length, compare the expected buffer to itself when lengths don't match. **Prevention:** Compare lengths first, and use a dummy `timingSafeEqual(expected, expected)` on mismatch to mitigate timing leaks. +## 2025-02-14 - [Input Length Limits (DoS prevention)] +**Vulnerability:** External input APIs (`packages/functions/src/lookupCharacter.ts`) accepted unbounded string sizes which were then fed into regular expressions (`/^[a-zA-Z0-9\s'-]+$/`), exposing the environment to Regex Denial of Service (ReDoS) and unexpectedly large downstream requests to external APIs like Battle.net. +**Learning:** Checking parameter types isn't enough; lengths must also be strictly bounded to mitigate resource exhaustion or ReDoS attacks. +**Prevention:** Always add a maximum length constraint (e.g., `< 50`) immediately after type validation before applying regex on external endpoints. diff --git a/packages/functions/src/lookupCharacter.ts b/packages/functions/src/lookupCharacter.ts index 29d5f8f0..bf5c21e7 100644 --- a/packages/functions/src/lookupCharacter.ts +++ b/packages/functions/src/lookupCharacter.ts @@ -74,6 +74,12 @@ export const lookupCharacter = onCall( throw new HttpsError('invalid-argument', 'name, realm, and region must be strings'); } + // Security Enhancement: Add input length limits to prevent DoS via ReDoS on the validation pattern + // or passing excessively large strings to Battle.net APIs. WoW names/realms max out well under 50 chars. + if (name.length > 50 || realm.length > 50 || region.length > 50) { + throw new HttpsError('invalid-argument', 'Input exceeds maximum allowed length'); + } + // Validate inputs contain only valid WoW name/realm slug characters (letters, digits, hyphens, spaces, apostrophes) const validPattern = /^[a-zA-Z0-9\s'-]+$/; if (!validPattern.test(name) || !validPattern.test(realm) || !validPattern.test(region)) {