mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-11-03 04:38:32 +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. |
||
|---|---|---|
| .. | ||
| __tests__ | ||
| accessResources | ||
| assistants | ||
| config | ||
| limiters | ||
| roles | ||
| spec | ||
| validate | ||
| abortMiddleware.js | ||
| abortMiddleware.spec.js | ||
| abortRun.js | ||
| buildEndpointOption.js | ||
| buildEndpointOption.spec.js | ||
| canAccessSharedLink.js | ||
| canDeleteAccount.js | ||
| canDeleteAccount.spec.js | ||
| checkBan.js | ||
| checkDomainAllowed.js | ||
| checkInviteUser.js | ||
| checkPeoplePickerAccess.js | ||
| checkPeoplePickerAccess.spec.js | ||
| checkSharePublicAccess.js | ||
| checkSharePublicAccess.spec.js | ||
| denyRequest.js | ||
| error.js | ||
| index.js | ||
| logHeaders.js | ||
| messageValidation.js | ||
| moderateText.js | ||
| moderateText.spec.js | ||
| noIndex.js | ||
| oauthNavigation.js | ||
| optionalJwtAuth.js | ||
| optionalShareFileAuth.js | ||
| optionalShareFileAuth.spec.js | ||
| requireJwtAuth.js | ||
| requireLdapAuth.js | ||
| requireLocalAuth.js | ||
| setHeaders.js | ||
| setTwoFactorTempUser.js | ||
| uaParser.js | ||
| validateEmailLogin.js | ||
| validateImageRequest.js | ||
| validateMessageReq.js | ||
| validateModel.js | ||
| validatePasswordReset.js | ||
| validateRegistration.js | ||