Commit Graph
100 Commits
Author SHA1 Message Date
Claude b9acd6d677 Remove claude.ai/code skip
The extension needs to work on claude.ai/code like every other site;
the user toggles it off manually when needed. Dropping the host-level
skip leaves the word-boundary mapping fix as the only behavior change
on this branch.

https://claude.ai/code/session_01Y2YprLMx348eWD5C9Z4zpV
2026-04-16 18:10:04 +00:00
Claude f55f63c254 Revert claude.ai/code to blanket skip
Drops the narrow field-based walker. Matching only known user-input
fields is brittle: the moment Anthropic renames prompt/input/messages
the walker silently stops substituting and real data leaks.

Preferring robustness over convenience here — the extension stays
dormant on claude.ai/code regardless of future API shape changes, and
users handle redaction manually on that one app. The word-boundary
mapping fix from the previous commit is unaffected.

https://claude.ai/code/session_01Y2YprLMx348eWD5C9Z4zpV
2026-04-16 18:03:36 +00:00
Claude 331b1cb2a2 Narrow claude.ai/code interception to user-input fields
Replaces the blanket skip on claude.ai/code with a targeted walker so
the user's prompt and pasted attachments still get substituted, but
tool_use / tool_result / system / IDs / metadata pass through verbatim.

Fields touched:
- top-level prompt and input (classic claude.ai shape)
- attachments[].extracted_content and attachments[].file_name
- messages[].content (or text parts) when role === 'user'

FormData uploads continue to flow through DocumentScanner since they
are user input. Raw non-JSON bodies are skipped on this path because
they are typically tool traffic, not user text.

https://claude.ai/code/session_01Y2YprLMx348eWD5C9Z4zpV
2026-04-16 17:58:48 +00:00
Claude 16c8275f4e Skip claude.ai/code and use word boundaries for mappings
Two user-reported conflicts when using the extension alongside Claude
Code on the web and with short mappings:

1. Claude Code (claude.ai/code) sends real file paths, usernames, and
   shell commands to its tool runtime. Substituting any of these
   corrupts execution, forcing users to disable the extension for that
   app. early-hook.js and injector.js now short-circuit on that path so
   fetch/XHR are never wrapped and no page-world script is injected.

2. Explicit mappings used raw literal regexes, so "not" -> "bad" would
   rewrite "nothing" into "badhing". substitute/reveal/scan/diff (and
   their page-world mirrors) now add \b on word-character edges only,
   leaving non-word edges like "@foo" unchanged so they keep matching.

https://claude.ai/code/session_01Y2YprLMx348eWD5C9Z4zpV
2026-04-16 16:12:04 +00:00
Claude a69c39e8e7 Bump version to 0.9.46
https://claude.ai/code/session_01RfzvB5sHah326acr8Xa7Jn
2026-04-04 15:22:27 +00:00
Claude fb237c91a8 Fix substitute values being re-detected as PII after adding mapping
Two issues fixed:

1. autoDetectPII only skipped identity values (names, emails, etc.) but not
   mapping values. After clicking "+" to map e.g. an IP address, the fake
   substitute IP was immediately re-detected as PII, causing the notification
   to reappear. Now autoDetectPII accepts opts.mappings and adds both real
   and substitute values to the skip set.

2. The local mappings array was only updated after the async background
   round-trip completed. The 150ms re-scan could fire before that, missing
   the new mapping. Now an optimistic temp mapping is added to the local
   array immediately so the re-scan already knows to skip both values.

https://claude.ai/code/session_01RfzvB5sHah326acr8Xa7Jn
2026-04-04 15:18:51 +00:00
Claude d8a4e3a668 Bump manifest.firefox.json version to 0.9.45
https://claude.ai/code/session_01RfzvB5sHah326acr8Xa7Jn
2026-04-04 14:24:52 +00:00
Claude 36b7bf34e9 Bump version to 0.9.45
https://claude.ai/code/session_01RfzvB5sHah326acr8Xa7Jn
2026-04-04 14:21:00 +00:00
Claude 909bb3a847 Fix PII mapping add (plus button) not persisting and not dismissing notification
The plus button handler in the pre-send PII warning had two bugs:

1. It read/wrote mappings directly via the storage bridge (api.storage.local),
   bypassing the Storage module's encryption layer. When at-rest encryption was
   enabled, getStorageData returned an encrypted blob instead of an array,
   causing .push() to throw a TypeError that silently aborted the handler —
   the mapping was never saved and replaceInInput never ran.

2. Unlike the ignore button which immediately removes the DOM item, the plus
   button relied on a re-scan at 150ms to dismiss the notification. If
   replaceInInput didn't stick (e.g. React-controlled inputs), the re-scan
   found PII again and the notification persisted.

Fix: Route mapping creation through the background service worker via a new
'add:mapping' message handler (which uses Storage.addMapping with proper
encryption support), and immediately dismiss the notification item from the
DOM like the ignore button does.

https://claude.ai/code/session_01RfzvB5sHah326acr8Xa7Jn
2026-04-04 14:14:15 +00:00
Claude 44f9d9444c Fix encryption enable by removing broken org UI references, bump to 0.9.44
Removed Organization section JS (initOrgUI, showOrgJoined, showOrgNotJoined,
setOrgStatus) and OrgPolicy import from options.js. These referenced DOM
elements deleted in a prior commit, causing DOMContentLoaded init to fail
before the encryption button listener could be registered.

https://claude.ai/code/session_01CwcZK8nqL8pyBH9AxDs9qo
2026-04-02 13:55:04 +00:00
Claude 450688a787 Bump version to 0.9.43
https://claude.ai/code/session_01CwcZK8nqL8pyBH9AxDs9qo
2026-04-02 13:27:01 +00:00
Claude 617a2d10eb Wrap fetch interceptor in top-level try/catch to prevent breaking page requests
If anything in Silent Send's substitution logic throws (bad mapping,
regex error, unexpected body format, etc.), the exception was propagating
up to the caller instead of falling through to the original fetch. This
broke Claude Code's web interface (claude.ai/code) when a bad mapping
caused a substitution error mid-request.

The inner try/catch on JSON.parse only covered the JSON path — the outer
logic (hasSubstitutions, processBody, notifySubstitutions, etc.) had no
protection. Added a top-level try/catch that catches any unhandled error
and falls through to originalFetch, logging a warning so the error is
still visible in the console.

https://claude.ai/code/session_01CwcZK8nqL8pyBH9AxDs9qo
2026-04-02 13:23:33 +00:00
Claude 3d8642db52 Fix whitespace-only mappings highlighting every space on the page
A mapping imported with real: " " (single space) passed the !m.real
guard (space is truthy), producing new RegExp(" ", "gi") which matched
every space character in the DOM and highlighted them all.

- Added .trim() to all real/substitute guard conditions in content.js
  (inline substitute, reveal functions) and substitution-engine.js so
  whitespace-only values are treated as empty and skipped
- Added the same guard in highlightMatches so the CSS Highlight API
  never creates ranges for blank search terms
- Added trim + blank-check to the bulk import path in options.js so
  whitespace-only real values are dropped at import time rather than
  saved to storage

https://claude.ai/code/session_01CwcZK8nqL8pyBH9AxDs9qo
2026-04-02 13:19:14 +00:00
Claude abeb79a93f Wrap long mapping values instead of truncating with ellipsis
https://claude.ai/code/session_01CwcZK8nqL8pyBH9AxDs9qo
2026-04-02 12:12:08 +00:00
Claude 0b4fbe1efc Remove Organization section; fix mappings table column layout
- Removed the Organization section from options page (unused feature)
- Fixed mapping table columns spreading Real Value and Substitute to
  opposite ends of the row. Added table-layout: fixed with explicit
  column widths (35/35/12/8/6/4%) so columns are evenly distributed.
  Added text-overflow: ellipsis on td so long values truncate cleanly.

https://claude.ai/code/session_01CwcZK8nqL8pyBH9AxDs9qo
2026-04-02 12:09:00 +00:00
Claude deacfd5907 Bump version to 0.9.42
https://claude.ai/code/session_01CwcZK8nqL8pyBH9AxDs9qo
2026-04-02 00:39:49 +00:00
Claude e93e27012e Fix fresh-install pull blocked by saveSettings timestamp
After a browser restart with no extension data, enabling encryption calls
saveSettings() which writes ss_lastModified = Date.now(). All pull paths
then compare that fresh timestamp against the remote's older timestamp,
see local >= remote, and skip the pull with "already on latest version" —
even though local is completely empty.

Fix: add _hasRealLocalData() which returns true only if local storage
contains actual identity profiles or mappings with real values. The
timestamp skip condition now requires BOTH a newer local timestamp AND
real local data. An empty/fresh install always falls through to pull.

Affects pullFromGist, pullFromUrl, pullFromSyncStorage, and importSyncCode.

https://claude.ai/code/session_01CwcZK8nqL8pyBH9AxDs9qo
2026-04-01 23:45:10 +00:00
Claude dbf178937f Bump version to 0.9.41
https://claude.ai/code/session_01CwcZK8nqL8pyBH9AxDs9qo
2026-04-01 20:04:51 +00:00
Claude 1b224fb233 Add Ignore button to auto-detect warnings for permanent per-value dismissal
The × close button only hid the warning for the current send — the same
detection reappeared on every subsequent send with no way to silence it.

Added an Ignore button to each item in both the on-send warning
(showAutoDetectWarning) and the pre-send input scanner warning
(showPreSendWarning). Clicking Ignore:
- Adds the specific value to an in-memory Set (ignoredDetections)
- Persists it to ss_ignored_detections in storage
- Removes that item from the current warning; closes the popup if empty

On page load, ss_ignored_detections is read from storage so ignores
survive page reloads. Ignored values are also filtered before
auto-redaction in the fetch interceptor, so they are not redacted either.

https://claude.ai/code/session_01CwcZK8nqL8pyBH9AxDs9qo
2026-04-01 18:25:13 +00:00
Claude ed8d094320 Bump version to 0.9.40
https://claude.ai/code/session_01CwcZK8nqL8pyBH9AxDs9qo
2026-04-01 16:08:30 +00:00
Claude ed92b07703 Add grok.com; fix fetch hook on React/Next.js sites (Reddit, Grok, etc.)
Two fixes:

1. grok.com missing from manifest
   Grok moved from grok.x.ai to grok.com. The extension wasn't activating
   on grok.com at all (no content script injected). Added to host_permissions
   and content_scripts in both manifests.

2. Fetch hook bypassed on React/Next.js sites
   content.js was injected async (after awaiting the document-scanner),
   so by the time it replaced window.fetch, React/Next.js had already stored
   a reference to the original. All fetch calls from framework code bypassed
   the hook entirely.

   Fix: load early-hook.js as a "world": "MAIN" content script at
   document_start. This runs synchronously before any page JavaScript,
   captures the real fetch in window.__ssOriginalFetch, and replaces
   window.fetch with a lightweight wrapper. When content.js eventually
   loads, it sets window.__ssInterceptFetch (the real substitution logic)
   and window.__ssReady = true, activating the wrapper. React's stored
   fetch reference now routes through the interceptor.

   Falls back to direct window.fetch replacement if early-hook.js somehow
   didn't run (e.g. older browser without world: MAIN support).

https://claude.ai/code/session_01CwcZK8nqL8pyBH9AxDs9qo
2026-04-01 16:07:01 +00:00
Claude 9af92a24aa Bump version to 0.9.39
https://claude.ai/code/session_01CwcZK8nqL8pyBH9AxDs9qo
2026-04-01 14:56:40 +00:00
Claude 1c4b8b0cdd Fix race condition: vault:unlocked message lost when service worker responds before content.js loads
Two related races caused substitution to silently fail with at-rest encryption:

1. vault:request-unlock was sent at the top of init(), but the runtime.onMessage
   listener that handles the vault:unlocked response was registered only after
   await-ing the document-scanner script. A warm service worker (e.g. freshly
   woken by a sync operation) could respond before the listener existed, dropping
   the message permanently.

2. Even if the listener was registered in time, it posted to window immediately,
   but content.js hadn't been injected yet, so its window.addEventListener('message')
   handler wasn't live and the message went nowhere.

Fix: register api.runtime.onMessage before injecting content.js, and move the
vault:request-unlock send into script.onload — by that point content.js has fully
executed and its message listener is live.

Also stop passing the encrypted settings blob as initial config. When isLocked,
ss_settings is { _ssLocalEncrypted: true, data: '...' }; passing it to content.js
as the initial settings object clutters the settings with encrypted garbage.
Now falls back to { enabled: true } like mappings/identity already did.

https://claude.ai/code/session_01CwcZK8nqL8pyBH9AxDs9qo
2026-04-01 14:48:19 +00:00
Claude b3dc4f7abd Bump version to 0.9.34
https://claude.ai/code/session_01QJnEnLfbXKR5FSCQ3Qfs53
2026-04-01 04:56:22 +00:00
Claude 7028f60b56 Fix fresh-install pull skipping due to empty-profile timestamp pollution
Root cause: when the popup opens for the first time it calls
addProfile('Personal') + updateProfile(...) to create a default empty
profile. Both call saveProfiles, which was unconditionally setting
ss_lastModified: Date.now(). This made the new browser's local
timestamp look like right now — newer than any Gist data pushed by
the source browser — so every pull returned "Already up to date"
without ever prompting for a password or importing anything.

Fixes:

1. storage.js saveProfiles: only advance ss_lastModified when at
   least one profile contains real PII (non-empty real value in
   names, emails, usernames, phones, or catchAllEmail). Creating the
   default empty profile structure on first install leaves
   ss_lastModified at 0 so pulls correctly see remote data as newer.

2. sync.js pushToGist: persist ss_last_push_time and
   ss_last_push_source alongside ss_gist_id so the source browser
   (which only pushes) can also show its last activity time.

3. options.js: display both "Pushed: <time>" and "Pulled: <time>"
   in the Gist status area on page load, giving both browsers
   meaningful feedback.

4. service-worker.js: after a successful auto-sync pull, broadcast
   vault:unlocked to all open content-script tabs so substitution
   works immediately without a page reload.

https://claude.ai/code/session_01QJnEnLfbXKR5FSCQ3Qfs53
2026-04-01 04:53:12 +00:00
Claude 8b6f779a07 Prevent empty-browser auto-sync from clobbering Gist data
On a fresh browser install with auto-sync enabled, performAutoSync
would push because config.lastPush was null (!config.lastPush = true).
With _getAllData now returning lastModified: 0 for browsers with no
saved data, this pushed a payload with lastModified: 0 to the Gist,
overwriting the real data from the source browser. Subsequent pulls
then saw remoteMod (0) <= local (0) and returned "Already up to date"
without ever prompting for a password or importing anything.

Three fixes:

1. performAutoSync: add local.lastModified > 0 guard to the push
   condition so a browser with no saved data never pushes in the
   background (both Gist and URL paths).

2. pushToGist / pushToUrl: return an explicit error if lastModified
   is 0, preventing a manual Push click on a fresh browser from
   clobbering the Gist too.

3. pullFromGist: if the Gist's lastModified is 0 (already clobbered),
   return a clear error message telling the user to push from the
   source browser first, rather than silently returning "up to date".

https://claude.ai/code/session_01QJnEnLfbXKR5FSCQ3Qfs53
2026-04-01 04:43:52 +00:00
Claude 64f24f28f1 Bump version to 0.9.33
https://claude.ai/code/session_01QJnEnLfbXKR5FSCQ3Qfs53
2026-04-01 04:30:39 +00:00
Claude 9f763ba0ec Fix substitution breaking after sync import with at-rest encryption
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
2026-04-01 04:27:26 +00:00
Claude 0997f7a8e4 Fix pull silently skipping on fresh browser install; show last pull time
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
2026-04-01 04:11:43 +00:00
Claude 6a962e927b Add .version-bump-undo to .gitignore
Local scratch file created by bump-version.sh; no need to track it.

https://claude.ai/code/session_01QJnEnLfbXKR5FSCQ3Qfs53
2026-03-31 21:21:16 +00:00
Claude 8539c7ba9b Bump version to 0.9.32
https://claude.ai/code/session_01QJnEnLfbXKR5FSCQ3Qfs53
2026-03-31 21:20:46 +00:00
Claude 3420299f51 Fix cross-browser GitHub Gist sync when encryption is enabled
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
2026-03-31 20:55:12 +00:00
Claude bb5d9d3837 Bump version to 0.9.31
https://claude.ai/code/session_015TEttQgcq5FALLKLb3uEW8
2026-03-30 22:48:36 +00:00
Claude f021e50c08 Fix extension not fully disabling when toggled off
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
2026-03-30 22:26:58 +00:00
Claude 4968b4cde2 Bump version to 0.9.30 2026-03-30 15:54:48 +00:00
Claude 90b0a04a9c Bump version to 0.9.29 2026-03-30 14:24:33 +00:00
Claude d35926083f Bump version to 0.9.28 2026-03-30 13:57:56 +00:00
Claude 74df048cef Bump version to 0.9.28 2026-03-30 13:56:49 +00:00
Claude 1390e3d245 Bump version to 0.9.26
https://claude.ai/code/session_01ReZUeR1nrYzJeX7fTqwXMw
2026-03-29 21:36:51 +00:00
Claude bcce37483e Fix footer version, improve PDF filter detection
- 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
2026-03-29 21:32:49 +00:00
Claude 4b494b5eab Fix PDF decompression, add proper noun detection toggle, bump to 0.9.25
- 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
2026-03-29 21:09:49 +00:00
Claude 490b2ebf33 Fix reveal mode not restoring substitute values on toggle off
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
2026-03-29 19:56:57 +00:00
Claude 4fa607d53f Wire up document scanner for file upload interception, bump to 0.9.23
- 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
2026-03-29 19:49:04 +00:00
Claude bc1492032d Fix PPI → PII terminology across entire codebase
All references to "PPI" (Personal Private Information) have been
corrected to the industry-standard "PII" (Personally Identifiable
Information). This includes UI labels, comments, CSS class comments,
and variable/function names (PPI_PATTERNS → PII_PATTERNS,
autoDetectPPI → autoDetectPII, scanInputForPPI → scanInputForPII,
ppiWarnings → piiWarnings).

Files changed: README.md, content.css, content.js, auto-detect.js,
org-policy.js, storage.js, options.html, popup.html, popup.js

https://claude.ai/code/session_01NNBEPuXMFGWezJb1f958nL
2026-03-29 19:32:02 +00:00
Claude 2719e1be44 revert: restore entire src/ from pre-doc-scanner commit (e4b44a7)
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
2026-03-29 18:45:10 +00:00
Claude 740f692ff1 fix: config not reaching content.js — race condition with script removal
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
2026-03-29 18:25:28 +00:00
Claude b622d5abd4 fix: restore working injector.js + add missing get:decrypted-config handler
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
2026-03-29 18:06:51 +00:00
Claude 2a1bd88dd6 fix: restore working content.js from v2.0.14 with renames applied
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
2026-03-29 17:55:54 +00:00
Claude fe684047a8 fix: isInNonChatArea too aggressive — blocking reveal on chat content
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
2026-03-29 17:50:25 +00:00
Claude 433e5a7736 fix: filter out partial name substitutions from reveal pairs
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
2026-03-29 17:35:39 +00:00
Claude 3ae740216b fix: restore missing isInNonChatArea + SKIP_REVEAL_TAGS from unmerged branch
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
2026-03-29 17:28:46 +00:00
Claude 5e47ed5aae fix: reveal pairs oscillating between populated and empty
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
2026-03-29 17:20:48 +00:00
Claude e9faf1df8e debug: add reveal pairs logging to diagnose empty pairs issue, bump 0.9.13
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-29 17:10:18 +00:00
Claude 9cb582652f chore: bump version to 0.9.12
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-29 16:52:34 +00:00
Claude d494ebaf8c fix: reveal mode not working when already ON at page load
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
2026-03-29 16:49:42 +00:00
Claude 65d60cfbe0 fix: new unique extension UUID to avoid duplicate ID on AMO, bump 0.9.10
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-29 04:05:54 +00:00
Claude 281e1a1d08 fix: data_collection_permissions.required must be array ["none"]
Per Mozilla docs, required is an array of data type strings.
For extensions that collect no data: ["none"].
Source: https://extensionworkshop.com/documentation/develop/firefox-builtin-data-consent/

https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-29 04:02:31 +00:00
Claude 75f1dd7aca fix: data_collection_permissions requires 'required' property, bump min version to 140
AMO expects { required: false } not { collect_user_data: false }.
Bumped strict_min_version to 140.0 (when data_collection_permissions
was introduced). Version 0.9.8.

https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-29 03:59:29 +00:00
Claude e759b5f1b4 fix: move data_collection_permissions under browser_specific_settings.gecko
AMO requires the property at browser_specific_settings/gecko/data_collection_permissions,
not at the manifest root. Bump 0.9.7.

https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-29 03:57:34 +00:00
Claude 85c71631eb fix: add data_collection_permissions for AMO validation, bump 0.9.6
Mozilla now requires data_collection_permissions in the manifest.
Set collect_user_data: false (Silent Send collects no user data).

https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-29 03:41:44 +00:00
Claude 072511eb7a chore: bump version to 0.9.5
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-29 01:26:52 +00:00
Claude d8784e8f75 fix: reveal mode broken when encryption enabled — encrypted settings blob passed to content script
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
2026-03-29 00:53:17 +00:00
Claude 002a67ba95 license: switch from BSL-1.1 to MIT
MIT is true open source (OSI-approved) and provides the same liability
protection through its warranty disclaimer. The strengthened disclaimer
covering silent third-party failures, regulatory non-compliance, and
user verification responsibility is retained in the LICENSE file.

BSL restricted commercial use but didn't add legal protection — the
liability limitation is what protects against lawsuits, and that works
the same under MIT.

https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-28 21:09:14 +00:00
Claude d3646fbdf3 fix: move test suite JS to external file for Firefox CSP, bump 0.9.3
Firefox extension CSP blocks inline <script> tags. Moved all test
code to test-suite.js (loaded via src attribute). Build script updated
to copy both files.

https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-28 18:37:58 +00:00
Claude a2b598236c chore: bump version to 0.9.2
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-28 18:20:21 +00:00
Claude ed9419f04b fix: test suite CSP compatibility for Firefox extension pages
Replace inline onclick handlers with addEventListener (Firefox
extension CSP blocks inline event handlers). Show loading state
and test count on page load.

https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-28 18:13:17 +00:00
Claude 9ecd532a71 fix: include test-suite.html in build output
build.sh was not copying test-suite.html to dist/, causing "File not
found" when opening it as an extension page.

https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-28 17:07:45 +00:00
Claude 7c3c2ba3ae merge: integrate claude/read-repo-YMu21 branch
Merged changes from the other session:
- Ko-fi support link in options footer
- PPI → PII renaming across codebase
- Legal/licensing updates (stronger disclaimer, non-commercial only)
- Custom secret/auto-redact patterns
- Internal refactoring of secret scanner to auto-redact naming
- Custom domains in popup Options tab
- Additional AI services (Perplexity, DeepSeek, HuggingChat, Poe)
- Developer sites (GitHub, GitLab, Reddit, StackOverflow, Pastebin)
- Dynamic content script registration for custom domains

Resolved conflicts in options.html and popup.html (kept newer
"Auto Redact" naming with custom patterns description).

https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
2026-03-28 16:39:27 +00:00
Claude ccf69a91a7 fix: duplicate optAutoRedact ID + hardcoded log limit + add test suite
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
2026-03-28 16:36:57 +00:00
Claude 5ce639f40f docs: update support section wording
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-28 16:28:05 +00:00
Claude ad3b8718fa fix: PPI → PII across entire codebase + add Ko-fi support section
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
2026-03-28 16:21:21 +00:00
Claude 22205c2590 legal: remove commercial licensing, non-commercial use only
Commercial use is no longer permitted under the license. The BSL-1.1
still converts to MIT on March 26, 2030. All disclaimer and liability
language retained — applies to all users regardless.

https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-28 15:59:20 +00:00
Claude f237139e80 legal: strengthen disclaimer, liability, and warranty language
- LICENSE: expanded warranty disclaimer covering silent failures from
  third-party changes, coverage gaps, regulatory non-compliance, and
  commercial licensee expectations. Explicit limitation of liability
  for privacy breaches, identity theft, and regulatory penalties.
- PRIVACY.md: added limitations section covering third-party changes,
  coverage gaps, user responsibility, and compliance disclaimer.
- README.md: detailed disclaimer with specific scenarios — site API
  changes, coverage gaps, user verification responsibility, and
  commercial license scope.
- Popup: expanded footer warning about third-party changes and user
  responsibility.
- Options page: added footer disclaimer with LICENSE link, updated
  version to 0.9.0.

https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-28 15:30:45 +00:00
Claude 4b478c71bd chore: set version to 0.9.0 for pre-release testing
https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-28 15:17:20 +00:00
Claude 33d1542d12 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
2026-03-28 14:59:59 +00:00
Claude 7ec058f1ea 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
2026-03-28 14:32:57 +00:00
Claude eb2e946673 feat: bump-version.sh v2 — preview, confirm, undo, profiles, help
Complete rewrite with safety features:
- Version profiles: groups files by version, only updates the current
  version (ignores unrelated X.Y.Z strings in the project)
- Preview: shows exact line-by-line diff before applying changes
- Confirmation: asks y/N before modifying files (--yes to skip)
- Undo: saves rollback info, ./bump-version.sh undo to revert
- --help: full usage documentation with examples
- Only matches version DECLARATIONS (requires keyword context like
  "version":, version=, VERSION=, v prefix) — won't match random
  numbers like dates, IPs, or port numbers

https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
2026-03-28 12:43:32 +00:00
Claude 863e637642 feat: make bump-version.sh truly project-agnostic
Now supports all common version patterns:
- JSON ("version": "X.Y.Z") — Node, extensions, composer
- TOML (version = "X.Y.Z") — Rust, Python pyproject.toml
- YAML (version: X.Y.Z) — Helm, GitHub Actions
- Python (__version__ = "X.Y.Z") — packages
- PHP (Version: X.Y.Z) — WordPress plugins/themes
- Shell (VERSION="X.Y.Z") — scripts
- HTML (vX.Y.Z) — display footers
- XML — .csproj, plist

Skips node_modules, dist, build, .git, __pycache__, venv, vendor.
Skips itself to avoid matching usage examples.
Skips files over 500KB to avoid scanning binaries.

https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
2026-03-28 12:30:13 +00:00
Claude e8746babe4 feat: add version bump script (bump-version.sh)
Agnostic version bump tool that finds all version strings in the
project and updates them together.

Usage:
  ./bump-version.sh          # interactive menu
  ./bump-version.sh patch    # 0.9.0 → 0.9.1
  ./bump-version.sh minor    # 0.9.0 → 0.10.0
  ./bump-version.sh major    # 0.9.0 → 1.0.0
  ./bump-version.sh 1.2.3    # set to specific version

Finds versions in JSON files ("version": "X.Y.Z") and HTML/JS
files (vX.Y.Z). Excludes node_modules and dist directories.

https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
2026-03-28 12:23:31 +00:00
Claude 9da922f662 chore: set version to 0.9.0 (beta)
Not a fully tested release. Marking as beta until all services
are verified and edge cases are resolved.

https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
2026-03-28 12:00:10 +00:00
Claude fa36a1f977 feat: add Copilot + Edge support, update README
Added copilot.microsoft.com to host_permissions, content_scripts
matches, and BUILTIN_URL_PATTERNS in both Chrome and Firefox manifests
plus the service worker.

Updated README:
- Added Copilot to supported services table
- Added Edge and Brave to browser list
- Added note about desktop apps (can't intercept, use web version)
- Removed old duplicate browser note

https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
2026-03-27 23:15:03 +00:00
Claude b0fce541f3 chore: rename 'Secret Scanner' to 'Auto-redact' in all UI
'Secret Scanner' described what it looks for. 'Auto-redact' describes
what it does — clearer for users. Renamed in popup, options page,
org section, and README. Internal code (secret-scanner.js) unchanged
to avoid breaking references.

https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
2026-03-27 22:49:43 +00:00
Claude a9c5f9b43f fix: Options tab broken — removed footer link crashed init
Removing the footer <a id="btnOptions"> made $('#btnOptions') return
null, causing addEventListener to throw. This killed the rest of
initUnlockedUI(), preventing the Options tab and all subsequent
handlers from being wired up.

https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
2026-03-27 22:25:15 +00:00
Claude c8673d5b1c feat: frictionless custom domain management
- Edit domains inline (pencil icon, saves on Enter)
- Suggested domains: clickable chips for popular AI/dev/collab sites
- Bulk add: paste multiple domains at once (one per line or comma-separated)
- Popup domain management: add/remove/suggestions directly from the popup
- Permission revoke on removal + suggested list updates dynamically

https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-27 21:32:31 +00:00
Claude e6e9894d99 feat: add AI services and developer/support sites to built-in domains
New AI sites: Perplexity, Copilot, DeepSeek, HuggingChat, Poe
Developer/support: GitHub, GitLab, Reddit (www + old), Stack Overflow, Pastebin

The existing interception is service-agnostic — it scans all JSON strings
in POST/PUT/PATCH requests through the 4-stage substitution pipeline.
No site-specific handling needed; all sites use the same method.

https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-27 21:12:13 +00:00
Claude 9a1189659b fix: reveal/highlight scoped to chat area only + remove footer link — v2.0.14
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
2026-03-27 21:05:32 +00:00
Claude eec0d52681 fix: PPI warning accept/ignore buttons — v2.0.14
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
2026-03-27 20:53:53 +00:00
Claude 5c863b084d fix: encrypted data breaks Firefox — injector can't decrypt — v2.0.13
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
2026-03-27 16:58:40 +00:00
Claude c42a4aa55e revert: remove early fetch hook, restore simple injection — v2.0.12
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
2026-03-27 16:42:09 +00:00
Claude 557b1ffd0c fix: Firefox broken — CSP blocks inline early hook script — v2.0.11
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
2026-03-27 15:04:13 +00:00
Claude 85fbc6c4d0 fix: reveal mode broken + missing await + select rendering — v2.0.10
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
2026-03-27 12:41:47 +00:00
Claude 97e37e65ac fix: cross-browser sync import corrupting local encryption + v2.0.9
Root cause: when importing an encrypted sync code from another device,
_decryptFromSync was replacing the local encryption config (salt) with
the source device's salt. This caused:
1. Local key cache derived from wrong salt
2. Local data (encrypted with local salt) became unreadable
3. _applyData tried to write with the wrong key

Fixed with a complete refactor of cross-device decryption:
- authenticateForSync() derives a TEMPORARY key using the source salt
- Temporary key stored separately as 'tempSyncKey' in IndexedDB
- Local encryption config and cached key are NEVER modified
- After decryption, _applyData writes via _writeSecure using the
  LOCAL key (which uses the local salt)
- _handleDecryptedMeta extracted for code reuse

Also:
- Options.js auth handler detects pending sync import and routes to
  authenticateForSync instead of regular authenticate
- After auth success, automatically retries the import
- README updated: imported passwords are protected (dots in UI,
  vault password to reveal, AES-256 encrypted at rest)
- Bumped to v2.0.9

https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
2026-03-27 05:23:50 +00:00
Claude f2fa4befdd fix: sync code import not applying data after authentication
When importing an encrypted sync code, the flow was:
1. Click Apply → importSyncCode returns needsAuth
2. Auth prompt shown → user enters password → authentication succeeds
3. Green checkmark shown... but nobody re-runs the import

The user had to manually click Apply a second time. Now the import
automatically retries after successful authentication via a
__ssPendingSyncImport callback.

https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
2026-03-27 05:19:51 +00:00
Claude dd68361474 fix: move early fetch hook BEFORE async storage read
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
2026-03-27 05:14:13 +00:00
Claude 66b497f94b feat: dynamic content script registration for custom domains
Replace tab-load-only injection with scripting.registerContentScripts()
so custom domains inject at document_start (like built-in sites) and
persist across service worker restarts. Scripts re-register automatically
when domains change. Removed domains now revoke browser permissions.

https://claude.ai/code/session_01KF4i7Ra7zCEDskxDBaNtcT
2026-03-27 04:40:56 +00:00
Claude d1bc43d02d fix: cross-browser sync decryption + WebAuthn on extension pages
Sync code import between browsers was failing with "Wrong key or
corrupted data" because each browser derives a different AES key
from the same password (different salt). The decrypt function was
using the local device's salt instead of the source device's salt
embedded in the sync envelope.

Fixed _decryptFromSync to always use the sync envelope's salt for
decryption. If the cached key's salt doesn't match, forces re-auth
so the password is re-entered and a new key is derived with the
correct salt.

Also fixed WebAuthn "device can't be used" error on extension pages.
chrome-extension:// and moz-extension:// origins are not valid for
WebAuthn. isWebAuthnAvailable() now returns false on extension pages.

https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
2026-03-27 04:38:25 +00:00
Claude f284238792 chore: bump to 2.0.7
https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
2026-03-27 04:29:39 +00:00
Claude 271f036749 fix: ChatGPT interception — synchronous early fetch hook
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
2026-03-27 04:27:29 +00:00
Claude a3904843ba security: require encryption for all sync channels, reduce activity log to 100
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
2026-03-27 04:17:50 +00:00
Claude 02c635c4d7 fix: ChatGPT fetch interception failure + bump to 2.0.6
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
2026-03-27 04:07:11 +00:00