- document-scanner.js: Add missing await on _inflateSync() call — FlateDecode
streams were not being decompressed because the async function was called
without await, causing decompressed to hold a Promise instead of data
- content.js, storage.js, options.html, options.js: Gate proper noun / capital
letter detection behind a new detectProperNouns setting (default off); was
previously always-on causing noisy false-positive warnings on AI thinking
output and common capitalized words
- Bump version 0.9.23 → 0.9.25
https://claude.ai/code/session_01ReZUeR1nrYzJeX7fTqwXMw
The old unrevealInElement relied on a WeakMap (originalTexts) to restore
text nodes to their pre-reveal state. This broke when SPA frameworks
(React) re-rendered the DOM while reveal was active — new text nodes
containing real values had no WeakMap entry to restore from, so real
data stayed visible after turning reveal off.
Fix: unrevealInElement now actively reverse-replaces real values back
to their substitute counterparts using the reveal pairs, matching the
same approach revealText uses in the forward direction. This works
regardless of DOM re-renders or streaming content changes.
https://claude.ai/code/session_01NNBEPuXMFGWezJb1f958nL
- Inject document-scanner.js into page world via injector.js (as module,
sets globalThis.DocumentScanner)
- Add FormData interception to fetch hook: scans File/Blob entries through
DocumentScanner.processUpload(), also substitutes string fields
- Add document-scanner.js to web_accessible_resources in both manifests
- Bump version to 0.9.23 in package.json, manifest.json, manifest.firefox.json
https://claude.ai/code/session_01NNBEPuXMFGWezJb1f958nL
Going back to a known-good baseline. This version had:
- Working reveal mode with CSS Highlight API
- Working substitution (fetch + XHR hooks)
- Smart patterns (names, emails, phones, usernames)
- Encryption/sync (password, TOTP, WebAuthn)
- Multiple identity profiles
- Activity log
- Secret scanner
- Auto-detect PII warnings
- Pre-send PII detection
Kept current manifests (UUID, data_collection_permissions, version).
No renames applied — uses original naming (secretScanning, PPI, etc).
Will re-apply renames and new features from this working base.
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
The data-ss-config attribute on the script tag was being removed
(via script.onload) before content.js could read it. Changed approach:
inject config as a separate <script type="application/json" id="ss-config-data">
element that persists in the DOM until content.js reads and removes it.
This eliminates the race condition between script execution and onload
removal. Bump 0.9.20.
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
Restored injector.js from v2.0.14 (commit 9a11896) which:
- Requests decrypted config from background via get:decrypted-config
message instead of passing empty arrays when data is encrypted
- Handles the identity.profiles merge correctly for background responses
- Passes ss_settings directly (not checking _ssLocalEncrypted which
caused settings loss)
Added missing get:decrypted-config message handler to service-worker.js
which returns decrypted mappings, identity, and settings via Storage
module.
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
Reverted content.js to commit 9a11896 (the last version where reveal
mode worked) and re-applied only the renames:
- PPI → PII
- secretScanning → autoRedact
- SECRET_PATTERNS → REDACT_PATTERNS
- scanAndRedactSecrets → runAutoRedact
- category: 'secret' → category: 'redact'
- Added customRedactPatterns support to runAutoRedact
This restores:
- Individual word storage in sessionSubstitutions (needed for reveal)
- fetch(Request) handling (not just fetch(url, options))
- Blob/ArrayBuffer/URLSearchParams body conversion
- isInNonChatArea scoping (narrowed version)
- Proper noun detection gated by settings.detectProperNouns
- Mapping values added to configured skip set
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
The class-based selectors ([class*="header"], [class*="Header"],
[class*="nav-"], etc.) were matching Claude.ai's chat content area
elements, preventing reveal from running on any response text.
Narrowed to only structural elements: nav, aside, [role="navigation"],
[role="complementary"], [data-sidebar]. Removed HEADER/FOOTER from
SKIP_REVEAL_TAGS since sites use these tags inside chat layouts.
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
The smart engine records individual name parts ("Ademo"→"John",
"Demo"→"Smith") AND the combined form ("Ademo Demo"→"John Smith")
in sessionSubstitutions. The catch-all in buildRevealPairs was
adding all of them, causing partial replacements that corrupted
the DOM and made the cache oscillate between 4 and 0 pairs.
Fix: skip session entries whose key is a substring of a longer
entry (e.g. "ademo" is part of "ademo demo"). Only the combined
form gets added as a reveal pair.
Also: remove debug logging, improve cache with size tracking.
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
Root cause found: commit 9a11896 (v2.0.14) added isInNonChatArea()
and expanded SKIP_REVEAL_TAGS with NAV/ASIDE/HEADER/FOOTER, but that
commit was on a branch that was never merged into main. When we started
our work from main, these fixes were missing.
Restored:
- isInNonChatArea() function — prevents reveal from touching sidebars,
navigation, headers, footers, and other non-chat UI
- SKIP_REVEAL_TAGS expanded with NAV, ASIDE, HEADER, FOOTER
- isInNonChatArea checks added to revealInElement, unrevealInElement,
and highlightMatches (both element-level and walker-level)
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
Two bugs found:
1. Individual name parts ("Ademo"→"John", "Demo"→"Smith") were added
as reveal pairs, causing partial replacements that corrupted the DOM.
The smart engine sends combined forms ("Ademo Demo"→"John Smith")
which the catch-all already handles. Removed individual name entries
from buildRevealPairs — only emails, usernames, hostnames, phones
are matched individually.
2. Cache invalidated on every ss:config-updated (including settings-only
changes like reveal toggle). Now only invalidates when mappings or
identity actually change.
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
If reveal mode was saved as ON in settings, the page loaded with
revealMode=true and prevRevealMode=true. The checkRevealToggle
function only fires on transitions (off→on or on→off), so the
reveal interval was never started. Added initialization check
that starts the reveal interval immediately if revealMode is
already true at page load.
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
When at-rest encryption is enabled, storage.onChanged fires with
encrypted blobs for ss_settings. The injector was passing this
encrypted blob directly as settings to the page world content script,
overwriting real settings with { _ssLocalEncrypted: true, data: ... }.
This broke reveal mode, highlights, and any setting toggle because
the content script's settings object became the encrypted blob.
Fix: skip encrypted settings blobs in injector.js (same check already
existed for mappings and identity). The background's settings:updated
message already sends decrypted settings correctly.
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
Bug 1: popup.html had duplicate id="optAutoRedact" on both the
Auto Redact toggle and Auto-redact Detected PII toggle. The second
overwrote the first, making the Auto Redact setting uncontrollable.
Fixed by giving the second toggle id="optAutoRedactDetected".
Bug 2: injector.js hardcoded activity log trim to 100, ignoring the
user's maxLogEntries setting. Now reads the setting from storage.
Added test-suite.html with 35+ tests covering storage, encryption,
sync (encryption-mandatory flows), auto-redact (built-in + custom
patterns), substitution engine, smart patterns, and auto-detect.
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
PII (Personally Identifiable Information) is the correct standard term.
Renamed all instances — comments, UI labels, variable names, function
names (autoDetectPPI → autoDetectPII, scanInputForPPI → scanInputForPII,
ppiWarnings → piiWarnings), CSS comments, README, and options page.
Added Ko-fi donation section to README and Options footer. Tone: no
obligation, no warranty, no influence on updates — just appreciation.
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
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
Reveal mode and highlights were replacing text in sidebars, navigation,
headers, repo names, and other non-chat UI elements. Now skips:
- <nav>, <aside>, <header>, <footer> elements
- Elements with role="navigation", role="banner", role="complementary"
- Elements with class names containing sidebar, nav, menu, header
Added isInNonChatArea() check to all five code paths:
- revealInElement, unrevealInElement, highlightMatches
- MutationObserver addedNodes and characterData handlers
Also:
- Removed footer Options link (redundant with 5th Options tab)
- Updated README: ChatGPT marked as Tested
https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
Accept (+) button:
- Was reading raw storage (getStorageData) which returns encrypted
blobs when encryption is enabled, silently failing to add mappings
- Fixed: adds directly to the local mappings array and persists via
setStorageData (storage bridge handles encryption transparently)
Auto-detect false positives:
- Was only checking identity values, not explicit mappings — values
already in the mappings table still got flagged as unconfigured PPI
- Fixed: now adds all mapping real/substitute values to the skip set
Ignore button:
- Changed from plain text link to grey pill button for better UX
- Still persists permanently via ss_ignored_ppi in storage
https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
When at-rest encryption is enabled, storage.local contains encrypted
blobs. The injector reads raw storage (content script world, no
access to IndexedDB CryptoKey) and sees { _ssLocalEncrypted: true }.
It passed empty config to content.js → no mappings → no substitution.
This is why Firefox stopped working after encryption was enabled.
Chrome/Brave worked because the user hadn't set up encryption there.
Fixed: injector now detects encrypted data and asks the background
script for decrypted config via 'get:decrypted-config' message.
The background uses the Storage module (which has IndexedDB access)
to decrypt and return the data. Falls back to empty config if the
vault is actually locked.
https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
The early hook approach (inline script, then external early-hook.js)
kept breaking Firefox due to CSP restrictions and timing issues with
the async external script load. Each fix for ChatGPT introduced a
new regression for Firefox/Brave.
Reverted to the original simple approach:
- Injector reads storage, injects content.js via <script src="...">
- content.js captures window.fetch at load time and patches it
- No inline scripts, no early hooks, no __ssOriginalFetch globals
This is what worked on Claude.ai across all browsers before the
ChatGPT fix attempts. ChatGPT support may need a different approach
later (possibly using declarativeNetRequest for header-only changes,
or a ChatGPT-specific content script), but it should not break the
core functionality on Claude.ai.
Kept the Request object handling in the fetch interceptor (needed for
some frameworks) but removed all early hook dependencies.
https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
The inline <script> with textContent was blocked by claude.ai's
Content Security Policy on Firefox (Chrome is more permissive).
No fetch interception = no substitution = completely broken on FF.
Fixed by moving the early fetch hook to its own file (early-hook.js)
loaded via <script src="..."> which is CSP-compliant. Added to
web_accessible_resources in both Chrome and Firefox manifests.
Also fixed duplicate 'const api' declaration in injector.js that
would have crashed the content script.
https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
Bug 1 - Reveal mode not working (all browsers):
sessionSubstitutions stored "ademo demo" (full name) as key, but
buildRevealPairs looked up "ademo" and "demo" individually. No match,
no reveal pairs, reveal did nothing. Fixed by also storing individual
words from multi-word replacements so both "ademo demo" AND "ademo"
AND "demo" are in the map.
Bug 2 - Missing await on _handleDecryptedMeta (sync.js):
Two call sites returned the Promise instead of the resolved value.
Downstream code checking decResult.data got undefined. Added await.
Bug 3 - Profile selector broken by safeHTML (popup.js):
DOMParser.parseFromString wraps content in <html><body> which
mangles <option> elements when moved to a <select>. Replaced with
new Option() DOM API which creates proper option elements.
https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
The early fetch hook was still inside the async init() function,
running AFTER await storage.local.get(). By the time storage
responded, ChatGPT's JS had already loaded and captured the
original fetch().
Moved the inline script injection to the TOP of the IIFE, before
any async operations. The sequence is now:
1. [synchronous] Inject inline <script> that captures fetch/XHR
2. [synchronous] Define mergeProfiles and other helpers
3. [async] Read storage for config
4. [async] Inject content.js with full substitution engine
This guarantees the fetch proxy is installed before any page
JavaScript runs, regardless of how long storage reads take.
https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
The fetch interceptor was loaded via <script src="content.js"> which
is asynchronous. ChatGPT's Next.js framework stores a reference to
the original fetch() during module initialization — before our script
finishes downloading. By the time content.js patches window.fetch,
ChatGPT is already using its stored copy of the original.
Fixed with a two-step injection:
1. Injector injects a tiny INLINE <script> (synchronous, instant)
that stores the real fetch/XHR references and installs a thin
proxy. This runs before ANY page JavaScript.
2. Content.js loads normally, uses the stored __ssOriginalFetch
reference, and registers __ssInterceptFetch so the proxy can
route future calls through the full substitution engine.
This ensures the fetch hook is in place before frameworks like
Next.js, React, or any SPA framework can save a reference to
the original fetch().
https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
Sync operations (browser sync, Gist, custom URL, sync code) now refuse to
operate without encryption enabled. Disabling encryption also turns off all
active sync channels. Activity log cap reduced from 200 to 100 entries for
both storage and display.
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
ChatGPT (and potentially other AI services) calls fetch() with a
Request object as the first argument: fetch(new Request(url, opts))
instead of fetch(url, opts). The interceptor only handled the second
form, so ChatGPT's conversation requests passed through unmodified.
Fixed by handling both fetch signatures:
- fetch(url, options) — existing path
- fetch(Request) — new: extracts URL, method, headers, reads body
via request.text() for JSON/text content types
Also handles non-string body types:
- Blob → text via blob.text()
- ArrayBuffer → text via TextDecoder
- URLSearchParams → string via toString()
These cover the various ways modern frameworks call fetch().
https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
Proper noun detection (flagging capitalized phrases like "Getting
Started", "Generate Design", "Introduction Getting Started") now
disabled by default. Enable via popup → Options tab → Detect proper
nouns.
The pattern-based detection (IPs, emails, API keys, addresses, paths,
etc.) remains always-on and reliable. The capitalized phrase heuristic
was producing too many false positives on normal UI phrases, headings,
and instructions — adding words to the filter was a losing game.
Added detectProperNouns toggle to popup Options tab.
Updated README to note it's opt-in.
https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
Fixed safeHTML in all three files — template.content.childNodes and
doc.body.childNodes are live NodeLists that shrink as nodes are moved.
Using Array.from() to create a static copy before spreading into
replaceChildren().
Expanded proper noun common words filter with ~500 additional verbs,
nouns, and adjectives (generate, design, manage, process, account,
button, dashboard, etc.) to prevent false PPI flags on titles,
headings, and UI button labels.
https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn