mirror of
https://github.com/Sea-Haven-Industries/sh-mcp.git
synced 2026-10-07 02:22:04 +00:00
Fix polynomial ReDoS in Bearer-token extraction (CodeQL js/polynomial-redos)
extractBearerToken matched /^Bearer\s+(.+)$/ — \s and . both match a space, so the two quantifiers overlap and a crafted header can drive polynomial backtracking. Require the capture to start with a non-whitespace char (/^Bearer\s+(\S.*)$/), removing the ambiguity → linear match. Behavior is unchanged for real tokens; +2 regression tests.
This commit is contained in:
parent
00762de0a6
commit
f4111e4e17
2 changed files with 15 additions and 1 deletions
|
|
@ -260,4 +260,14 @@ describe('extractBearerToken', () => {
|
|||
it('throws AuthError on a non-Bearer header', () => {
|
||||
expect(() => extractBearerToken('Basic abc')).toThrow(AuthError);
|
||||
});
|
||||
|
||||
// ReDoS guard (CodeQL js/polynomial-redos): the matcher is /\s+(\S.*)/, not
|
||||
// the ambiguous /\s+(.+)/. These pin the behavior that the `\S` fix preserves.
|
||||
it('still captures a token that follows multiple separating spaces', () => {
|
||||
expect(extractBearerToken('Bearer abc.def')).toBe('abc.def');
|
||||
});
|
||||
|
||||
it('rejects a "Bearer" header with no token after the whitespace', () => {
|
||||
expect(() => extractBearerToken(`Bearer ${' '.repeat(5_000)}`)).toThrow(AuthError);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -189,7 +189,11 @@ export function extractBearerToken(req: unknown): string {
|
|||
if (!header) {
|
||||
throw new AuthError('missing_token', 'No Authorization header present.');
|
||||
}
|
||||
const match = /^Bearer\s+(.+)$/i.exec(header.trim());
|
||||
// `\s+(\S.*)` — NOT `\s+(.+)`: requiring the capture to start with a
|
||||
// non-whitespace char removes the quantifier overlap (both `\s` and `.`
|
||||
// match a space), which otherwise allows polynomial backtracking on a
|
||||
// crafted all-whitespace header (CodeQL js/polynomial-redos). Linear now.
|
||||
const match = /^Bearer\s+(\S.*)$/i.exec(header.trim());
|
||||
const token = match?.[1]?.trim();
|
||||
if (!token) {
|
||||
throw new AuthError('missing_token', 'Authorization header is not a Bearer token.');
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue