From 2d11c59e465d7ae6e3ece8bab807d60e95b8d746 Mon Sep 17 00:00:00 2001 From: Mahesh Makani Date: Tue, 4 Feb 2025 11:01:41 +0000 Subject: [PATCH] fix(rate-limit): update regex to remove email alias Fixes: https://github.com/guardian/gateway/security/code-scanning/23 The old regex is possibly vulnerable to ReDoS attacks when removing email aliases. Where the old regex may run slow on strings starting with '+' and with many repetitions of '+'. Although it is unlikely an attacker could use this to perform a DoS attack, as emails are already validated in Identity/Okta, we should still prevent the possibility of this occuring. This PR fixes the regex to make it safe. ``` - const removalRegex = /\+.*@/g; + const removalRegex = /\+[^@]*@/g; ``` The fix uses `[^@]*` instead of `.*` to explicitly match non-`@` characters, which prevents backtracking as the new regex stops at the first `@`. For example with the old behaviour: ``` Input: "test+alias1+alias2@example.com" 1. Matches '+' 2. '.*' greedily consumes all characters 3. Fails to match '@' at end 4. Backtracks one character at a time until '@' is found ``` With the worst case scenario being with time complexity `O(2^n)`. ``` Input: "test+aaaaaaaaaaaaaaaaaaaaaaX" (no @ symbol) 1. '+' matches 2. '.*' greedily consumes all 'a's 3. Fails to find '@' 4. Backtracks for each 'a' 5. Try all possible combinations ``` Now with the new regex for both examples: ``` Input: "test+alias1+alias2@example.com" 1. Matches '+' 2. [^@]* matches any non-@ character deterministically 3. Stops immediately when '@' is found 4. No backtracking needed ``` and for the worst case scenario ``` Input: "test+aaaaaaaaaaaaaaaaaaaaaaX" (no @ symbol) 1. '+' matches 2. [^@]* matches all non-@ chars in one pass 3. Fails to find '@', moves on 4. No backtracking ``` which now has timecomplexity `O(n)` regarless of input, reducing the chance of a ReDoS attack. --- src/server/lib/rate-limit/keys.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/lib/rate-limit/keys.ts b/src/server/lib/rate-limit/keys.ts index 2f10db9f4..1e157e10b 100644 --- a/src/server/lib/rate-limit/keys.ts +++ b/src/server/lib/rate-limit/keys.ts @@ -15,7 +15,7 @@ const getRateLimitKey = ( ) => `gw-rl-${route}-${bucketName}${value ? '-' + sha256(value) : ''}`; const removeEmailAlias = (email?: string) => { - const removalRegex = /\+.*@/g; + const removalRegex = /\+[^@]*@/g; return email?.replace(removalRegex, '@'); };