From a56114650a1780cc1ccb4c206d272dbf73fb1a17 Mon Sep 17 00:00:00 2001 From: adlee-was-taken Date: Sun, 12 Apr 2026 12:22:54 -0400 Subject: [PATCH] feat: add settings, blacklist, and credential check handlers Co-Authored-By: Claude Opus 4.6 (1M context) --- extension/src/service-worker/index.ts | 113 +++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 1 deletion(-) diff --git a/extension/src/service-worker/index.ts b/extension/src/service-worker/index.ts index 39640c7..1d6b703 100644 --- a/extension/src/service-worker/index.ts +++ b/extension/src/service-worker/index.ts @@ -4,7 +4,8 @@ /// and routes all messages from the popup and content scripts. import type { Request, Response } from '../shared/messages'; -import type { Manifest, VaultConfig, SetupState } from '../shared/types'; +import type { Manifest, VaultConfig, SetupState, IdfotoSettings } from '../shared/types'; +import { DEFAULT_SETTINGS } from '../shared/types'; import type { GitHost } from './git-host'; import { createGitHost } from './git-host'; import { base64ToUint8Array } from './git-host'; @@ -69,6 +70,26 @@ async function loadSetupState(): Promise { }; } +// --- Settings & blacklist helpers --- + +async function loadSettings(): Promise { + const result = await chrome.storage.local.get('idfotoSettings'); + return (result.idfotoSettings as IdfotoSettings) ?? { ...DEFAULT_SETTINGS }; +} + +async function saveSettings(settings: IdfotoSettings): Promise { + await chrome.storage.local.set({ idfotoSettings: settings }); +} + +async function loadBlacklist(): Promise { + const result = await chrome.storage.local.get('captureBlacklist'); + return (result.captureBlacklist as string[]) ?? []; +} + +async function saveBlacklist(list: string[]): Promise { + await chrome.storage.local.set({ captureBlacklist: list }); +} + function ensureGitHost(config: VaultConfig): GitHost { if (!gitHost) { gitHost = createGitHost(config.hostType, config.hostUrl, config.repoPath, config.apiToken); @@ -310,6 +331,96 @@ async function handleMessage(req: Request): Promise { return { ok: true }; } + // --- Settings & blacklist --- + + case 'get_settings': { + const settings = await loadSettings(); + return { ok: true, data: { settings } }; + } + + case 'update_settings': { + const current = await loadSettings(); + const updated = { ...current, ...req.settings }; + await saveSettings(updated); + return { ok: true }; + } + + case 'get_blacklist': { + const blacklist = await loadBlacklist(); + return { ok: true, data: { blacklist } }; + } + + case 'remove_blacklist': { + const bl = await loadBlacklist(); + await saveBlacklist(bl.filter((h) => h !== req.hostname)); + return { ok: true }; + } + + case 'blacklist_site': { + const bl2 = await loadBlacklist(); + if (!bl2.includes(req.hostname)) { + bl2.push(req.hostname); + await saveBlacklist(bl2); + } + return { ok: true }; + } + + // --- Credential capture --- + + case 'check_credential': { + // Skip if vault locked + if (!masterKey || !gitHost || !manifest) { + return { ok: true, data: { action: 'skip' } }; + } + + // Skip if capture disabled + const captureSettings = await loadSettings(); + if (!captureSettings.captureEnabled) { + return { ok: true, data: { action: 'skip' } }; + } + + // Skip if hostname blacklisted + let checkHostname: string; + try { + checkHostname = new URL(req.url).hostname; + } catch { + return { ok: true, data: { action: 'skip' } }; + } + + const captureBlacklist = await loadBlacklist(); + if (captureBlacklist.includes(checkHostname)) { + return { ok: true, data: { action: 'skip' } }; + } + + // Search manifest by hostname + const candidates = vault.findByUrl(manifest, req.url); + + if (candidates.length === 0) { + return { ok: true, data: { action: 'save' } }; + } + + // Check for matching username + for (const [entryId, entry] of candidates) { + if (entry.username === req.username) { + // Same hostname + username — compare passwords + try { + const fullEntry = await vault.fetchAndDecryptEntry(gitHost, masterKey, entryId); + if (fullEntry.password === req.password) { + return { ok: true, data: { action: 'skip' } }; + } else { + return { ok: true, data: { action: 'update', entryId, entryName: entry.name } }; + } + } catch { + // If we can't decrypt, skip rather than error + return { ok: true, data: { action: 'skip' } }; + } + } + } + + // Same hostname, different username — new account + return { ok: true, data: { action: 'save' } }; + } + default: return { ok: false, error: `Unknown message type: ${(req as { type: string }).type}` }; }