From 18022c6b68eec21985875ec98810da00e1bb79db Mon Sep 17 00:00:00 2001 From: lockbitchat Date: Sun, 17 May 2026 22:58:21 -0400 Subject: [PATCH] fix: gate debug window hooks behind explicit flag --- package.json | 2 +- src/app.jsx | 36 +++++---------- src/utils/debugWindowHooks.js | 35 +++++++++++++++ tests/debug-window-hooks.test.mjs | 75 +++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 26 deletions(-) create mode 100644 src/utils/debugWindowHooks.js create mode 100644 tests/debug-window-hooks.test.mjs diff --git a/package.json b/package.json index f91e7a7..72dbc23 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "dev": "npm run build && python -m http.server 8000", "watch": "npx tailwindcss -i src/styles/tw-input.css -o assets/tailwind.css --watch", "serve": "npx http-server -p 8000", - "test": "node tests/sas-verification.test.mjs && node tests/file-transfer-consent.test.mjs && node tests/incoming-message-sanitization.test.mjs && node tests/file-type-allowlist.test.mjs && node tests/webrtc-privacy-mode.test.mjs && node tests/indexeddb-metadata-encryption.test.mjs && node tests/disconnect-cleanup.test.mjs && node tests/timer-lifecycle.test.mjs && node tests/file-transfer-cleanup.test.mjs && node tests/file-transfer-ui-cleanup.test.mjs && node tests/file-transfer-callback-propagation.test.mjs" + "test": "node tests/sas-verification.test.mjs && node tests/file-transfer-consent.test.mjs && node tests/incoming-message-sanitization.test.mjs && node tests/file-type-allowlist.test.mjs && node tests/webrtc-privacy-mode.test.mjs && node tests/indexeddb-metadata-encryption.test.mjs && node tests/disconnect-cleanup.test.mjs && node tests/timer-lifecycle.test.mjs && node tests/file-transfer-cleanup.test.mjs && node tests/file-transfer-ui-cleanup.test.mjs && node tests/file-transfer-callback-propagation.test.mjs && node tests/debug-window-hooks.test.mjs" }, "keywords": [ "p2p", diff --git a/src/app.jsx b/src/app.jsx index 934c596..e4850c4 100644 --- a/src/app.jsx +++ b/src/app.jsx @@ -1,4 +1,4 @@ - +import { installDebugWindowHooks } from './utils/debugWindowHooks.js'; // Enhanced Copy Button with better UX const EnhancedCopyButton = ({ text, className = "", children }) => { const [copied, setCopied] = React.useState(false); @@ -1639,32 +1639,18 @@ }); }; - // Global functions for cleanup - React.useEffect(() => { - window.forceCleanup = () => { - handleClearData(); - if (webrtcManagerRef.current) { - webrtcManagerRef.current.disconnect(); - } - }; - - window.clearLogs = () => { - if (typeof console.clear === 'function') { - console.clear(); - } - }; - - return () => { - delete window.forceCleanup; - delete window.clearLogs; - }; - }, []); - const webrtcManagerRef = React.useRef(null); const notificationIntegrationRef = React.useRef(null); - // Expose for modules/UI that run outside this closure (e.g., inline handlers) - // Safe because it's a ref object and we maintain it centrally here - window.webrtcManagerRef = webrtcManagerRef; + + // Development-only debug helpers. Production never exposes + // manager internals or cleanup controls on `window`. + React.useEffect(() => { + return installDebugWindowHooks({ + targetWindow: window, + webrtcManagerRef, + onClearData: handleClearData + }); + }, []); const addMessageWithAutoScroll = React.useCallback((message, type) => { const newMessage = { diff --git a/src/utils/debugWindowHooks.js b/src/utils/debugWindowHooks.js new file mode 100644 index 0000000..cb37399 --- /dev/null +++ b/src/utils/debugWindowHooks.js @@ -0,0 +1,35 @@ +function isSecureBitDebugEnabled(targetWindow = globalThis.window) { + return targetWindow?.SECUREBIT_DEBUG === true; +} + +function installDebugWindowHooks({ + targetWindow = globalThis.window, + webrtcManagerRef, + onClearData, + clearConsole = () => { + if (typeof console.clear === 'function') { + console.clear(); + } + } +}) { + if (!isSecureBitDebugEnabled(targetWindow)) { + return () => {}; + } + + targetWindow.forceCleanup = () => { + onClearData(); + if (webrtcManagerRef.current) { + webrtcManagerRef.current.disconnect(); + } + }; + targetWindow.clearLogs = clearConsole; + targetWindow.webrtcManagerRef = webrtcManagerRef; + + return () => { + delete targetWindow.forceCleanup; + delete targetWindow.clearLogs; + delete targetWindow.webrtcManagerRef; + }; +} + +export { installDebugWindowHooks, isSecureBitDebugEnabled }; diff --git a/tests/debug-window-hooks.test.mjs b/tests/debug-window-hooks.test.mjs new file mode 100644 index 0000000..636f022 --- /dev/null +++ b/tests/debug-window-hooks.test.mjs @@ -0,0 +1,75 @@ +import assert from 'node:assert/strict'; + +const { installDebugWindowHooks, isSecureBitDebugEnabled } = await import('../src/utils/debugWindowHooks.js'); + +// Production mode does not expose debug/control globals. +{ + const targetWindow = {}; + const managerRef = { current: { disconnect() {} } }; + const cleanup = installDebugWindowHooks({ + targetWindow, + webrtcManagerRef: managerRef, + onClearData() {} + }); + + assert.equal(isSecureBitDebugEnabled(targetWindow), false); + assert.equal('forceCleanup' in targetWindow, false); + assert.equal('clearLogs' in targetWindow, false); + assert.equal('webrtcManagerRef' in targetWindow, false); + cleanup(); +} + +// Debug mode exposes hooks only when explicitly requested. +{ + let clearDataCalls = 0; + let disconnectCalls = 0; + let clearLogCalls = 0; + const targetWindow = { SECUREBIT_DEBUG: true }; + const managerRef = { + current: { + disconnect() { + disconnectCalls += 1; + } + } + }; + const cleanup = installDebugWindowHooks({ + targetWindow, + webrtcManagerRef: managerRef, + onClearData() { + clearDataCalls += 1; + }, + clearConsole() { + clearLogCalls += 1; + } + }); + + assert.equal(isSecureBitDebugEnabled(targetWindow), true); + assert.equal(targetWindow.webrtcManagerRef, managerRef); + targetWindow.forceCleanup(); + targetWindow.clearLogs(); + assert.equal(clearDataCalls, 1); + assert.equal(disconnectCalls, 1); + assert.equal(clearLogCalls, 1); + + cleanup(); + assert.equal('forceCleanup' in targetWindow, false); + assert.equal('clearLogs' in targetWindow, false); + assert.equal('webrtcManagerRef' in targetWindow, false); +} + +// Normal cleanup remains available through the app-owned callback path. +{ + let clearDataCalls = 0; + const onClearData = () => { + clearDataCalls += 1; + }; + installDebugWindowHooks({ + targetWindow: {}, + webrtcManagerRef: { current: null }, + onClearData + }); + onClearData(); + assert.equal(clearDataCalls, 1); +} + +console.log('Debug window hook tests passed');