When at-rest encryption is enabled, injector.js detects encrypted blobs
in storage, passes empty config to the content script, and waits for a
vault:unlocked broadcast from the background. That broadcast was only
ever triggered when the user explicitly entered their password in the
popup. After a Gist/URL sync import (which writes newly-imported data
in encrypted form), no page ever received the decrypted config, so
substitution silently stopped working.
Two fixes:
1. injector.js: when isLocked is true (encrypted blobs detected), send
vault:request-unlock to the background. If the key is already cached
(e.g. the user authenticated during the sync pull), the background
responds immediately with vault:unlocked containing the decrypted
data. This fixes every new page load after a sync import.
2. service-worker.js: add vault:request-unlock handler that checks
Storage.isLocked() and, if the key is available, reads decrypted
mappings/identity/settings and sends vault:unlocked back to the
requesting tab.
3. options.js: after a successful Gist or URL pull, send vault:unlocked
to the background so it broadcasts decrypted data to all currently-
open tabs immediately, without requiring a page reload.
https://claude.ai/code/session_01QJnEnLfbXKR5FSCQ3Qfs53
Three issues in the Gist pull flow on a browser with no prior data:
1. _getAllData() returned Date.now() as lastModified when ss_lastModified
was unset (fresh install). The timestamp check in pullFromGist then
saw the Gist data as older than local, silently returned imported:false
("Already up to date") without importing anything or prompting for
auth. Fixed by using || 0 so a browser with no data always accepts
remote data as newer.
2. _applyData now persists ss_last_pull_time and ss_last_pull_source so
the last successful pull survives page reloads.
3. The Gist section on the options page now shows "Last pulled: <time>"
alongside the Gist ID on load, so both browsers display their sync
status rather than showing nothing on first open.
https://claude.ai/code/session_01QJnEnLfbXKR5FSCQ3Qfs53
Two bugs prevented sync from working between different browsers
sharing the same GitHub token and encryption password:
1. pullFromGist failed immediately with "No Gist ID stored" on any
browser that had not previously pushed, because ss_gist_id lives
in browser.storage.local (isolated per browser). Fix: when no
local Gist ID is found, search the authenticated user's Gists for
one containing silent-send-sync.json and cache the result.
2. When the pulled data was encrypted with a different salt (each
browser generates its own random salt on setup), pullFromGist
returned needsAuth:true but the UI never set
window.__ssPendingSyncImport, so the auth prompt fell through to
reverifyWithPassword instead of authenticateForSync, and the pull
was never retried after the user entered their password. Fix: set
window.__ssPendingSyncImport before showing the auth prompt for
both Gist pull and custom-URL pull, matching how sync-code import
already handled this flow.
https://claude.ai/code/session_01QJnEnLfbXKR5FSCQ3Qfs53
The fetch/XHR interception correctly checked settings.enabled, but
several other features ignored it — highlights, reveal mode, and
PII auto-detect all continued running after disabling. This adds
settings.enabled checks to the MutationObserver, highlightMatches,
auto-detect input/paste listeners, reveal mode, and cleans up
visual artifacts (highlights, warnings) when the extension is
toggled off.
https://claude.ai/code/session_015TEttQgcq5FALLKLb3uEW8
- options.html/js: Footer version now reads dynamically from manifest via
api.runtime.getManifest().version instead of hardcoded v0.3.0
- document-scanner.js: Increase FlateDecode lookback from 200 to 500 bytes
(real PDF stream dictionaries are often larger than 200 bytes), and support
array form /Filter [/FlateDecode] alongside the scalar form
https://claude.ai/code/session_01ReZUeR1nrYzJeX7fTqwXMw
- 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