Merge pull request #59 from outis1one/claude/audit-silent-send-5thF8
Fix PPI → PII terminology across entire codebase
This commit is contained in:
@@ -15,7 +15,7 @@ A browser extension (Chrome, Firefox, and Safari) that intercepts personal infor
|
||||
|
||||
> **Browsers:** Chrome, Edge, Brave, Firefox, and Safari. Edge and Brave use the Chrome extension directly.
|
||||
>
|
||||
> **Desktop apps** (Claude, ChatGPT, Copilot) bypass the browser — use the web version with the extension for PPI protection.
|
||||
> **Desktop apps** (Claude, ChatGPT, Copilot) bypass the browser — use the web version with the extension for PII protection.
|
||||
>
|
||||
> **Note:** Claude and ChatGPT are tested. Other services have interception patterns defined but may need adjustments. PRs welcome.
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* Auto-detect PPI warning banner */
|
||||
/* Auto-detect PII warning banner */
|
||||
.ss-autodetect-warning {
|
||||
position: fixed;
|
||||
top: 16px;
|
||||
@@ -136,7 +136,7 @@
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Pre-send PPI warning (spellcheck-style, appears while typing) */
|
||||
/* Pre-send PII warning (spellcheck-style, appears while typing) */
|
||||
.ss-presend-warning {
|
||||
position: fixed;
|
||||
top: 16px;
|
||||
|
||||
+19
-19
@@ -254,7 +254,7 @@
|
||||
|
||||
// ============================================================
|
||||
// Combined substitution: smart patterns + explicit + secret scan
|
||||
// + auto-detect warning for unconfigured PPI
|
||||
// + auto-detect warning for unconfigured PII
|
||||
// ============================================================
|
||||
function substituteAll(text) {
|
||||
const allReplacements = [];
|
||||
@@ -275,12 +275,12 @@
|
||||
finalText = secrets.text;
|
||||
}
|
||||
|
||||
// 4. Auto-detect: scan the FINAL text for unconfigured PPI
|
||||
// 4. Auto-detect: scan the FINAL text for unconfigured PII
|
||||
// Auto-redact if enabled, otherwise just warn
|
||||
if (settings.autoDetect !== false) {
|
||||
const warnings = autoDetectPPI(finalText, identity);
|
||||
const warnings = autoDetectPII(finalText, identity);
|
||||
if (warnings.length > 0) {
|
||||
// Auto-redact detected PPI in the outbound text
|
||||
// Auto-redact detected PII in the outbound text
|
||||
if (settings.autoRedactDetected !== false) {
|
||||
for (let i = warnings.length - 1; i >= 0; i--) {
|
||||
const w = warnings[i];
|
||||
@@ -309,9 +309,9 @@
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Auto-Detect PPI Scanner (inline for page world)
|
||||
// Auto-Detect PII Scanner (inline for page world)
|
||||
// ============================================================
|
||||
const PPI_PATTERNS = [
|
||||
const PII_PATTERNS = [
|
||||
// Network
|
||||
{ name: 'Private IP', re: /\b(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3}|172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3})\b/g,
|
||||
hint: 'Private IP address', cat: 'network' },
|
||||
@@ -389,7 +389,7 @@
|
||||
// Common sentence starters that aren't names
|
||||
'please', 'thanks', 'hello', 'hi', 'hey', 'dear', 'sincerely',
|
||||
'regards', 'best', 'cheers', 'sorry', 'yes', 'no', 'ok', 'okay',
|
||||
// Days and months (not PPI)
|
||||
// Days and months (not PII)
|
||||
'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday',
|
||||
'january', 'february', 'march', 'april', 'may', 'june', 'july',
|
||||
'august', 'september', 'october', 'november', 'december',
|
||||
@@ -454,7 +454,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
function autoDetectPPI(text, ident) {
|
||||
function autoDetectPII(text, ident) {
|
||||
if (!text || text.length < 5) return [];
|
||||
const hasContext = CONTEXT_WORDS_RE.test(text);
|
||||
|
||||
@@ -470,7 +470,7 @@
|
||||
}
|
||||
|
||||
const findings = [];
|
||||
for (const pat of PPI_PATTERNS) {
|
||||
for (const pat of PII_PATTERNS) {
|
||||
if (pat.contextRequired && !hasContext) continue;
|
||||
pat.re.lastIndex = 0;
|
||||
let m;
|
||||
@@ -521,7 +521,7 @@
|
||||
|
||||
warningEl.innerHTML = `
|
||||
<div class="ss-ad-header">
|
||||
<strong>Silent Send detected potential PPI that may not be substituted:</strong>
|
||||
<strong>Silent Send detected potential PII that may not be substituted:</strong>
|
||||
<button class="ss-ad-close">×</button>
|
||||
</div>
|
||||
${items}
|
||||
@@ -1180,7 +1180,7 @@
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Pre-Send PPI Detection — scans as you type/paste (spellcheck style)
|
||||
// Pre-Send PII Detection — scans as you type/paste (spellcheck style)
|
||||
// ============================================================
|
||||
|
||||
// Generate obviously-fake values using reserved/standard ranges
|
||||
@@ -1244,7 +1244,7 @@
|
||||
|
||||
preSendWarningEl.innerHTML = `
|
||||
<div class="ss-ad-header">
|
||||
<strong>Potential PPI detected — not yet configured:</strong>
|
||||
<strong>Potential PII detected — not yet configured:</strong>
|
||||
<button class="ss-ad-close">×</button>
|
||||
</div>
|
||||
${items}
|
||||
@@ -1285,12 +1285,12 @@
|
||||
// Update local mappings so the fetch interceptor uses them immediately
|
||||
mappings = currentMappings;
|
||||
|
||||
// Replace the PPI value in the current input right now
|
||||
// Replace the PII value in the current input right now
|
||||
if (inputEl) {
|
||||
replaceInInput(inputEl, real, fake);
|
||||
// Re-scan — will dismiss warning if no more PPI remains
|
||||
// Re-scan — will dismiss warning if no more PII remains
|
||||
if (inputScanTimer) clearTimeout(inputScanTimer);
|
||||
inputScanTimer = setTimeout(() => scanInputForPPI(inputEl), 150);
|
||||
inputScanTimer = setTimeout(() => scanInputForPII(inputEl), 150);
|
||||
}
|
||||
|
||||
// Visual feedback
|
||||
@@ -1346,14 +1346,14 @@
|
||||
// Scan input on type and paste
|
||||
let inputScanTimer = null;
|
||||
|
||||
function scanInputForPPI(target) {
|
||||
function scanInputForPII(target) {
|
||||
const text = target.textContent || target.value || '';
|
||||
if (!text || text.length < 5) {
|
||||
if (preSendWarningEl) preSendWarningEl.classList.remove('visible');
|
||||
return;
|
||||
}
|
||||
|
||||
const warnings = autoDetectPPI(text, identity);
|
||||
const warnings = autoDetectPII(text, identity);
|
||||
if (warnings.length > 0) {
|
||||
showPreSendWarning(warnings, target);
|
||||
} else if (preSendWarningEl) {
|
||||
@@ -1367,7 +1367,7 @@
|
||||
if (target.matches?.('[contenteditable], textarea, input[type="text"]')) {
|
||||
// Debounce — don't scan on every keystroke
|
||||
if (inputScanTimer) clearTimeout(inputScanTimer);
|
||||
inputScanTimer = setTimeout(() => scanInputForPPI(target), 800);
|
||||
inputScanTimer = setTimeout(() => scanInputForPII(target), 800);
|
||||
}
|
||||
}, true);
|
||||
|
||||
@@ -1377,7 +1377,7 @@
|
||||
if (target.matches?.('[contenteditable], textarea, input[type="text"]') ||
|
||||
target.closest?.('[contenteditable]')) {
|
||||
// Scan shortly after paste completes
|
||||
setTimeout(() => scanInputForPPI(target.closest?.('[contenteditable]') || target), 200);
|
||||
setTimeout(() => scanInputForPII(target.closest?.('[contenteditable]') || target), 200);
|
||||
}
|
||||
}, true);
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/**
|
||||
* Silent Send - Auto-Detect
|
||||
*
|
||||
* Scans text for potential PPI that the user hasn't configured.
|
||||
* Scans text for potential PII that the user hasn't configured.
|
||||
* This catches things the identity and secret scanner can't —
|
||||
* because the user forgot or didn't know to configure them.
|
||||
*
|
||||
* Returns warnings (not auto-redactions) so the user can decide.
|
||||
*/
|
||||
|
||||
const PPI_PATTERNS = [
|
||||
const PII_PATTERNS = [
|
||||
// --- Network ---
|
||||
{
|
||||
name: 'Private IP Address',
|
||||
@@ -21,7 +21,7 @@ const PPI_PATTERNS = [
|
||||
regex: /\b(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\b/g,
|
||||
category: 'network',
|
||||
hint: 'IP address — could identify your network',
|
||||
// Exclude common non-PPI IPs
|
||||
// Exclude common non-PII IPs
|
||||
exclude: /^(?:127\.0\.0\.1|0\.0\.0\.0|255\.255\.255\.\d+|8\.8\.[84]\.[84]|1\.1\.1\.1|1\.0\.0\.1)$/,
|
||||
},
|
||||
{
|
||||
@@ -126,12 +126,12 @@ const PPI_PATTERNS = [
|
||||
},
|
||||
];
|
||||
|
||||
// Context words that make ambiguous patterns more likely to be PPI
|
||||
// Context words that make ambiguous patterns more likely to be PII
|
||||
const CONTEXT_WORDS = /\b(?:born|birthday|dob|birth|passport|license|driver|ssn|social\s*security|address|home|live|lives|reside|zip|postal)\b/i;
|
||||
|
||||
const AutoDetect = {
|
||||
/**
|
||||
* Scan text for potential unconfigured PPI.
|
||||
* Scan text for potential unconfigured PII.
|
||||
* Pass in identity so we can skip values the user already configured.
|
||||
*
|
||||
* Returns array of { name, value, hint, category, index }
|
||||
@@ -167,7 +167,7 @@ const AutoDetect = {
|
||||
}
|
||||
}
|
||||
|
||||
for (const pattern of PPI_PATTERNS) {
|
||||
for (const pattern of PII_PATTERNS) {
|
||||
// Skip context-dependent patterns if no context words present
|
||||
if (pattern.contextRequired && !hasContext) continue;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* - Policy updates are applied automatically
|
||||
*
|
||||
* Privacy: the org admin can check compliance (are required fields
|
||||
* configured?) but CANNOT see individual PPI values.
|
||||
* configured?) but CANNOT see individual PII values.
|
||||
*/
|
||||
|
||||
import api from './browser-polyfill.js';
|
||||
@@ -220,7 +220,7 @@ const OrgPolicy = {
|
||||
|
||||
/**
|
||||
* Check if the user's configuration meets org policy requirements.
|
||||
* Returns compliance status WITHOUT revealing actual PPI values.
|
||||
* Returns compliance status WITHOUT revealing actual PII values.
|
||||
*
|
||||
* @returns {{ compliant: boolean, missing: string[], configured: string[] }}
|
||||
*/
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ const KEYS = {
|
||||
SETTINGS: 'ss_settings',
|
||||
};
|
||||
|
||||
// Keys that contain sensitive PPI and should be encrypted at rest
|
||||
// Keys that contain sensitive PII and should be encrypted at rest
|
||||
// All user data keys are encrypted at rest — settings included since
|
||||
// custom domains and configuration can reveal what services the user
|
||||
// accesses. Only ss_sync_encryption (salt, verification blob) and
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div>
|
||||
<label>Auto-detect unconfigured PPI</label>
|
||||
<label>Auto-detect unconfigured PII</label>
|
||||
<p class="setting-desc">Warn when potential personal data (IPs, addresses, paths) is detected that you haven't configured</p>
|
||||
</div>
|
||||
<label class="toggle">
|
||||
@@ -69,8 +69,8 @@
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div>
|
||||
<label>Auto-redact detected PPI on send</label>
|
||||
<p class="setting-desc">Automatically replace detected PPI with generic placeholders (192.0.2.1, 123 Example Street, etc.) when sending</p>
|
||||
<label>Auto-redact detected PII on send</label>
|
||||
<p class="setting-desc">Automatically replace detected PII with generic placeholders (192.0.2.1, 123 Example Street, etc.) when sending</p>
|
||||
</div>
|
||||
<label class="toggle">
|
||||
<input type="checkbox" id="autoRedactDetected" checked>
|
||||
@@ -79,8 +79,8 @@
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div>
|
||||
<label>Offer to auto-add detected PPI</label>
|
||||
<p class="setting-desc">Show a + button on detected PPI to instantly create a mapping with a suggested fake value</p>
|
||||
<label>Offer to auto-add detected PII</label>
|
||||
<p class="setting-desc">Show a + button on detected PII to instantly create a mapping with a suggested fake value</p>
|
||||
</div>
|
||||
<label class="toggle">
|
||||
<input type="checkbox" id="autoAddDetected" checked>
|
||||
|
||||
@@ -202,7 +202,7 @@
|
||||
</div>
|
||||
<div class="privacy-note" style="color:#b45309;background:#fef3c7;padding:6px 8px;border-radius:4px;margin-bottom:6px">
|
||||
Silent Send is a convenience tool, not a security guarantee. It can miss
|
||||
PPI in images, file uploads, unusual name forms, or data you forgot to
|
||||
PII in images, file uploads, unusual name forms, or data you forgot to
|
||||
configure. Always verify sensitive messages before sending.
|
||||
</div>
|
||||
<a href="#" id="btnOptions">Options</a>
|
||||
|
||||
+7
-7
@@ -715,17 +715,17 @@ function renderTestDiff() {
|
||||
if (secretCount > 0) parts.push(`${secretCount} secrets redacted`);
|
||||
if (warnCount > 0) parts.push(`${warnCount} warnings`);
|
||||
|
||||
// Auto-detect unconfigured PPI in the final text
|
||||
const ppiWarnings = AutoDetect.scan(finalText, identity);
|
||||
if (ppiWarnings.length > 0) parts.push(`${ppiWarnings.length} PPI detected`);
|
||||
// Auto-detect unconfigured PII in the final text
|
||||
const piiWarnings = AutoDetect.scan(finalText, identity);
|
||||
if (piiWarnings.length > 0) parts.push(`${piiWarnings.length} PII detected`);
|
||||
|
||||
stats.textContent = `${allReplacements.length} substitution${allReplacements.length !== 1 ? 's' : ''} (${parts.join(', ')})`;
|
||||
|
||||
// Show PPI warnings below stats
|
||||
if (ppiWarnings.length > 0) {
|
||||
// Show PII warnings below stats
|
||||
if (piiWarnings.length > 0) {
|
||||
stats.innerHTML += `<div style="margin-top:6px;padding:6px 8px;background:#fef3c7;border-radius:4px;color:#92400e;font-size:11px">
|
||||
<strong>Unconfigured PPI detected:</strong>
|
||||
${ppiWarnings.map(w => `<div style="margin-top:3px"><code style="background:#fff;padding:1px 4px;border-radius:2px;color:#b45309">${escapeHtml(w.value)}</code> — ${w.hint}</div>`).join('')}
|
||||
<strong>Unconfigured PII detected:</strong>
|
||||
${piiWarnings.map(w => `<div style="margin-top:3px"><code style="background:#fff;padding:1px 4px;border-radius:2px;color:#b45309">${escapeHtml(w.value)}</code> — ${w.hint}</div>`).join('')}
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user