Overview
Deep audit (2026-03-27) identified 6 new medium-severity security findings not covered by existing issues.
Finding 1: Google OAuth Response URL Logged with Tokens on Failure
File: src/services/AuthService.ts, line 126
When Google OAuth fails to return an id_token, the entire responseUrl is logged:
console.error('No id_token found in response', responseUrl);
This URL may contain access_token, state, and other sensitive OAuth parameters. Distinct from #47/#20 which cover other logging issues.
Fix: Log only the error condition: console.error('No id_token found in Google auth response');
Finding 2: No Client-Side Rate Limiting on Login/Signup
File: src/popup/AuthForm.tsx, lines 21-41
handleSubmit sends login/signup requests with no attempt counting, throttling, or backoff. Enables automated brute-force against the API.
Fix: Implement attempt counter with exponential backoff (3 failures -> 5s wait, 5 failures -> 30s lockout).
Finding 3: Content Script innerHTML with DOM Clobbering Risk
File: src/content/selection-overlay.ts, lines 56-81
Content script injects UI into host page DOM via innerHTML. Elements appended to document.body are accessible to host page JS, enabling DOM clobbering and future XSS regression if dynamic data is interpolated.
Fix: Use Shadow DOM for content script UI elements; replace innerHTML with createElement/textContent.
Finding 4: enableLogging: true Hardcoded with No Production Controls
File: src/config/environment.ts, line 15
export const config: EnvironmentConfig = {
apiUrl: 'https://api.numbersprotocol.io/api/v3',
enableLogging: true, // Always on, no build-time or runtime toggle
timeout: 60000,
};
The codebase has ~80+ console.log calls that are always active. No mechanism exists to suppress logging in production. This is the architectural gap beyond #20's specific verbose-log instances.
Fix: Use Vite's define to strip logging in production builds. Replace direct console.log with a logging utility respecting the config flag.
Finding 5: Offscreen Document Message Handler Lacks Sender Validation
File: src/offscreen/offscreen.ts, lines 29-48
The offscreen onMessage handler processes ADD_WATERMARK, CROP_IMAGE, and GET_GEOLOCATION without validating sender. Distinct from #19 which covers the service worker handler.
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
if (message.type === 'ADD_WATERMARK') { ... }
});
Fix: Validate sender.id === chrome.runtime.id before processing.
Finding 6: No Size Validation on Screenshot Data URLs
Files: src/background/service-worker.ts, lines 428-434; src/services/UploadService.ts, lines 441-443
Screenshot data URLs (10-50+ MB on hi-DPI) pass through capture -> fetch -> ImageBitmap -> message passing -> IndexedDB -> upload with zero size validation. Can cause OOM crashes and storage exhaustion.
Fix: Add max data URL size check (e.g., 25MB) immediately after captureVisibleTab. Add try-catch around createImageBitmap chain.
Generated by Heart Beat with Omni
Overview
Deep audit (2026-03-27) identified 6 new medium-severity security findings not covered by existing issues.
Finding 1: Google OAuth Response URL Logged with Tokens on Failure
File:
src/services/AuthService.ts, line 126When Google OAuth fails to return an
id_token, the entireresponseUrlis logged:This URL may contain
access_token,state, and other sensitive OAuth parameters. Distinct from #47/#20 which cover other logging issues.Fix: Log only the error condition:
console.error('No id_token found in Google auth response');Finding 2: No Client-Side Rate Limiting on Login/Signup
File:
src/popup/AuthForm.tsx, lines 21-41handleSubmitsends login/signup requests with no attempt counting, throttling, or backoff. Enables automated brute-force against the API.Fix: Implement attempt counter with exponential backoff (3 failures -> 5s wait, 5 failures -> 30s lockout).
Finding 3: Content Script
innerHTMLwith DOM Clobbering RiskFile:
src/content/selection-overlay.ts, lines 56-81Content script injects UI into host page DOM via
innerHTML. Elements appended todocument.bodyare accessible to host page JS, enabling DOM clobbering and future XSS regression if dynamic data is interpolated.Fix: Use Shadow DOM for content script UI elements; replace
innerHTMLwithcreateElement/textContent.Finding 4:
enableLogging: trueHardcoded with No Production ControlsFile:
src/config/environment.ts, line 15The codebase has ~80+
console.logcalls that are always active. No mechanism exists to suppress logging in production. This is the architectural gap beyond #20's specific verbose-log instances.Fix: Use Vite's
defineto strip logging in production builds. Replace directconsole.logwith a logging utility respecting the config flag.Finding 5: Offscreen Document Message Handler Lacks Sender Validation
File:
src/offscreen/offscreen.ts, lines 29-48The offscreen
onMessagehandler processesADD_WATERMARK,CROP_IMAGE, andGET_GEOLOCATIONwithout validatingsender. Distinct from #19 which covers the service worker handler.Fix: Validate
sender.id === chrome.runtime.idbefore processing.Finding 6: No Size Validation on Screenshot Data URLs
Files:
src/background/service-worker.ts, lines 428-434;src/services/UploadService.ts, lines 441-443Screenshot data URLs (10-50+ MB on hi-DPI) pass through capture -> fetch -> ImageBitmap -> message passing -> IndexedDB -> upload with zero size validation. Can cause OOM crashes and storage exhaustion.
Fix: Add max data URL size check (e.g., 25MB) immediately after
captureVisibleTab. Add try-catch aroundcreateImageBitmapchain.Generated by Heart Beat with Omni