Merge pull request #30 from outis1one/claude/read-repo-wA3y1

Claude/read repo w a3y1
This commit is contained in:
Outis
2026-03-27 17:07:25 -04:00
committed by GitHub
8 changed files with 40 additions and 26 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ A browser extension (Chrome, Firefox, and Safari) that intercepts personal infor
| Service | Domains | Status | | Service | Domains | Status |
|---------|---------|--------| |---------|---------|--------|
| Claude | claude.ai, claude.ai/code | Tested | | Claude | claude.ai, claude.ai/code | Tested |
| ChatGPT | chatgpt.com, chat.openai.com | Untested | | ChatGPT | chatgpt.com, chat.openai.com | Tested |
| Grok | grok.x.ai, x.com/i/grok | Untested | | Grok | grok.x.ai, x.com/i/grok | Untested |
| Gemini | gemini.google.com | Untested | | Gemini | gemini.google.com | Untested |
| OpenWebUI | localhost, 127.0.0.1, or custom domain | Untested | | OpenWebUI | localhost, 127.0.0.1, or custom domain | Untested |
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "Silent Send", "name": "Silent Send",
"version": "2.0.13", "version": "2.0.14",
"description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.", "description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.",
"browser_specific_settings": { "browser_specific_settings": {
"gecko": { "gecko": {
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "Silent Send", "name": "Silent Send",
"version": "2.0.13", "version": "2.0.14",
"description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.", "description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.",
"permissions": [ "permissions": [
"storage", "storage",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "silent-send", "name": "silent-send",
"version": "2.0.13", "version": "2.0.14",
"private": true, "private": true,
"license": "BSL-1.1", "license": "BSL-1.1",
"description": "Browser extension that substitutes personal data before sending to AI services", "description": "Browser extension that substitutes personal data before sending to AI services",
+4 -3
View File
@@ -221,13 +221,14 @@
.ss-ps-add:disabled { border-color: #333; cursor: default; } .ss-ps-add:disabled { border-color: #333; cursor: default; }
.ss-ps-ignore { .ss-ps-ignore {
background: none; background: #4b5563;
border: none; border: none;
color: #6b7280; color: #d1d5db;
font-size: 9px; font-size: 9px;
cursor: pointer; cursor: pointer;
flex-shrink: 0; flex-shrink: 0;
padding: 2px 4px; padding: 3px 6px;
border-radius: 3px;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.5px; letter-spacing: 0.5px;
} }
+31 -17
View File
@@ -578,7 +578,7 @@
if (!text || text.length < 5) return []; if (!text || text.length < 5) return [];
const hasContext = CONTEXT_WORDS_RE.test(text); const hasContext = CONTEXT_WORDS_RE.test(text);
// Build skip set from configured values // Build skip set from configured values (identity + explicit mappings)
const configured = new Set(); const configured = new Set();
if (ident) { if (ident) {
const addAll = (arr, key) => (arr || []).forEach(item => { const addAll = (arr, key) => (arr || []).forEach(item => {
@@ -588,6 +588,11 @@
addAll(ident.names); addAll(ident.emails); addAll(ident.names); addAll(ident.emails);
addAll(ident.usernames); addAll(ident.hostnames); addAll(ident.phones); addAll(ident.usernames); addAll(ident.hostnames); addAll(ident.phones);
} }
// Also skip values that are already in explicit mappings
for (const m of mappings) {
if (m.real) configured.add(m.real.toLowerCase());
if (m.substitute) configured.add(m.substitute.toLowerCase());
}
const findings = []; const findings = [];
for (const pat of PPI_PATTERNS) { for (const pat of PPI_PATTERNS) {
@@ -1548,8 +1553,8 @@
function revealInElement(el) { function revealInElement(el) {
if (SKIP_REVEAL_TAGS.has(el.tagName)) return; if (SKIP_REVEAL_TAGS.has(el.tagName)) return;
if (el.classList?.contains('ss-reveal-badge')) return; if (el.classList?.contains('ss-reveal-badge')) return;
// Never touch contenteditable elements (chat input boxes)
if (el.isContentEditable) return; if (el.isContentEditable) return;
if (isInNonChatArea(el)) return;
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, { const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, {
acceptNode(node) { acceptNode(node) {
@@ -1557,6 +1562,7 @@
if (parent && SKIP_REVEAL_TAGS.has(parent.tagName)) return NodeFilter.FILTER_REJECT; if (parent && SKIP_REVEAL_TAGS.has(parent.tagName)) return NodeFilter.FILTER_REJECT;
if (parent?.closest?.('.ss-autodetect-warning, .ss-presend-warning, .ss-reveal-badge')) return NodeFilter.FILTER_REJECT; if (parent?.closest?.('.ss-autodetect-warning, .ss-presend-warning, .ss-reveal-badge')) return NodeFilter.FILTER_REJECT;
if (parent?.closest?.('[contenteditable="true"]')) return NodeFilter.FILTER_REJECT; if (parent?.closest?.('[contenteditable="true"]')) return NodeFilter.FILTER_REJECT;
if (isInNonChatArea(parent)) return NodeFilter.FILTER_REJECT;
return NodeFilter.FILTER_ACCEPT; return NodeFilter.FILTER_ACCEPT;
} }
}); });
@@ -1576,11 +1582,13 @@
function unrevealInElement(el) { function unrevealInElement(el) {
if (SKIP_REVEAL_TAGS.has(el.tagName)) return; if (SKIP_REVEAL_TAGS.has(el.tagName)) return;
if (el.isContentEditable) return; if (el.isContentEditable) return;
if (isInNonChatArea(el)) return;
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, { const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, {
acceptNode(node) { acceptNode(node) {
const parent = node.parentElement; const parent = node.parentElement;
if (parent?.closest?.('[contenteditable="true"]')) return NodeFilter.FILTER_REJECT; if (parent?.closest?.('[contenteditable="true"]')) return NodeFilter.FILTER_REJECT;
if (isInNonChatArea(parent)) return NodeFilter.FILTER_REJECT;
return NodeFilter.FILTER_ACCEPT; return NodeFilter.FILTER_ACCEPT;
} }
}); });
@@ -1615,6 +1623,7 @@
if (parent && SKIP_REVEAL_TAGS.has(parent.tagName)) return NodeFilter.FILTER_REJECT; if (parent && SKIP_REVEAL_TAGS.has(parent.tagName)) return NodeFilter.FILTER_REJECT;
if (parent?.closest?.('.ss-autodetect-warning, .ss-presend-warning, .ss-reveal-badge')) return NodeFilter.FILTER_REJECT; if (parent?.closest?.('.ss-autodetect-warning, .ss-presend-warning, .ss-reveal-badge')) return NodeFilter.FILTER_REJECT;
if (parent?.closest?.('[contenteditable="true"]')) return NodeFilter.FILTER_REJECT; if (parent?.closest?.('[contenteditable="true"]')) return NodeFilter.FILTER_REJECT;
if (isInNonChatArea(parent)) return NodeFilter.FILTER_REJECT;
return NodeFilter.FILTER_ACCEPT; return NodeFilter.FILTER_ACCEPT;
} }
}); });
@@ -1653,11 +1662,18 @@
} }
} }
// Elements to skip when revealing (inputs, scripts, styles, extension UI) // Elements to skip when revealing (inputs, scripts, styles, extension UI, navigation)
const SKIP_REVEAL_TAGS = new Set([ const SKIP_REVEAL_TAGS = new Set([
'SCRIPT', 'STYLE', 'NOSCRIPT', 'IFRAME', 'INPUT', 'TEXTAREA', 'SELECT', 'SCRIPT', 'STYLE', 'NOSCRIPT', 'IFRAME', 'INPUT', 'TEXTAREA', 'SELECT',
'NAV', 'ASIDE', 'HEADER', 'FOOTER',
]); ]);
// Skip reveal in navigation, sidebars, headers, and other non-chat UI
function isInNonChatArea(el) {
if (!el) return false;
return !!el.closest('nav, aside, header, footer, [role="navigation"], [role="banner"], [role="complementary"], [data-sidebar], [class*="sidebar"], [class*="Sidebar"], [class*="nav-"], [class*="Nav"], [class*="menu"], [class*="Menu"], [class*="header"], [class*="Header"]');
}
// Reveal ALL text on the page + apply highlights // Reveal ALL text on the page + apply highlights
function revealAllResponses() { function revealAllResponses() {
revealInElement(document.body); revealInElement(document.body);
@@ -1695,10 +1711,11 @@
// Only do text replacement in reveal mode // Only do text replacement in reveal mode
if (settings.revealMode) { if (settings.revealMode) {
if (node.nodeType === Node.ELEMENT_NODE) { if (node.nodeType === Node.ELEMENT_NODE) {
// Skip contenteditable (chat input) and skipped tags // Skip non-chat areas, contenteditable, and skipped tags
if (!SKIP_REVEAL_TAGS.has(node.tagName) && if (!SKIP_REVEAL_TAGS.has(node.tagName) &&
!node.isContentEditable && !node.isContentEditable &&
!node.closest?.('[contenteditable="true"]')) { !node.closest?.('[contenteditable="true"]') &&
!isInNonChatArea(node)) {
revealInElement(node); revealInElement(node);
} }
} else if (node.nodeType === Node.TEXT_NODE) { } else if (node.nodeType === Node.TEXT_NODE) {
@@ -1848,13 +1865,12 @@
const items = warnings.slice(0, 8).map((w, i) => { const items = warnings.slice(0, 8).map((w, i) => {
const fake = generateFake(w.name, w.value); const fake = generateFake(w.name, w.value);
const displayVal = w.value.length > 25 ? w.value.slice(0, 22) + '...' : w.value; const displayVal = w.value.length > 25 ? w.value.slice(0, 22) + '...' : w.value;
const displayFake = fake.length > 20 ? fake.slice(0, 17) + '...' : fake;
return `<div class="ss-ps-item"> return `<div class="ss-ps-item">
<span class="ss-ps-type">${w.name}</span> <span class="ss-ps-type">${w.name}</span>
<code class="ss-ps-value">${displayVal}</code> <code class="ss-ps-value">${displayVal}</code>
<span class="ss-ps-hint">${w.hint}</span> <span class="ss-ps-hint">${w.hint}</span>
${settings.autoAddDetected !== false <button class="ss-ps-add" data-real="${encodeURIComponent(w.value)}" data-fake="${encodeURIComponent(fake)}" data-cat="${w.category}" title="Add mapping: ${displayVal}${displayFake}">+</button>
? `<button class="ss-ps-add" data-real="${encodeURIComponent(w.value)}" data-fake="${encodeURIComponent(fake)}" data-cat="${w.category}" title="Add mapping: ${displayVal}${fake}">+</button>`
: ''}
<button class="ss-ps-ignore" data-value="${encodeURIComponent(w.value)}" title="Never flag this value again">ignore</button> <button class="ss-ps-ignore" data-value="${encodeURIComponent(w.value)}" title="Never flag this value again">ignore</button>
</div>`; </div>`;
}).join(''); }).join('');
@@ -1881,28 +1897,26 @@
preSendWarningEl.classList.remove('visible'); preSendWarningEl.classList.remove('visible');
}); });
// Auto-add buttons // Auto-add buttons (+)
preSendWarningEl.querySelectorAll('.ss-ps-add').forEach(btn => { preSendWarningEl.querySelectorAll('.ss-ps-add').forEach(btn => {
btn.addEventListener('click', async () => { btn.addEventListener('click', async () => {
const real = decodeURIComponent(btn.dataset.real); const real = decodeURIComponent(btn.dataset.real);
const fake = decodeURIComponent(btn.dataset.fake); const fake = decodeURIComponent(btn.dataset.fake);
const cat = btn.dataset.cat || 'general'; const cat = btn.dataset.cat || 'general';
// Add to mappings via storage // Add to local mappings array (used by the fetch interceptor)
const result = await getStorageData('ss_mappings'); const newMapping = {
const currentMappings = result || [];
currentMappings.push({
id: crypto.randomUUID(), id: crypto.randomUUID(),
real, substitute: fake, real, substitute: fake,
category: cat, category: cat,
caseSensitive: false, caseSensitive: false,
enabled: true, enabled: true,
createdAt: Date.now(), createdAt: Date.now(),
}); };
await setStorageData('ss_mappings', currentMappings); mappings.push(newMapping);
// Update local mappings so the fetch interceptor uses them immediately // Persist via storage bridge (handles encryption transparently)
mappings = currentMappings; setStorageData('ss_mappings', mappings);
// Replace the PPI value in the current input right now // Replace the PPI value in the current input right now
if (inputEl) { if (inputEl) {
+1 -1
View File
@@ -591,7 +591,7 @@
</section> </section>
<footer> <footer>
<p>Silent Send v2.0.13</p> <p>Silent Send v2.0.14</p>
</footer> </footer>
</div> </div>
-1
View File
@@ -262,7 +262,6 @@
PPI in images, file uploads, unusual name forms, or data you forgot to PPI in images, file uploads, unusual name forms, or data you forgot to
configure. Always verify sensitive messages before sending. configure. Always verify sensitive messages before sending.
</div> </div>
<a href="#" id="btnOptions">Options</a>
</footer> </footer>
</div> </div>