feat: custom secret patterns + rename Secret Scanner → Auto Redact

Users can now define custom regex patterns for proprietary token formats,
internal URLs with keys, or any secret the built-in scanner doesn't cover.
Patterns are added/toggled/removed from the Options page and apply to both
the live interception (content.js) and the Test tab (popup.js).

Renamed all user-facing "Secret scanning" labels to "Auto Redact" across
popup and options. Internal variable names (secretScanning, SecretScanner)
kept for backwards compatibility with stored settings.

https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
This commit is contained in:
Claude
2026-03-28 14:32:57 +00:00
parent c8673d5b1c
commit 7ec058f1ea
9 changed files with 175 additions and 22 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
* Silent Send - Auto-Detect
*
* Scans text for potential PPI that the user hasn't configured.
* This catches things the identity and secret scanner can't —
* This catches things the identity and auto-redact scanner can't —
* because the user forgot or didn't know to configure them.
*
* Returns warnings (not auto-redactions) so the user can decide.
+2 -2
View File
@@ -198,9 +198,9 @@ const OrgPolicy = {
},
/**
* Get org-required secret scanner patterns.
* Get org-required auto-redact patterns.
*
* @returns {Array} additional patterns to add to the secret scanner
* @returns {Array} additional patterns to add to auto-redact
*/
async getOrgSecretPatterns() {
const policy = await this.getPolicy();
+36 -7
View File
@@ -1,11 +1,13 @@
/**
* Silent Send - Secret Scanner
* Silent Send - Auto Redact (Secret Scanner)
*
* Detects common secret/credential patterns in text and either
* warns or auto-redacts them. This catches things the identity-based
* smart patterns can't: API keys, tokens, passwords, SSNs, credit
* cards, private keys, connection strings, etc.
*
* Supports user-defined custom patterns for proprietary token formats.
*
* Each pattern has:
* - name: human-readable label
* - regex: detection pattern
@@ -163,12 +165,37 @@ const SECRET_PATTERNS = [
const SecretScanner = {
/**
* Scan text for secrets. Returns list of findings.
* Build the full pattern list (built-in + custom).
* Custom patterns come from settings.customSecretPatterns.
*/
scan(text) {
const findings = [];
_buildPatterns(customPatterns) {
const all = [...SECRET_PATTERNS];
if (Array.isArray(customPatterns)) {
for (const cp of customPatterns) {
if (!cp.enabled || !cp.pattern) continue;
try {
all.push({
name: cp.name || 'Custom Pattern',
regex: new RegExp(cp.pattern, 'g'),
redact: cp.redact || '[REDACTED-CUSTOM]',
severity: 'critical',
});
} catch { /* invalid regex — skip */ }
}
}
return all;
},
for (const pattern of SECRET_PATTERNS) {
/**
* Scan text for secrets. Returns list of findings.
* @param {string} text
* @param {Array} [customPatterns] — from settings.customSecretPatterns
*/
scan(text, customPatterns) {
const findings = [];
const patterns = this._buildPatterns(customPatterns);
for (const pattern of patterns) {
// Reset regex lastIndex
pattern.regex.lastIndex = 0;
let match;
@@ -204,9 +231,11 @@ const SecretScanner = {
/**
* Redact all critical secrets in text. Warnings are not auto-redacted.
* Returns { text, redactions[] }
* @param {string} text
* @param {Array} [customPatterns] — from settings.customSecretPatterns
*/
redact(text) {
const findings = this.scan(text);
redact(text, customPatterns) {
const findings = this.scan(text, customPatterns);
const redactions = [];
let result = text;
+1
View File
@@ -40,6 +40,7 @@ const DEFAULT_SETTINGS = {
autoAddDetected: true,
maxLogEntries: 100,
customDomains: [],
customSecretPatterns: [],
categories: ['name', 'email', 'phone', 'address', 'ssn', 'dob', 'domain', 'password', 'general'],
browserSync: false,
};