diff --git a/src/content/injector.js b/src/content/injector.js index 7bf78c4..9269750 100644 --- a/src/content/injector.js +++ b/src/content/injector.js @@ -105,7 +105,7 @@ url: location.href, }); // Trim - if (log.length > 200) log.length = 200; + if (log.length > 100) log.length = 100; await api.storage.local.set({ ss_activity_log: log }); } } diff --git a/src/lib/storage.js b/src/lib/storage.js index 9a60b2d..61d11c3 100644 --- a/src/lib/storage.js +++ b/src/lib/storage.js @@ -38,7 +38,7 @@ const DEFAULT_SETTINGS = { autoDetect: true, autoRedactDetected: true, autoAddDetected: true, - maxLogEntries: 200, + maxLogEntries: 100, customDomains: [], categories: ['name', 'email', 'phone', 'address', 'ssn', 'dob', 'domain', 'password', 'general'], browserSync: false, diff --git a/src/lib/sync.js b/src/lib/sync.js index b6a6b4e..055ad04 100644 --- a/src/lib/sync.js +++ b/src/lib/sync.js @@ -12,8 +12,9 @@ * 5. Custom HTTP endpoint — any URL supporting GET + PUT (WebDAV, * self-hosted server, cloud function, etc.). * - * Encryption: all sync channels can optionally encrypt data with a - * password (AES-256-GCM) and/or require TOTP verification. + * Encryption: all sync channels REQUIRE encryption with a password + * (AES-256-GCM) and/or TOTP verification. Syncing without encryption + * is not permitted — users must set up encryption before enabling sync. * Authentication is cached with a configurable TTL so the user only * needs to authenticate when the cache expires and new data exists. * @@ -370,6 +371,9 @@ const SilentSendSync = { const StorageModule = (await import('./storage.js')).default; await StorageModule.decryptAllData(); + // Disable all sync channels since encryption is mandatory for sync + await StorageModule.saveSettings({ browserSync: false }); + await api.storage.local.remove('ss_sync_encryption'); await SilentSendCrypto.clearCachedKey(); await SilentSendCrypto.clearWebAuthnCredential(); @@ -415,7 +419,7 @@ const SilentSendSync = { */ async _encryptForSync(data) { const config = await this._getSyncEncryption(); - if (!config?.enabled) return { data, encrypted: false }; + if (!config?.enabled) return { data: null, encrypted: false, needsEncryption: true }; const keyInfo = await this._getEncryptionKey(); if (!keyInfo) { @@ -507,12 +511,15 @@ const SilentSendSync = { async exportSyncCode() { const data = await this._getAllData(); - // Encrypt if enabled + // Encrypt (mandatory) const result = await this._encryptForSync(data); + if (result.needsEncryption) { + return { needsEncryption: true }; + } if (result.needsAuth) { return { needsAuth: true }; } - const payload = result.data || data; + const payload = result.data; const json = JSON.stringify(payload); return btoa(unescape(encodeURIComponent(json))); @@ -568,10 +575,10 @@ const SilentSendSync = { try { const data = await this._getAllData(); - // Encrypt if enabled + // Encrypt (mandatory) const result = await this._encryptForSync(data); - if (result.needsAuth) return; // silently skip — will sync on next auth - const payload = result.data || data; + if (result.needsEncryption || result.needsAuth) return; // skip — encryption required + const payload = result.data; const json = JSON.stringify(payload); @@ -637,12 +644,15 @@ const SilentSendSync = { try { const data = await this._getAllData(); - // Encrypt if enabled + // Encrypt (mandatory) const encResult = await this._encryptForSync(data); + if (encResult.needsEncryption) { + return { success: false, needsEncryption: true, reason: 'Encryption must be enabled before syncing.' }; + } if (encResult.needsAuth) { return { success: false, needsAuth: true, reason: 'Authentication required.' }; } - const payload = encResult.data || data; + const payload = encResult.data; const content = JSON.stringify(payload, null, 2); const stored = await api.storage.local.get('ss_gist_id'); @@ -738,10 +748,13 @@ const SilentSendSync = { const data = await this._getAllData(); const encResult = await this._encryptForSync(data); + if (encResult.needsEncryption) { + return { success: false, needsEncryption: true, reason: 'Encryption must be enabled before syncing.' }; + } if (encResult.needsAuth) { return { success: false, needsAuth: true, reason: 'Authentication required.' }; } - const payload = encResult.data || data; + const payload = encResult.data; const resp = await fetch(url, { method, diff --git a/src/options/options.html b/src/options/options.html index a4291ff..6e78c3f 100644 --- a/src/options/options.html +++ b/src/options/options.html @@ -92,7 +92,7 @@

Number of activity log entries to keep

- + @@ -106,7 +106,7 @@ 🔒 Sync Encryption

- Encrypt your sync data with a password, TOTP, or both. Authentication is only required when new data arrives and your cached key has expired. + Encryption is required for all sync channels. Set up a password (and optionally TOTP) before enabling any sync method. Authentication is only required when new data arrives and your cached key has expired.

diff --git a/src/options/options.js b/src/options/options.js index 5f6bbbc..83e5b26 100644 --- a/src/options/options.js +++ b/src/options/options.js @@ -30,7 +30,7 @@ document.addEventListener('DOMContentLoaded', async () => { $('#autoDetect').checked = settings.autoDetect !== false; $('#autoRedactDetected').checked = settings.autoRedactDetected !== false; $('#autoAddDetected').checked = settings.autoAddDetected !== false; - $('#maxLogEntries').value = settings.maxLogEntries || 200; + $('#maxLogEntries').value = settings.maxLogEntries || 100; $('#browserSync').checked = settings.browserSync === true; renderMappings(); @@ -51,17 +51,28 @@ document.addEventListener('DOMContentLoaded', async () => { // --- Sync section --- $('#browserSync').addEventListener('change', async (e) => { - await Storage.saveSettings({ browserSync: e.target.checked }); if (e.target.checked) { + const encEnabled = await SilentSendSync.isEncryptionEnabled(); + if (!encEnabled) { + e.target.checked = false; + setSyncStatus('Encryption must be enabled before syncing. Set up encryption first.', 'error'); + return; + } + await Storage.saveSettings({ browserSync: true }); await SilentSendSync.pushToSyncStorage(); setSyncStatus('Browser account sync enabled. Your settings will sync automatically.', 'ok'); } else { + await Storage.saveSettings({ browserSync: false }); setSyncStatus('Browser account sync disabled.', 'neutral'); } }); $('#btnGenerateSyncCode').addEventListener('click', async () => { const code = await SilentSendSync.exportSyncCode(); + if (code?.needsEncryption) { + setSyncStatus('Encryption must be enabled before syncing. Set up encryption first.', 'error'); + return; + } if (code?.needsAuth) { setSyncStatus('Authentication required to encrypt sync code.', 'warn'); showSyncAuthPrompt(); @@ -168,7 +179,9 @@ document.addEventListener('DOMContentLoaded', async () => { if (!token) { setGistSyncStatus('Enter your GitHub PAT first.', 'warn'); return; } setGistSyncStatus('Pushing…', 'neutral'); const r = await SilentSendSync.pushToGist(token); - if (r.needsAuth) { + if (r.needsEncryption) { + setGistSyncStatus('Encryption must be enabled before syncing.', 'error'); + } else if (r.needsAuth) { setGistSyncStatus('Authentication required to encrypt.', 'warn'); showSyncAuthPrompt(); } else if (r.success) { @@ -207,7 +220,9 @@ document.addEventListener('DOMContentLoaded', async () => { const headers = parseHeadersField($('#customSyncHeaders').value); setUrlSyncStatus('Pushing…', 'neutral'); const r = await SilentSendSync.pushToUrl({ url, headers }); - if (r.needsAuth) { + if (r.needsEncryption) { + setUrlSyncStatus('Encryption must be enabled before syncing.', 'error'); + } else if (r.needsAuth) { setUrlSyncStatus('Authentication required to encrypt.', 'warn'); showSyncAuthPrompt(); } else if (r.success) { @@ -282,7 +297,7 @@ document.addEventListener('DOMContentLoaded', async () => { }); $('#maxLogEntries').addEventListener('change', async (e) => { - await Storage.saveSettings({ maxLogEntries: parseInt(e.target.value, 10) || 200 }); + await Storage.saveSettings({ maxLogEntries: parseInt(e.target.value, 10) || 100 }); }); // Add mapping @@ -959,8 +974,9 @@ async function initSyncEncryptionUI() { $('#btnDisableEncryption').addEventListener('click', async () => { if (!window.confirm('Disable sync encryption? Existing encrypted sync data will become unreadable.')) return; await SilentSendSync.disableEncryption(); + $('#browserSync').checked = false; showEncryptionNotConfigured(); - setSyncEncStatus('Encryption disabled.', 'neutral'); + setSyncEncStatus('Encryption disabled. All sync channels have been turned off.', 'neutral'); }); // Change password