From 96fe6fb2e3a424a88da000725b31a2cc3ab0eb8e Mon Sep 17 00:00:00 2001 From: Harshidaa Date: Thu, 18 Jun 2026 09:57:33 +0530 Subject: [PATCH 1/2] Add API key sanitization and paste handling --- frontend/src/components/ApiKeySetupModal.tsx | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/frontend/src/components/ApiKeySetupModal.tsx b/frontend/src/components/ApiKeySetupModal.tsx index 60ab4c105..3d9bd04ed 100644 --- a/frontend/src/components/ApiKeySetupModal.tsx +++ b/frontend/src/components/ApiKeySetupModal.tsx @@ -18,6 +18,26 @@ export default function ApiKeySetupModal({ onSaved }: Props) { const [visible, setVisible] = useState(false) const [error, setError] = useState('') + // NEW: Sanitize pasted API keys + const sanitizeApiKey = (value: string): string => { + return value + .trim() // Remove leading/trailing whitespace + .replace(/\n/g, '') // Remove newlines + .replace(/\r/g, '') // Remove carriage returns + .replace(/\t/g, '') // Remove tabs + .replace(/\u00A0/g, ' ') // Replace non-breaking spaces with regular space + .trim(); // Final trim after replacements + } + + // NEW: Handle paste events + const handlePaste = (event: React.ClipboardEvent) => { + event.preventDefault() + const pastedText = event.clipboardData.getData('text') + const sanitizedText = sanitizeApiKey(pastedText) + setKey(sanitizedText) + setError('') + } + function handleSave() { const trimmed = key.trim() if (!trimmed) { @@ -78,6 +98,7 @@ export default function ApiKeySetupModal({ onSaved }: Props) { type="password" value={key} onChange={(e) => { setKey(e.target.value); setError('') }} + onPaste={handlePaste} // NEW: Added paste handler onKeyDown={(e) => e.key === 'Enter' && handleSave()} placeholder="Paste API key here" aria-label="Backend API Key" From 74fbee49a3b91791b7d4eca0e0a552f688239ae9 Mon Sep 17 00:00:00 2001 From: Harshidaa Date: Thu, 18 Jun 2026 10:53:32 +0530 Subject: [PATCH 2/2] Add pagination page size persistence in Scans component --- frontend/src/pages/Scans.tsx | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/frontend/src/pages/Scans.tsx b/frontend/src/pages/Scans.tsx index 143319a70..71da8efd4 100644 --- a/frontend/src/pages/Scans.tsx +++ b/frontend/src/pages/Scans.tsx @@ -65,7 +65,13 @@ export default function Scans() { const [selectedIds, setSelectedIds] = useState([]); const [page, setPage] = useState(1); const [total, setTotal] = useState(0); - const PAGE_LIMIT = 10; + + // Page size with localStorage persistence + const getSavedPageSize = () => { + const saved = localStorage.getItem('pagination_pageSize_scans'); + return saved ? parseInt(saved, 10) : 10; + }; + const [pageLimit, setPageLimit] = useState(getSavedPageSize); // Modal state for confirm dialogs const [modalState, setModalState] = useState<{ @@ -124,7 +130,7 @@ export default function Scans() { } document.removeEventListener("visibilitychange", handleVisibilityChange); }; - }, [filter, page]); + }, [filter, page, pageLimit]); async function loadTasks() { const requestSeq = requestSeqRef.current + 1; @@ -137,7 +143,7 @@ export default function Scans() { const params = new URLSearchParams(); if (filter !== "all") params.set("status", filter); params.set("page", String(page)); - params.set("per_page", String(PAGE_LIMIT)); + params.set("per_page", String(pageLimit)); const res = await fetch(`${API_BASE}/tasks?${params.toString()}`, { signal: controller.signal, @@ -675,14 +681,18 @@ export default function Scans() { )} - {total > PAGE_LIMIT && ( + {total > pageLimit && ( setPage((p) => p - 1)} onNext={() => setPage((p) => p + 1)} + onPageSizeChange={(newSize) => { + setPageLimit(newSize); + setPage(1); + }} /> )}