Adds a secret scanning layer that runs after identity/explicit substitutions. Catches secrets the user didn't configure: - API keys: OpenAI (sk-), Anthropic (sk-ant-), Google (AIza), AWS (AKIA), GitHub (ghp_), GitLab (glpat-), Slack (xox), Stripe (sk_live/test), SendGrid (SG.) - Auth: Bearer tokens, key=value assignments (password=, secret=, api_key=, token=), private key blocks (-----BEGIN PRIVATE KEY-----) - Connection strings: mongodb://, postgres://, mysql:// with creds - PII: SSN (xxx-xx-xxxx), credit card numbers (Visa/MC/Amex/Discover) Redacted values shown in red in the Test tab. Secrets are truncated in the activity log (first 8 chars + "...") to avoid logging the full secret. Enabled by default, toggle in Options. https://claude.ai/code/session_01Dvgwe7XMoSxnWXkih8p1Cw
138 lines
3.3 KiB
JavaScript
138 lines
3.3 KiB
JavaScript
/**
|
|
* Silent Send - Storage Manager
|
|
*
|
|
* Wraps browser/api.storage.local with typed helpers for mappings,
|
|
* activity log, and settings.
|
|
*/
|
|
|
|
import api from './browser-polyfill.js';
|
|
|
|
const KEYS = {
|
|
MAPPINGS: 'ss_mappings',
|
|
IDENTITY: 'ss_identity',
|
|
LOG: 'ss_activity_log',
|
|
SETTINGS: 'ss_settings',
|
|
};
|
|
|
|
const DEFAULT_SETTINGS = {
|
|
enabled: true,
|
|
showHighlights: false,
|
|
revealMode: false,
|
|
secretScanning: true,
|
|
maxLogEntries: 200,
|
|
customDomains: [],
|
|
categories: ['name', 'email', 'phone', 'address', 'ssn', 'dob', 'general'],
|
|
};
|
|
|
|
const Storage = {
|
|
// --- Mappings ---
|
|
|
|
async getMappings() {
|
|
const result = await api.storage.local.get(KEYS.MAPPINGS);
|
|
return result[KEYS.MAPPINGS] || [];
|
|
},
|
|
|
|
async saveMappings(mappings) {
|
|
await api.storage.local.set({ [KEYS.MAPPINGS]: mappings });
|
|
},
|
|
|
|
async addMapping(mapping) {
|
|
const mappings = await this.getMappings();
|
|
const newMapping = {
|
|
id: crypto.randomUUID(),
|
|
real: mapping.real || '',
|
|
substitute: mapping.substitute || '',
|
|
category: mapping.category || 'general',
|
|
caseSensitive: mapping.caseSensitive ?? false,
|
|
enabled: mapping.enabled ?? true,
|
|
createdAt: Date.now(),
|
|
};
|
|
mappings.push(newMapping);
|
|
await this.saveMappings(mappings);
|
|
return newMapping;
|
|
},
|
|
|
|
async updateMapping(id, updates) {
|
|
const mappings = await this.getMappings();
|
|
const idx = mappings.findIndex((m) => m.id === id);
|
|
if (idx === -1) return null;
|
|
mappings[idx] = { ...mappings[idx], ...updates };
|
|
await this.saveMappings(mappings);
|
|
return mappings[idx];
|
|
},
|
|
|
|
async deleteMapping(id) {
|
|
const mappings = await this.getMappings();
|
|
const filtered = mappings.filter((m) => m.id !== id);
|
|
await this.saveMappings(filtered);
|
|
},
|
|
|
|
// --- Identity (Smart Patterns) ---
|
|
|
|
async getIdentity() {
|
|
const result = await api.storage.local.get(KEYS.IDENTITY);
|
|
return result[KEYS.IDENTITY] || {
|
|
emails: [],
|
|
names: [],
|
|
usernames: [],
|
|
phones: [],
|
|
catchAllEmail: '',
|
|
emailDomains: [],
|
|
enabled: { emails: true, names: true, usernames: true, phones: true, paths: true },
|
|
};
|
|
},
|
|
|
|
async saveIdentity(identity) {
|
|
await api.storage.local.set({ [KEYS.IDENTITY]: identity });
|
|
},
|
|
|
|
// --- Activity Log ---
|
|
|
|
async getLog() {
|
|
const result = await api.storage.local.get(KEYS.LOG);
|
|
return result[KEYS.LOG] || [];
|
|
},
|
|
|
|
async addLogEntry(entry) {
|
|
const settings = await this.getSettings();
|
|
const log = await this.getLog();
|
|
|
|
log.unshift({
|
|
...entry,
|
|
id: crypto.randomUUID(),
|
|
timestamp: Date.now(),
|
|
});
|
|
|
|
// Trim to max entries
|
|
if (log.length > settings.maxLogEntries) {
|
|
log.length = settings.maxLogEntries;
|
|
}
|
|
|
|
await api.storage.local.set({ [KEYS.LOG]: log });
|
|
},
|
|
|
|
async clearLog() {
|
|
await api.storage.local.set({ [KEYS.LOG]: [] });
|
|
},
|
|
|
|
// --- Settings ---
|
|
|
|
async getSettings() {
|
|
const result = await api.storage.local.get(KEYS.SETTINGS);
|
|
return { ...DEFAULT_SETTINGS, ...(result[KEYS.SETTINGS] || {}) };
|
|
},
|
|
|
|
async saveSettings(settings) {
|
|
const current = await this.getSettings();
|
|
await api.storage.local.set({
|
|
[KEYS.SETTINGS]: { ...current, ...settings },
|
|
});
|
|
},
|
|
};
|
|
|
|
if (typeof globalThis !== 'undefined') {
|
|
globalThis.SilentSendStorage = Storage;
|
|
}
|
|
|
|
export default Storage;
|