T09 · Insecure Skill Coding Practices
Warning
- Location
SKILL.md:29- Finding
Unbounded Per-User State Enables Memory Exhaustion
- Content
View full analysis
now - timestamp < tierConfig.window ); // Check if under limit if (validRequests.length >= tierConfig.requests) { const oldestRequest = validRequests[0]; const resetIn = tierConfig.window - (now - oldestRequest); return { allowed: false, reason: 'rate_limit_exceeded', limit: tierConfig.requests, remaining: 0, resetIn: Math.ceil(resetIn / 1000) }; } // Add current request validRequests.push(now); this.requests.set(userId, validRequests); ``` ### Technical Analysis The rate limiter stores request history in a process-wide `Map` indexed by `userId`. Every previously unseen identifier passed to `checkLimit` creates a new entry. There is no global capacity limit, automatic expiration mechanism, periodic cleanup, or eviction policy for inactive identities. Expired timestamps are filtered only when the same `userId` makes another request. Even then, the identifier remains in the map after the filtered array is stored again. The only method that fully removes an entry is `resetUser`, which requires an explicit external call and is not integrated into routine cleanup. Consequently, if an untrusted client can influence `userId`, it can continuously submit unique identifiers and cause persistent heap growth. Very long identifier strings can further increase memory consumption. Per-identity rate limits do not prevent this attack because each new identifier receives an independent quota. ### Attack Path 1. An application instantiates `RateLimiter` in a long-running Node.js process. 2. The application passes a client-contro ...[truncated 1460 chars]- Remediation
View remediation
