refactor: rename all secret scanner internals to auto-redact

Full internal rename since still in testing — no backwards compat needed:
- secret-scanner.js → auto-redact.js
- SecretScanner → AutoRedact
- SECRET_PATTERNS → REDACT_PATTERNS
- secretScanning → autoRedact (setting key)
- customSecretPatterns → customRedactPatterns (setting key)
- scanAndRedactSecrets → runAutoRedact (function)
- All DOM ids, CSS classes, and variable names updated
- category: 'secret' → category: 'redact'
- getOrgSecretPatterns → getOrgRedactPatterns

https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
This commit is contained in:
Claude
2026-03-28 14:59:59 +00:00
parent 7ec058f1ea
commit 33d1542d12
8 changed files with 79 additions and 79 deletions
+12 -12
View File
@@ -287,7 +287,7 @@
} }
// ============================================================ // ============================================================
// Combined substitution: smart patterns + explicit + secret scan // Combined substitution: smart patterns + explicit + auto-redact
// + auto-detect warning for unconfigured PPI // + auto-detect warning for unconfigured PPI
// ============================================================ // ============================================================
function substituteAll(text) { function substituteAll(text) {
@@ -303,10 +303,10 @@
// 3. Auto Redact (API keys, tokens, SSNs, credit cards, custom patterns, etc.) // 3. Auto Redact (API keys, tokens, SSNs, credit cards, custom patterns, etc.)
let finalText = explicit.text; let finalText = explicit.text;
if (settings.secretScanning !== false) { if (settings.autoRedact !== false) {
const secrets = scanAndRedactSecrets(finalText); const redacted = runAutoRedact(finalText);
allReplacements.push(...secrets.redactions); allReplacements.push(...redacted.redactions);
finalText = secrets.text; finalText = redacted.text;
} }
// 4. Auto-detect: scan the FINAL text for unconfigured PPI // 4. Auto-detect: scan the FINAL text for unconfigured PPI
@@ -665,11 +665,11 @@
} }
// ============================================================ // ============================================================
// Auto Redact — Secret Scanner (inline for page world) // Auto Redact (inline for page world)
// Detects API keys, tokens, passwords, SSNs, credit cards, // Detects API keys, tokens, passwords, SSNs, credit cards,
// plus user-defined custom patterns from settings. // plus user-defined custom patterns from settings.
// ============================================================ // ============================================================
const SECRET_PATTERNS = [ const REDACT_PATTERNS = [
// OpenAI // OpenAI
{ name: 'OpenAI Key', re: /\bsk-[A-Za-z0-9]{20,}\b/g, to: '[REDACTED-OPENAI-KEY]' }, { name: 'OpenAI Key', re: /\bsk-[A-Za-z0-9]{20,}\b/g, to: '[REDACTED-OPENAI-KEY]' },
{ name: 'OpenAI Project Key', re: /\bsk-proj-[A-Za-z0-9_-]{20,}\b/g, to: '[REDACTED-OPENAI-KEY]' }, { name: 'OpenAI Project Key', re: /\bsk-proj-[A-Za-z0-9_-]{20,}\b/g, to: '[REDACTED-OPENAI-KEY]' },
@@ -707,13 +707,13 @@
{ name: 'Credit Card', re: /\b(?:4\d{3}|5[1-5]\d{2}|3[47]\d{2}|6(?:011|5\d{2}))[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/g, to: '[REDACTED-CARD]' }, { name: 'Credit Card', re: /\b(?:4\d{3}|5[1-5]\d{2}|3[47]\d{2}|6(?:011|5\d{2}))[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/g, to: '[REDACTED-CARD]' },
]; ];
function scanAndRedactSecrets(text) { function runAutoRedact(text) {
const redactions = []; const redactions = [];
let result = text; let result = text;
// Combine built-in + custom patterns // Combine built-in + custom patterns
const allPatterns = [...SECRET_PATTERNS]; const allPatterns = [...REDACT_PATTERNS];
const custom = settings.customSecretPatterns || []; const custom = settings.customRedactPatterns || [];
for (const cp of custom) { for (const cp of custom) {
if (!cp.enabled || !cp.pattern) continue; if (!cp.enabled || !cp.pattern) continue;
try { try {
@@ -739,7 +739,7 @@
redactions.push({ redactions.push({
original: match.value.slice(0, 8) + '...', // Don't log the full secret original: match.value.slice(0, 8) + '...', // Don't log the full secret
replaced: replacement, replaced: replacement,
category: 'secret', category: 'redact',
pattern: pat.name, pattern: pat.name,
}); });
result = result =
@@ -1437,7 +1437,7 @@
} }
} }
// Also add auto-detect and secret scanner substitutions from this session // Also add auto-detect and auto-redact substitutions from this session
for (const [key, entry] of sessionSubstitutions) { for (const [key, entry] of sessionSubstitutions) {
if (!pairs.some(p => p.from.toLowerCase() === key)) { if (!pairs.some(p => p.from.toLowerCase() === key)) {
pairs.push({ from: entry.replaced, to: entry.original }); pairs.push({ from: entry.replaced, to: entry.original });
@@ -1,5 +1,5 @@
/** /**
* Silent Send - Auto Redact (Secret Scanner) * Silent Send - Auto Redact
* *
* Detects common secret/credential patterns in text and either * Detects common secret/credential patterns in text and either
* warns or auto-redacts them. This catches things the identity-based * warns or auto-redacts them. This catches things the identity-based
@@ -15,7 +15,7 @@
* - severity: 'critical' (always redact) or 'warning' (flag but allow) * - severity: 'critical' (always redact) or 'warning' (flag but allow)
*/ */
const SECRET_PATTERNS = [ const REDACT_PATTERNS = [
// --- API Keys --- // --- API Keys ---
{ {
name: 'OpenAI API Key', name: 'OpenAI API Key',
@@ -163,13 +163,13 @@ const SECRET_PATTERNS = [
}, },
]; ];
const SecretScanner = { const AutoRedact = {
/** /**
* Build the full pattern list (built-in + custom). * Build the full pattern list (built-in + custom).
* Custom patterns come from settings.customSecretPatterns. * Custom patterns come from settings.customRedactPatterns.
*/ */
_buildPatterns(customPatterns) { _buildPatterns(customPatterns) {
const all = [...SECRET_PATTERNS]; const all = [...REDACT_PATTERNS];
if (Array.isArray(customPatterns)) { if (Array.isArray(customPatterns)) {
for (const cp of customPatterns) { for (const cp of customPatterns) {
if (!cp.enabled || !cp.pattern) continue; if (!cp.enabled || !cp.pattern) continue;
@@ -189,7 +189,7 @@ const SecretScanner = {
/** /**
* Scan text for secrets. Returns list of findings. * Scan text for secrets. Returns list of findings.
* @param {string} text * @param {string} text
* @param {Array} [customPatterns] from settings.customSecretPatterns * @param {Array} [customPatterns] from settings.customRedactPatterns
*/ */
scan(text, customPatterns) { scan(text, customPatterns) {
const findings = []; const findings = [];
@@ -232,7 +232,7 @@ const SecretScanner = {
* Redact all critical secrets in text. Warnings are not auto-redacted. * Redact all critical secrets in text. Warnings are not auto-redacted.
* Returns { text, redactions[] } * Returns { text, redactions[] }
* @param {string} text * @param {string} text
* @param {Array} [customPatterns] from settings.customSecretPatterns * @param {Array} [customPatterns] from settings.customRedactPatterns
*/ */
redact(text, customPatterns) { redact(text, customPatterns) {
const findings = this.scan(text, customPatterns); const findings = this.scan(text, customPatterns);
@@ -249,7 +249,7 @@ const SecretScanner = {
redactions.push({ redactions.push({
original: f.value, original: f.value,
replaced: f.redactTo, replaced: f.redactTo,
category: 'secret', category: 'redact',
pattern: f.name, pattern: f.name,
}); });
} }
@@ -266,7 +266,7 @@ const SecretScanner = {
}; };
if (typeof globalThis !== 'undefined') { if (typeof globalThis !== 'undefined') {
globalThis.SecretScanner = SecretScanner; globalThis.AutoRedact = AutoRedact;
} }
export default SecretScanner; export default AutoRedact;
+1 -1
View File
@@ -202,7 +202,7 @@ const OrgPolicy = {
* *
* @returns {Array} additional patterns to add to auto-redact * @returns {Array} additional patterns to add to auto-redact
*/ */
async getOrgSecretPatterns() { async getOrgRedactPatterns() {
const policy = await this.getPolicy(); const policy = await this.getPolicy();
if (!policy?.requiredSecretPatterns?.length) return []; if (!policy?.requiredSecretPatterns?.length) return [];
+2 -2
View File
@@ -34,13 +34,13 @@ const DEFAULT_SETTINGS = {
enabled: true, enabled: true,
showHighlights: false, showHighlights: false,
revealMode: false, revealMode: false,
secretScanning: true, autoRedact: true,
autoDetect: true, autoDetect: true,
autoRedactDetected: true, autoRedactDetected: true,
autoAddDetected: true, autoAddDetected: true,
maxLogEntries: 100, maxLogEntries: 100,
customDomains: [], customDomains: [],
customSecretPatterns: [], customRedactPatterns: [],
categories: ['name', 'email', 'phone', 'address', 'ssn', 'dob', 'domain', 'password', 'general'], categories: ['name', 'email', 'phone', 'address', 'ssn', 'dob', 'domain', 'password', 'general'],
browserSync: false, browserSync: false,
}; };
+7 -7
View File
@@ -53,26 +53,26 @@
<p class="setting-desc">Automatically detect and redact API keys, tokens, passwords, SSNs, credit card numbers, and custom patterns</p> <p class="setting-desc">Automatically detect and redact API keys, tokens, passwords, SSNs, credit card numbers, and custom patterns</p>
</div> </div>
<label class="toggle"> <label class="toggle">
<input type="checkbox" id="secretScanning" checked> <input type="checkbox" id="autoRedactToggle" checked>
<span class="toggle-slider"></span> <span class="toggle-slider"></span>
</label> </label>
</div> </div>
<!-- Custom Secret Patterns --> <!-- Custom Secret Patterns -->
<div style="margin:12px 0;padding:12px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px"> <div style="margin:12px 0;padding:12px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px">
<h3 style="font-size:13px;font-weight:600;margin:0 0 4px">Custom Secret Patterns</h3> <h3 style="font-size:13px;font-weight:600;margin:0 0 4px">Custom Redact Patterns</h3>
<p class="setting-desc" style="margin-bottom:8px"> <p class="setting-desc" style="margin-bottom:8px">
Define your own patterns to catch proprietary tokens, internal URLs with keys, or any format the built-in scanner doesn't cover. Define your own patterns to catch proprietary tokens, internal URLs with keys, or any format the built-in scanner doesn't cover.
</p> </p>
<div id="customSecretList"></div> <div id="customRedactList"></div>
<div style="display:flex;flex-direction:column;gap:6px;margin-top:8px"> <div style="display:flex;flex-direction:column;gap:6px;margin-top:8px">
<div style="display:flex;gap:6px;flex-wrap:wrap"> <div style="display:flex;gap:6px;flex-wrap:wrap">
<input type="text" id="newSecretName" placeholder="Name (e.g. ControlD Token)" style="flex:1;min-width:140px;font-size:12px;padding:5px 8px;border:1px solid #d1d5db;border-radius:6px"> <input type="text" id="newRedactName" placeholder="Name (e.g. ControlD Token)" style="flex:1;min-width:140px;font-size:12px;padding:5px 8px;border:1px solid #d1d5db;border-radius:6px">
<input type="text" id="newSecretPattern" placeholder="Regex or prefix (e.g. ctrl_[A-Za-z0-9]{20,})" style="flex:2;min-width:200px;font-size:12px;font-family:monospace;padding:5px 8px;border:1px solid #d1d5db;border-radius:6px"> <input type="text" id="newRedactPattern" placeholder="Regex or prefix (e.g. ctrl_[A-Za-z0-9]{20,})" style="flex:2;min-width:200px;font-size:12px;font-family:monospace;padding:5px 8px;border:1px solid #d1d5db;border-radius:6px">
</div> </div>
<div style="display:flex;gap:6px;align-items:center"> <div style="display:flex;gap:6px;align-items:center">
<input type="text" id="newSecretRedact" placeholder="Replacement (default: [REDACTED-NAME])" style="flex:1;font-size:12px;padding:5px 8px;border:1px solid #d1d5db;border-radius:6px"> <input type="text" id="newRedactReplacement" placeholder="Replacement (default: [REDACTED-NAME])" style="flex:1;font-size:12px;padding:5px 8px;border:1px solid #d1d5db;border-radius:6px">
<button class="btn btn-primary btn-sm" id="btnAddSecretPattern">Add Pattern</button> <button class="btn btn-primary btn-sm" id="btnAddRedactPattern">Add Pattern</button>
</div> </div>
</div> </div>
<p class="setting-desc" style="margin-top:6px;margin-bottom:0"> <p class="setting-desc" style="margin-top:6px;margin-bottom:0">
+34 -34
View File
@@ -26,7 +26,7 @@ document.addEventListener('DOMContentLoaded', async () => {
// Apply settings to UI // Apply settings to UI
$('#showHighlights').checked = settings.showHighlights || false; $('#showHighlights').checked = settings.showHighlights || false;
$('#secretScanning').checked = settings.secretScanning !== false; $('#autoRedactToggle').checked = settings.autoRedact !== false;
$('#autoDetect').checked = settings.autoDetect !== false; $('#autoDetect').checked = settings.autoDetect !== false;
$('#autoRedactDetected').checked = settings.autoRedactDetected !== false; $('#autoRedactDetected').checked = settings.autoRedactDetected !== false;
$('#autoAddDetected').checked = settings.autoAddDetected !== false; $('#autoAddDetected').checked = settings.autoAddDetected !== false;
@@ -293,15 +293,15 @@ document.addEventListener('DOMContentLoaded', async () => {
await Storage.saveSettings({ showHighlights: e.target.checked }); await Storage.saveSettings({ showHighlights: e.target.checked });
}); });
$('#secretScanning').addEventListener('change', async (e) => { $('#autoRedactToggle').addEventListener('change', async (e) => {
await Storage.saveSettings({ secretScanning: e.target.checked }); await Storage.saveSettings({ autoRedact: e.target.checked });
}); });
// Custom secret patterns // Custom redact patterns
renderCustomSecrets(); renderCustomRedactPatterns();
$('#btnAddSecretPattern').addEventListener('click', addCustomSecret); $('#btnAddRedactPattern').addEventListener('click', addCustomRedactPattern);
$('#newSecretPattern').addEventListener('keydown', (e) => { $('#newRedactPattern').addEventListener('keydown', (e) => {
if (e.key === 'Enter') addCustomSecret(); if (e.key === 'Enter') addCustomRedactPattern();
}); });
$('#autoDetect').addEventListener('change', async (e) => { $('#autoDetect').addEventListener('change', async (e) => {
@@ -805,12 +805,12 @@ function renderDomains() {
}); });
} }
// --- Custom Secret Patterns --- // --- Custom Redact Patterns ---
function addCustomSecret() { function addCustomRedactPattern() {
const name = $('#newSecretName').value.trim(); const name = $('#newRedactName').value.trim();
const pattern = $('#newSecretPattern').value.trim(); const pattern = $('#newRedactPattern').value.trim();
const redact = $('#newSecretRedact').value.trim(); const redact = $('#newRedactReplacement').value.trim();
if (!pattern) { alert('Pattern is required.'); return; } if (!pattern) { alert('Pattern is required.'); return; }
@@ -825,7 +825,7 @@ function addCustomSecret() {
const label = name || 'Custom Pattern'; const label = name || 'Custom Pattern';
const replacement = redact || `[REDACTED-${label.toUpperCase().replace(/\s+/g, '-')}]`; const replacement = redact || `[REDACTED-${label.toUpperCase().replace(/\s+/g, '-')}]`;
const patterns = settings.customSecretPatterns || []; const patterns = settings.customRedactPatterns || [];
patterns.push({ patterns.push({
id: crypto.randomUUID(), id: crypto.randomUUID(),
name: label, name: label,
@@ -834,19 +834,19 @@ function addCustomSecret() {
enabled: true, enabled: true,
}); });
settings.customSecretPatterns = patterns; settings.customRedactPatterns = patterns;
Storage.saveSettings({ customSecretPatterns: patterns }); Storage.saveSettings({ customRedactPatterns: patterns });
renderCustomSecrets(); renderCustomRedactPatterns();
$('#newSecretName').value = ''; $('#newRedactName').value = '';
$('#newSecretPattern').value = ''; $('#newRedactPattern').value = '';
$('#newSecretRedact').value = ''; $('#newRedactReplacement').value = '';
} }
function renderCustomSecrets() { function renderCustomRedactPatterns() {
const list = $('#customSecretList'); const list = $('#customRedactList');
if (!list) return; if (!list) return;
const patterns = settings.customSecretPatterns || []; const patterns = settings.customRedactPatterns || [];
if (patterns.length === 0) { if (patterns.length === 0) {
safeHTML(list, '<div style="font-size:12px;color:#9ca3af;padding:6px 0">No custom patterns defined. Built-in patterns cover common API keys, tokens, and credentials.</div>'); safeHTML(list, '<div style="font-size:12px;color:#9ca3af;padding:6px 0">No custom patterns defined. Built-in patterns cover common API keys, tokens, and credentials.</div>');
@@ -856,35 +856,35 @@ function renderCustomSecrets() {
safeHTML(list, patterns.map((p, i) => ` safeHTML(list, patterns.map((p, i) => `
<div style="display:flex;align-items:center;gap:6px;padding:6px 8px;background:#fff;border:1px solid #e5e7eb;border-radius:6px;margin-bottom:4px;flex-wrap:wrap"> <div style="display:flex;align-items:center;gap:6px;padding:6px 8px;background:#fff;border:1px solid #e5e7eb;border-radius:6px;margin-bottom:4px;flex-wrap:wrap">
<label style="display:flex;align-items:center;gap:4px;cursor:pointer;min-width:0"> <label style="display:flex;align-items:center;gap:4px;cursor:pointer;min-width:0">
<input type="checkbox" class="secret-toggle" data-index="${i}" ${p.enabled ? 'checked' : ''}> <input type="checkbox" class="redact-toggle" data-index="${i}" ${p.enabled ? 'checked' : ''}>
</label> </label>
<span style="font-size:12px;font-weight:500;white-space:nowrap">${escapeHtml(p.name)}</span> <span style="font-size:12px;font-weight:500;white-space:nowrap">${escapeHtml(p.name)}</span>
<code style="font-size:11px;color:#6b7280;background:#f3f4f6;padding:1px 5px;border-radius:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:250px" title="${escapeHtml(p.pattern)}">${escapeHtml(p.pattern)}</code> <code style="font-size:11px;color:#6b7280;background:#f3f4f6;padding:1px 5px;border-radius:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:250px" title="${escapeHtml(p.pattern)}">${escapeHtml(p.pattern)}</code>
<span style="font-size:11px;color:#9ca3af;margin-left:auto;white-space:nowrap">&rarr; ${escapeHtml(p.redact)}</span> <span style="font-size:11px;color:#9ca3af;margin-left:auto;white-space:nowrap">&rarr; ${escapeHtml(p.redact)}</span>
<button class="btn btn-sm btn-danger btn-remove-secret" data-index="${i}" style="padding:2px 6px">&times;</button> <button class="btn btn-sm btn-danger btn-remove-redact" data-index="${i}" style="padding:2px 6px">&times;</button>
</div> </div>
`).join('')); `).join(''));
// Toggle handlers // Toggle handlers
list.querySelectorAll('.secret-toggle').forEach(toggle => { list.querySelectorAll('.redact-toggle').forEach(toggle => {
toggle.addEventListener('change', async () => { toggle.addEventListener('change', async () => {
const idx = parseInt(toggle.dataset.index, 10); const idx = parseInt(toggle.dataset.index, 10);
const patterns = settings.customSecretPatterns || []; const patterns = settings.customRedactPatterns || [];
patterns[idx].enabled = toggle.checked; patterns[idx].enabled = toggle.checked;
settings.customSecretPatterns = patterns; settings.customRedactPatterns = patterns;
await Storage.saveSettings({ customSecretPatterns: patterns }); await Storage.saveSettings({ customRedactPatterns: patterns });
}); });
}); });
// Remove handlers // Remove handlers
list.querySelectorAll('.btn-remove-secret').forEach(btn => { list.querySelectorAll('.btn-remove-redact').forEach(btn => {
btn.addEventListener('click', async () => { btn.addEventListener('click', async () => {
const idx = parseInt(btn.dataset.index, 10); const idx = parseInt(btn.dataset.index, 10);
const patterns = settings.customSecretPatterns || []; const patterns = settings.customRedactPatterns || [];
patterns.splice(idx, 1); patterns.splice(idx, 1);
settings.customSecretPatterns = patterns; settings.customRedactPatterns = patterns;
await Storage.saveSettings({ customSecretPatterns: patterns }); await Storage.saveSettings({ customRedactPatterns: patterns });
renderCustomSecrets(); renderCustomRedactPatterns();
}); });
}); });
} }
+1 -1
View File
@@ -201,7 +201,7 @@
<strong>Auto Redact</strong> <strong>Auto Redact</strong>
<span class="setting-desc">Automatically redact API keys, tokens, passwords, SSNs, credit cards, and custom patterns</span> <span class="setting-desc">Automatically redact API keys, tokens, passwords, SSNs, credit cards, and custom patterns</span>
</div> </div>
<label class="toggle"><input type="checkbox" id="optSecretScanning" checked><span class="toggle-slider"></span></label> <label class="toggle"><input type="checkbox" id="optAutoRedact" checked><span class="toggle-slider"></span></label>
</div> </div>
<div class="setting-item"> <div class="setting-item">
+12 -12
View File
@@ -1,6 +1,6 @@
import SubstitutionEngine from '../lib/substitution-engine.js'; import SubstitutionEngine from '../lib/substitution-engine.js';
import SmartPatterns from '../lib/smart-patterns.js'; import SmartPatterns from '../lib/smart-patterns.js';
import SecretScanner from '../lib/secret-scanner.js'; import AutoRedact from '../lib/auto-redact.js';
import AutoDetect from '../lib/auto-detect.js'; import AutoDetect from '../lib/auto-detect.js';
import Storage from '../lib/storage.js'; import Storage from '../lib/storage.js';
import SilentSendSync from '../lib/sync.js'; import SilentSendSync from '../lib/sync.js';
@@ -220,7 +220,7 @@ async function initUnlockedUI() {
}); });
// Load options tab settings // Load options tab settings
$('#optSecretScanning').checked = settings.secretScanning !== false; $('#optAutoRedact').checked = settings.autoRedact !== false;
$('#optAutoDetect').checked = settings.autoDetect !== false; $('#optAutoDetect').checked = settings.autoDetect !== false;
$('#optAutoRedact').checked = settings.autoRedactDetected !== false; $('#optAutoRedact').checked = settings.autoRedactDetected !== false;
$('#optHighlights').checked = settings.showHighlights || false; $('#optHighlights').checked = settings.showHighlights || false;
@@ -228,7 +228,7 @@ async function initUnlockedUI() {
// Options tab change handlers // Options tab change handlers
const optHandlers = [ const optHandlers = [
['optSecretScanning', 'secretScanning'], ['optAutoRedact', 'autoRedact'],
['optAutoDetect', 'autoDetect'], ['optAutoDetect', 'autoDetect'],
['optAutoRedact', 'autoRedactDetected'], ['optAutoRedact', 'autoRedactDetected'],
['optHighlights', 'showHighlights'], ['optHighlights', 'showHighlights'],
@@ -713,16 +713,16 @@ function renderTestDiff() {
const smartResult = SmartPatterns.substitute(input, identity); const smartResult = SmartPatterns.substitute(input, identity);
const explicitResult = SubstitutionEngine.substitute(smartResult.text, mappings); const explicitResult = SubstitutionEngine.substitute(smartResult.text, mappings);
const secretResult = SecretScanner.redact(explicitResult.text, settings.customSecretPatterns); const redactResult = AutoRedact.redact(explicitResult.text, settings.customRedactPatterns);
const allReplacements = [ const allReplacements = [
...smartResult.replacements, ...smartResult.replacements,
...explicitResult.replacements, ...explicitResult.replacements,
...secretResult.redactions, ...redactResult.redactions,
]; ];
const finalText = secretResult.text; const finalText = redactResult.text;
if (finalText === input && secretResult.warnings.length === 0) { if (finalText === input && redactResult.warnings.length === 0) {
output.textContent = input; output.textContent = input;
stats.textContent = 'No substitutions detected'; stats.textContent = 'No substitutions detected';
return; return;
@@ -738,8 +738,8 @@ function renderTestDiff() {
`<span class="sub-highlight" title="Was: ${escapeHtml(r.original)} [${r.pattern || r.category}]">${escapedReplaced}</span>` `<span class="sub-highlight" title="Was: ${escapeHtml(r.original)} [${r.pattern || r.category}]">${escapedReplaced}</span>`
); );
} }
// Highlight secret redactions in red // Highlight auto-redactions in red
for (const r of secretResult.redactions) { for (const r of redactResult.redactions) {
const escapedReplaced = escapeHtml(r.replaced); const escapedReplaced = escapeHtml(r.replaced);
html = html.replace( html = html.replace(
escapedReplaced, escapedReplaced,
@@ -750,12 +750,12 @@ function renderTestDiff() {
const smartCount = smartResult.replacements.length; const smartCount = smartResult.replacements.length;
const explicitCount = explicitResult.replacements.length; const explicitCount = explicitResult.replacements.length;
const secretCount = secretResult.redactions.length; const redactCount = redactResult.redactions.length;
const warnCount = secretResult.warnings.length; const warnCount = redactResult.warnings.length;
const parts = []; const parts = [];
if (smartCount > 0) parts.push(`${smartCount} smart`); if (smartCount > 0) parts.push(`${smartCount} smart`);
if (explicitCount > 0) parts.push(`${explicitCount} explicit`); if (explicitCount > 0) parts.push(`${explicitCount} explicit`);
if (secretCount > 0) parts.push(`${secretCount} secrets redacted`); if (redactCount > 0) parts.push(`${redactCount} auto-redacted`);
if (warnCount > 0) parts.push(`${warnCount} warnings`); if (warnCount > 0) parts.push(`${warnCount} warnings`);
// Auto-detect unconfigured PPI in the final text // Auto-detect unconfigured PPI in the final text