From 7cb0ff16080ad795cc996cc0e93fa1c6558b04b8 Mon Sep 17 00:00:00 2001 From: SmartUp Developer Date: Mon, 18 May 2026 14:25:56 +0800 Subject: [PATCH] fix: object URL leak, CDP before goto, limit raw secrets in extract - AuthCaptureDialog: revokeObjectURL on each frame to prevent memory leak - CDP Network capture starts before initial page.goto, not after - /extract defaults to candidates-only; pass ?include_raw=true for full data --- backend/app/routers/auth_capture.py | 10 +++++++--- backend/app/services/browser_session_service.py | 5 +++-- frontend/src/components/AuthCaptureDialog.vue | 9 +++++---- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/backend/app/routers/auth_capture.py b/backend/app/routers/auth_capture.py index 1b2b73b..d606bfa 100644 --- a/backend/app/routers/auth_capture.py +++ b/backend/app/routers/auth_capture.py @@ -89,12 +89,13 @@ async def create_capture_session( @router.get("/sessions/{session_id}/extract", response_model=CaptureExtractResponse) async def extract_credentials( session_id: str, + include_raw: bool = Query(default=False, description="Include full cookies/storage/headers in response"), _=Depends(get_current_user), ): - """Extract all auth credentials from the browser session. + """Extract auth credentials from the browser session. - Returns cookies, localStorage, sessionStorage, and curated candidates. - Candidate values are masked in logs. + By default only returns curated candidates (typed, scored, with masked preview). + Pass include_raw=true to also get full cookies, localStorage, and headers. """ try: session = browser_sessions.get_session(session_id) @@ -106,6 +107,9 @@ async def extract_credentials( except Exception as exc: raise _browser_error(exc) + if not include_raw: + # Strip raw data — only keep curated candidates + return CaptureExtractResponse(candidates=result.get("candidates", [])) return CaptureExtractResponse(**result) diff --git a/backend/app/services/browser_session_service.py b/backend/app/services/browser_session_service.py index 6c06d26..4d31b53 100644 --- a/backend/app/services/browser_session_service.py +++ b/backend/app/services/browser_session_service.py @@ -387,10 +387,11 @@ class BrowserSessionService: captured_headers=[], ) self._sessions[session.id] = session + # Start CDP network capture BEFORE the initial page load, + # so we capture login redirects and auth headers from the start. + await self._start_cdp_capture(session) try: await page.goto(url, wait_until="domcontentloaded", timeout=45000) - # Start CDP network capture immediately — so we don't miss login requests - await self._start_cdp_capture(session) except Exception: await self.close(session.id) raise diff --git a/frontend/src/components/AuthCaptureDialog.vue b/frontend/src/components/AuthCaptureDialog.vue index 59c1cb3..11ca9a6 100644 --- a/frontend/src/components/AuthCaptureDialog.vue +++ b/frontend/src/components/AuthCaptureDialog.vue @@ -180,6 +180,7 @@ const frameRef = ref(null) let ws: WebSocket | null = null let pointerDown = false let frameW = 1; let frameH = 1 // natural dimensions of the frame +let prevFrameUrl = '' // previous blob URL to revoke // ——— Launch ——— @@ -212,11 +213,11 @@ function connectWs() { ws.onmessage = (evt) => { if (evt.data instanceof ArrayBuffer) { - // Binary JPEG frame + // Binary JPEG frame — revoke previous to avoid memory leak + if (prevFrameUrl) URL.revokeObjectURL(prevFrameUrl) const blob = new Blob([evt.data], { type: 'image/jpeg' }) - frameUrl.value = URL.createObjectURL(blob) - // Revoke previous URL after a tick to free memory - setTimeout(() => { /* old URL auto-revoked */ }, 100) + prevFrameUrl = URL.createObjectURL(blob) + frameUrl.value = prevFrameUrl } else { // JSON message (init, error, etc.) try {