From 909bb3a8476061137f18d15d66b491188bbc3a0e Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 4 Apr 2026 14:14:15 +0000 Subject: [PATCH] Fix PII mapping add (plus button) not persisting and not dismissing notification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The plus button handler in the pre-send PII warning had two bugs: 1. It read/wrote mappings directly via the storage bridge (api.storage.local), bypassing the Storage module's encryption layer. When at-rest encryption was enabled, getStorageData returned an encrypted blob instead of an array, causing .push() to throw a TypeError that silently aborted the handler — the mapping was never saved and replaceInInput never ran. 2. Unlike the ignore button which immediately removes the DOM item, the plus button relied on a re-scan at 150ms to dismiss the notification. If replaceInInput didn't stick (e.g. React-controlled inputs), the re-scan found PII again and the notification persisted. Fix: Route mapping creation through the background service worker via a new 'add:mapping' message handler (which uses Storage.addMapping with proper encryption support), and immediately dismiss the notification item from the DOM like the ignore button does. https://claude.ai/code/session_01RfzvB5sHah326acr8Xa7Jn --- src/background/service-worker.js | 6 +++++ src/content/content.js | 44 ++++++++++++++++++-------------- src/content/injector.js | 20 +++++++++++++++ 3 files changed, 51 insertions(+), 19 deletions(-) diff --git a/src/background/service-worker.js b/src/background/service-worker.js index f0f35a0..ac24c0e 100644 --- a/src/background/service-worker.js +++ b/src/background/service-worker.js @@ -138,6 +138,12 @@ const messageHandlers = { sendResponse({ mappings }); }, + async 'add:mapping'(message, _sender, sendResponse) { + const newMapping = await Storage.addMapping(message.mapping); + const mappings = await Storage.getMappings(); + sendResponse({ mapping: newMapping, mappings }); + }, + async 'get:settings'(_message, _sender, sendResponse) { const settings = await Storage.getSettings(); sendResponse({ settings }); diff --git a/src/content/content.js b/src/content/content.js index 0702685..0fae146 100644 --- a/src/content/content.js +++ b/src/content/content.js @@ -1406,21 +1406,17 @@ const fake = decodeURIComponent(btn.dataset.fake); const cat = btn.dataset.cat || 'general'; - // Add to mappings via storage - const result = await getStorageData('ss_mappings'); - const currentMappings = result || []; - currentMappings.push({ - id: crypto.randomUUID(), - real, substitute: fake, - category: cat, - caseSensitive: false, - enabled: true, - createdAt: Date.now(), - }); - await setStorageData('ss_mappings', currentMappings); + // Immediately dismiss this notification item (same as ignore) + btn.closest('.ss-ps-item').remove(); + if (!preSendWarningEl.querySelector('.ss-ps-item')) { + preSendWarningEl.classList.remove('visible'); + } - // Update local mappings so the fetch interceptor uses them immediately - mappings = currentMappings; + // Add to mappings via background script (handles encryption) + addMappingViaBackground({ real, substitute: fake, category: cat }) + .then(updatedMappings => { + if (updatedMappings.length) mappings = updatedMappings; + }); // Replace the PII value in the current input right now if (inputEl) { @@ -1429,11 +1425,6 @@ if (inputScanTimer) clearTimeout(inputScanTimer); inputScanTimer = setTimeout(() => scanInputForPII(inputEl), 150); } - - // Visual feedback - btn.textContent = '\u2714'; - btn.style.color = '#4ade80'; - btn.disabled = true; }); }); @@ -1493,6 +1484,21 @@ window.postMessage({ type: 'ss:storage-set', key, value }, '*'); } + function addMappingViaBackground(mapping) { + return new Promise(resolve => { + const id = 'ss-add-' + Math.random(); + const handler = (event) => { + if (event.data?.type === 'ss:add-mapping-result' && event.data.id === id) { + window.removeEventListener('message', handler); + resolve(event.data.mappings || []); + } + }; + window.addEventListener('message', handler); + window.postMessage({ type: 'ss:add-mapping', mapping, id }, '*'); + setTimeout(() => { window.removeEventListener('message', handler); resolve([]); }, 2000); + }); + } + // Scan input on type and paste let inputScanTimer = null; diff --git a/src/content/injector.js b/src/content/injector.js index fbc526f..e609d65 100644 --- a/src/content/injector.js +++ b/src/content/injector.js @@ -193,6 +193,26 @@ if (event.data?.type === 'ss:storage-set') { await api.storage.local.set({ [event.data.key]: event.data.value }); } + + if (event.data?.type === 'ss:add-mapping') { + try { + const response = await api.runtime.sendMessage({ + type: 'add:mapping', + mapping: event.data.mapping, + }); + window.postMessage({ + type: 'ss:add-mapping-result', + id: event.data.id, + mappings: response?.mappings || [], + }, '*'); + } catch { + window.postMessage({ + type: 'ss:add-mapping-result', + id: event.data.id, + mappings: [], + }, '*'); + } + } }); }