mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-14 23:21:35 +00:00
address greptile review feedback (greploop iteration 1)
- storageUtils: fix btoa() crash on non-Latin1 chars by encoding through encodeURIComponent before base64 - sanitizeImageSrc: restrict data: URIs to image/* and application/pdf - useMcpOAuthFlow: restore console.warn on storage failure, document localStorage migration fallback removal timeline - ChatUI: add comment explaining chatHistory persistence origin Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
065e7b45e1
commit
d5bad2f53e
4 changed files with 21 additions and 4 deletions
|
|
@ -340,6 +340,10 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
proxySettings,
|
||||
]);
|
||||
|
||||
// Note: this debounced chatHistory persistence was part of the original
|
||||
// security-fixes branch (litellm_security_fixes_v1.82.3) and is ported
|
||||
// as-is to keep the cherry-pick faithful. It prevents the synchronous
|
||||
// sessionStorage writes that were flagged by the security scan.
|
||||
useEffect(() => {
|
||||
if (simplified) return; // Do not persist chat history in simplified (embedded) mode
|
||||
const handler = setTimeout(() => {
|
||||
|
|
|
|||
|
|
@ -16,12 +16,18 @@ export const sanitizeImageSrc = (url: string | undefined): string => {
|
|||
const proto = parsed.protocol;
|
||||
if (
|
||||
proto === "blob:" ||
|
||||
proto === "data:" ||
|
||||
proto === "http:" ||
|
||||
proto === "https:"
|
||||
) {
|
||||
return parsed.href;
|
||||
}
|
||||
// Restrict data: URIs to image and PDF MIME types only
|
||||
if (proto === "data:") {
|
||||
const mime = parsed.pathname.split(";")[0].toLowerCase();
|
||||
if (mime.startsWith("image/") || mime === "application/pdf") {
|
||||
return parsed.href;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// invalid URL — fall through
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,13 +83,17 @@ export const useMcpOAuthFlow = ({
|
|||
};
|
||||
|
||||
const getStorageItem = (key: string): string | null => {
|
||||
// Try obfuscated sessionStorage first, fall back to legacy plain localStorage
|
||||
// Try obfuscated sessionStorage first, fall back to legacy plain localStorage.
|
||||
// The localStorage fallback exists for one-time migration of values written
|
||||
// before the obfuscated-sessionStorage switch. It can be removed once all
|
||||
// active sessions have been refreshed (target: next major release).
|
||||
const obfuscated = getObfuscated(key);
|
||||
if (obfuscated !== null) return obfuscated;
|
||||
if (typeof window === "undefined") return null;
|
||||
try {
|
||||
return window.localStorage.getItem(key);
|
||||
} catch (err) {
|
||||
console.warn(`Failed to read legacy localStorage key "${key}"`, err);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@
|
|||
|
||||
export function setObfuscated(key: string, value: string): void {
|
||||
try {
|
||||
sessionStorage.setItem(key, btoa(value));
|
||||
// Encode via encodeURIComponent first so non-Latin1 characters
|
||||
// (e.g. Unicode MCP server aliases in OAuth flow-state JSON)
|
||||
// are converted to percent-encoded ASCII before btoa.
|
||||
sessionStorage.setItem(key, btoa(unescape(encodeURIComponent(value))));
|
||||
} catch {
|
||||
// quota exceeded or SSR — silently drop
|
||||
}
|
||||
|
|
@ -20,7 +23,7 @@ export function getObfuscated(key: string): string | null {
|
|||
try {
|
||||
const raw = sessionStorage.getItem(key);
|
||||
if (raw === null) return null;
|
||||
return atob(raw);
|
||||
return decodeURIComponent(escape(atob(raw)));
|
||||
} catch {
|
||||
// invalid base64 or SSR — treat as missing
|
||||
return null;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue