mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-10-21 09:32:24 +00:00
* 🪵 fix: Aggregate Audit Findings and Rate Limit Shared Link Reads Audit-mode content filter rules wrote one `logger.info` per matching fragment, and inspection deliberately continues past every finding. A shared conversation full of distinct matching strings therefore produced one log write per fragment, regenerated on every retrieval of `GET /api/share/:shareId` — a public, unauthenticated route with no limiter. The 4,096-entry inspection dedupe set is a memory bound, not an event budget: once full, further values are inspected and logged again. Audit metadata carries no inspected text — only rule, source, field and provenance — so thousands of those writes were byte-identical lines. Findings are now counted inside an aggregation scope and reported once per distinct key with an `occurrences` count when the scope ends: - `assertModelBoundContent` opens a scope when audit rules are configured, bounding one inspection pass (a single message's fragments). - `assertConversationImportContentAllowed` opens the outer scope for import and shared-link snapshots, so per-message inspections nest into one report instead of one per message. - `createShareContentPreflight` spans the shared-file metadata pass too. Nested scopes reuse the outermost aggregation, and outside any scope the previous immediate write is kept. `GET /api/share/:shareId` re-inspects the whole snapshot on every request, so it now carries IP and user limiters like the neighbouring fork route (`SHARE_IP_MAX`/`SHARE_IP_WINDOW`, `SHARE_USER_MAX`/`SHARE_USER_WINDOW`, `SHARE_VIOLATION_SCORE`); the user limiter skips anonymous viewers. * 🔒 fix: Address Codex Findings on Share Audit Aggregation - The aggregation key joined rule fields with a space, but the filter schema allows spaces in custom pattern ids and labels, so `{id: 'a', label: 'b c'}` and `{id: 'a b', label: 'c'}` collided on the same source/field/provenance and merged two rules' counts under one identity. Serialize the tuple instead. - `SHARE_VIOLATION_SCORE` left unset reached `logViolation` as `undefined`, which selects that function's default score of 1 rather than the documented zero. With default ban settings a viewer refreshing a rate-limited shared link would be banned after 20 rejections. Default the score to 0 in code, not only in `.env.example`. - `removePorts` returns a full IPv6 address, so an attacker holding a prefix could rotate the host portion for a fresh bucket on every request — and for anonymous viewers the IP limiter is the only bound. Derive the key through `ipKeyGenerator` so IPv6 clients group by /56. |
||
|---|---|---|
| .. | ||
| code.js | ||
| code.spec.js | ||
| forkLimiters.js | ||
| importLimiters.js | ||
| index.js | ||
| loginLimiter.js | ||
| loginLimiter.spec.js | ||
| messageLimiters.js | ||
| messageLimiters.spec.js | ||
| promptUsageLimiter.js | ||
| registerLimiter.js | ||
| resetPasswordLimiter.js | ||
| resetPasswordSubmissionLimiter.js | ||
| shareLimiters.js | ||
| shareLimiters.spec.js | ||
| sttLimiters.js | ||
| toolCallLimiter.js | ||
| ttsLimiters.js | ||
| twoFactorTempLimiter.js | ||
| twoFactorTempLimiter.test.js | ||
| uploadLimiters.js | ||
| verifyEmailLimiter.js | ||
| verifyEmailSubmissionLimiter.js | ||