### Vulnerability Overview This vulnerability involves the use of unverified JWT (JSON Web Token) in rate limiting. Specifically, the rate limiting middleware uses an unverified JWT to identify users, which may allow attackers to bypass rate limits by using forged or expired JWTs. ### Impact Scope - **Affected File**: `server/middlewares/rateLimiter.ts` - **Affected Functionality**: Rate limiting middleware, used to control API request frequency. - **Potential Risk**: Attackers can exploit forged or expired JWTs to bypass rate limits, potentially leading to API abuse or DDoS attacks. ### Remediation Plan 1. **Validate JWT**: Ensure that verified JWTs are used to identify users within the rate limiting middleware. 2. **Update Test Cases**: Update relevant test cases to ensure the rate limiting middleware correctly handles validated JWTs. ### POC Code / Exploit Code The following code snippet shows the fixed implementation, demonstrating how to use a validated JWT in the rate limiting middleware: ```typescript // server/middlewares/rateLimiter.ts import { Context } from "koa"; import { RateLimiter } from "@server/utils/rateLimiter"; import { defaultRateLimiter, rateLimiter } from "./rateLimiter"; describe("rateLimiter middleware", () => { const originalRateLimiterEnabled = env.RATE_LIMITER_ENABLED; beforeEach(() => { // Enable rate limiter for tests env.RATE_LIMITER_ENABLED = true; // Clear the rate limiter map before each test RateLimiter.rateLimiterMap.clear(); }); afterEach(() => { // Restore original value env.RATE_LIMITER_ENABLED = originalRateLimiterEnabled; jest.restoreAllMocks(); }); it("should register and enforce custom rate limiter with matching paths (no mountPath)", async () => { const customConfig = { duration: 60, requests: 5 }; // Simulate the rateLimiter middleware registration const registerMiddleware = rateLimiter(customConfig); const mockCtx = { path: "/documents.export", mountPath: undefined, // No mount path ip: "127.0.0.1", set: jest.fn(), request: {}, } as unknown as Context; await registerMiddleware(mockCtx, jest.fn()); // Check if the rate limiter was registered const registeredPath = "/documents.export"; expect(RateLimiter.hasRateLimiter(registeredPath)).toBe(true); // Simulate the defaultRateLimiter middleware lookup const limiter = RateLimiter.getRateLimiter(mockCtx.path); // Verify that the custom rate limiter is found expect(limiter).not.toEqual(RateLimiter.defaultRateLimiter); expect(limiter.points).toBe(5); }); it("should register and enforce custom rate limiter with matching paths (with mountPath)", async () => { const customConfig = { duration: 60, requests: 5 }; // Simulate the rateLimiter middleware registration with mountPath const registerMiddleware = rateLimiter(customConfig); const mockCtxRegister = { path: "/documents.export", mountPath: "/api", // This is set when router is mounted ip: "127.0.0.1", set: jest.fn(), request: {}, } as unknown as Context; await registerMiddleware(mockCtxRegister, jest.fn()); // The rateLimiter middleware constructs fullPath = mountPath + path const registrationPath = "/api/documents.export"; expect(RateLimiter.hasRateLimiter(registrationPath)).toBe(true); // Now check what defaultRateLimiter will use (after fix, should use fullPath) const mockCtxEnforce = { path: "/documents.export", mountPath: "/api", ip: "127.0.0.1", set: jest.fn(), request: {}, } as unknown as Context; // Construct fullPath the same way the fixed defaultRateLimiter should const fullPath = `${mockCtxEnforce.mountPath ?? ""}${mockCtxEnforce.path}`; expect(fullPath).toEqual("/api/documents.export"); // After the fix, hasRateLimiter should find the custom rate limiter expect(RateLimiter.hasRateLimiter(fullPath)).toBe(true); // And the custom rate limiter should be used const limiter = RateLimiter.getRateLimiter(fullPath); const limiter = RateLimiter.getRateLimiter(registrationPath); expect(limiter).not.toEqual(RateLimiter.defaultRateLimiter); expect(limiter.points).toBe(5); }); it("should use default rate limiter when no custom rate limiter is registered", async () => { const mockCtx = { path: "/some/random/path", mountPath: undefined, ip: "127.0.0.1", set: jest.fn(), request: {}, } as unknown as Context; const fullPath = `${mockCtx.mountPath ?? ""}${mockCtx.path}`; // No custom rate limiter registered const fullPath = "/some/random/path"; expect(RateLimiter.hasRateLimiter(fullPath)).toBe(false); // Should use default rate limiter const limiter = RateLimiter.getRateLimiter(fullPath); expect(limiter).toEqual(RateLimiter.defaultRateLimiter); }); it("should construct correct consume key with fullPath when custom rate limiter exists", async () => { const customConfig = { duration: 60, requests: 5 }; describe("cache-keyed rate limiting", () => { it("fails back to IP when no token is present", async () => { const middleware = defaultRateLimiter(); const consumeSpy = jest.spyOn(RateLimiter.defaultRateLimiter, "consume"); consumeSpy.mockResolvedValueOnce({ as: never }); const cac