Skip to content

fix: resolve ratelimit, worker eviction, body hang, and path traversal - #236

Merged
1Lucas1apk merged 1 commit into
PerformanC:devfrom
SreerajSK990:fix/core-stability-and-security
Sep 13, 2026
Merged

1Lucas1apk merged 1 commit into
PerformanC:devfrom
SreerajSK990:fix/core-stability-and-security

Conversation

@SreerajSK990

Copy link
Copy Markdown
Contributor

Changes

  • RateLimitManager (src/managers/rateLimitManager.ts): Made the cleanup loop scope-aware so each rate-limit key is pruned based on its own configured time window (global, IP, guild, user) instead of purging all keys against the shortest window.
  • SourceWorkerManager (src/managers/sourceWorkerManager.ts):
    • Added an indexOf === -1 guard when handling worker exit events so unknown/already removed workers do not accidentally evict active workers from the pool.
    • Added a !res.headersSent check before sending 504 on timeouts to avoid ERR_HTTP_HEADERS_SENT crashes when streaming has already begun.
  • API Body Reader (src/api/index.ts):
    • Resolved promise hangs on malformed JSON and early returns by ensuring the reader promise always settles and cleans up listeners.
    • Added socket error handling and an inactivity timeout.
    • Replaced string concatenation with buffer arrays (Buffer.concat) for incoming chunks.
  • Local Source Traversal (src/sources/local.ts):
    • Enforced basePath boundaries on both relative and absolute paths, preventing path traversal outside the configured directory.
    • Added realpath verification to guard against symlink escapes, verified targets are regular files, and added the same boundary check inside loadStream.
  • Typings (src/typings/api/api.types.ts): Added headersSent to ApiResponse and bodyTimeout to server options.

Why

  • Prevents rate limits from being silently bypassed when different scopes have different window lengths.
  • Prevents the worker pool from gradually draining to zero on worker restarts.
  • Fixes unhandled exceptions (ERR_HTTP_HEADERS_SENT) during worker timeouts on active streams.
  • Prevents API requests from hanging indefinitely on broken connections or bad payloads.
  • Closes local filesystem traversal and information leaks via the local audio source.

Checkmarks

  • The modified endpoints have been tested.
  • Used the same indentation as the rest of the project.
  • Still compatible with LavaLink clients.

Additional information

  • Verified clean with npm run type-check (0 errors) and Biome check/format (0 errors).
  • Changes are strictly isolated to the 5 core source files.

@1Lucas1apk 1Lucas1apk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You missed running npm run build.

@1Lucas1apk 1Lucas1apk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, looks good to me. I just left a few comments about some edge cases and consistency stuff that I think would be worth fixing before merging.

Comment thread src/api/index.ts
}

const onEnd = () => {
const onError = (error: Error) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there’s a small issue here.

In onError, we destroy req and call settle(false), but res is not closed. In the other cases (timeoutId, onData when the body exceeds the limit, and onEnd when the JSON is invalid), sendErrorResponse is used to close the response, but that doesn’t happen here.

So if the headers haven’t been sent yet, the request could end up hanging for the client.

Maybe we should check res.headersSent here and send a 500 before finishing:

if (!res.headersSent) {
  sendErrorResponse(
    req,
    res,
    500,
    'Internal Server Error',
    'Failed to read request body',
    parsedUrl.pathname,
    trace
  )
}

That way we don’t leave the response open if reading the body fails.

Comment thread src/api/index.ts Outdated
clearTimeout(timeoutId)
}
req.removeListener?.('data', onData)
req.removeListener?.('end', onEnd as unknown as (chunk: Buffer) => void)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could avoid these as unknown as (chunk: Buffer) => void casts here.

They seem to be needed because ApiRequest in api.types.ts:87-92 currently restricts the listener signature to (chunk: Buffer) => void.

Instead of having to force the type with casts every time, it might be cleaner to make the type definition more generic:

on?: (event: string, listener: (...args: any[]) => void) => void
removeListener?: (event: string, listener: (...args: any[]) => void) => void

That way we can remove all these as unknown as ... casts and keep the call sites cleaner.

Comment thread src/api/index.ts Outdated

req.on('data', onData)
req.on('end', onEnd)
req.on('end', onEnd as unknown as (chunk: Buffer) => void)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here on lines 600-601 — I think we can avoid these as unknown as ... casts by making the ApiRequest listener types more generic as mentioned above.

Comment thread src/sources/local.ts Outdated
@SreerajSK990
SreerajSK990 force-pushed the fix/core-stability-and-security branch from 2b0f208 to f61237d Compare September 13, 2026 16:29
@1Lucas1apk
1Lucas1apk merged commit 1fdf387 into PerformanC:dev Sep 13, 2026
1 check passed
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 13, 2026
@SreerajSK990
SreerajSK990 deleted the fix/core-stability-and-security branch September 14, 2026 04:15
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants