From ed92b077032fabdcc3bbed0070695180826ef23d Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 1 Apr 2026 16:07:01 +0000 Subject: [PATCH 1/2] 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 --- manifest.firefox.json | 33 ++++++++++++++++++++++++++++++++- manifest.json | 33 ++++++++++++++++++++++++++++++++- src/content/content.js | 16 ++++++++++++++-- 3 files changed, 78 insertions(+), 4 deletions(-) diff --git a/manifest.firefox.json b/manifest.firefox.json index a780e8e..8396c0b 100644 --- a/manifest.firefox.json +++ b/manifest.firefox.json @@ -25,6 +25,7 @@ "https://chatgpt.com/*", "https://chat.openai.com/*", "https://copilot.microsoft.com/*", + "https://grok.com/*", "https://grok.x.ai/*", "https://x.com/i/grok*", "https://gemini.google.com/*", @@ -53,6 +54,36 @@ "https://chatgpt.com/*", "https://chat.openai.com/*", "https://copilot.microsoft.com/*", + "https://grok.com/*", + "https://grok.x.ai/*", + "https://x.com/i/grok*", + "https://gemini.google.com/*", + "https://www.perplexity.ai/*", + "https://copilot.microsoft.com/*", + "https://chat.deepseek.com/*", + "https://huggingface.co/chat/*", + "https://poe.com/*", + "https://github.com/*", + "https://gitlab.com/*", + "https://www.reddit.com/*", + "https://old.reddit.com/*", + "https://stackoverflow.com/*", + "https://pastebin.com/*", + "http://localhost/*", + "http://127.0.0.1/*" + ], + "js": ["src/content/early-hook.js"], + "run_at": "document_start", + "world": "MAIN", + "all_frames": true + }, + { + "matches": [ + "https://claude.ai/*", + "https://chatgpt.com/*", + "https://chat.openai.com/*", + "https://copilot.microsoft.com/*", + "https://grok.com/*", "https://grok.x.ai/*", "https://x.com/i/grok*", "https://gemini.google.com/*", @@ -114,7 +145,7 @@ ], "web_accessible_resources": [ { - "resources": ["src/content/content.js", "src/content/early-hook.js", "src/lib/document-scanner.js"], + "resources": ["src/content/content.js", "src/lib/document-scanner.js"], "matches": [""] } ] diff --git a/manifest.json b/manifest.json index 172b26e..f6b01c3 100644 --- a/manifest.json +++ b/manifest.json @@ -15,6 +15,7 @@ "https://chatgpt.com/*", "https://chat.openai.com/*", "https://copilot.microsoft.com/*", + "https://grok.com/*", "https://grok.x.ai/*", "https://x.com/i/grok*", "https://gemini.google.com/*", @@ -43,6 +44,36 @@ "https://chatgpt.com/*", "https://chat.openai.com/*", "https://copilot.microsoft.com/*", + "https://grok.com/*", + "https://grok.x.ai/*", + "https://x.com/i/grok*", + "https://gemini.google.com/*", + "https://www.perplexity.ai/*", + "https://copilot.microsoft.com/*", + "https://chat.deepseek.com/*", + "https://huggingface.co/chat/*", + "https://poe.com/*", + "https://github.com/*", + "https://gitlab.com/*", + "https://www.reddit.com/*", + "https://old.reddit.com/*", + "https://stackoverflow.com/*", + "https://pastebin.com/*", + "http://localhost/*", + "http://127.0.0.1/*" + ], + "js": ["src/content/early-hook.js"], + "run_at": "document_start", + "world": "MAIN", + "all_frames": true + }, + { + "matches": [ + "https://claude.ai/*", + "https://chatgpt.com/*", + "https://chat.openai.com/*", + "https://copilot.microsoft.com/*", + "https://grok.com/*", "https://grok.x.ai/*", "https://x.com/i/grok*", "https://gemini.google.com/*", @@ -102,7 +133,7 @@ ], "web_accessible_resources": [ { - "resources": ["src/content/content.js", "src/content/early-hook.js", "src/lib/document-scanner.js"], + "resources": ["src/content/content.js", "src/lib/document-scanner.js"], "matches": [""] } ] diff --git a/src/content/content.js b/src/content/content.js index 93ae82e..4e69f94 100644 --- a/src/content/content.js +++ b/src/content/content.js @@ -789,7 +789,9 @@ // Fetch Interception — scans ALL POST requests with a body. // Service-agnostic: doesn't depend on URL patterns. // ============================================================ - const originalFetch = window.fetch; + // Use the real fetch captured by early-hook.js (before React stored its reference), + // or fall back to whatever window.fetch is now if early-hook.js didn't run. + const originalFetch = window.__ssOriginalFetch || window.fetch; // URLs to never touch (static assets, analytics, etc.) const SKIP_URL_PATTERNS = [ @@ -805,7 +807,7 @@ return SKIP_URL_PATTERNS.some(p => p.test(url)); } - window.fetch = async function (url, options) { + window.__ssInterceptFetch = async function (url, options) { if (!settings.enabled || !hasSubstitutions()) { return originalFetch.call(this, url, options); } @@ -869,6 +871,16 @@ return originalFetch.call(this, url, options); }; + // Activate the fetch hook. If early-hook.js ran first (world: MAIN, document_start), + // it already replaced window.fetch with a wrapper that forwards to __ssInterceptFetch + // once __ssReady is true — this catches React/Next.js fetch references stored before + // content.js loaded. Otherwise fall back to replacing window.fetch directly. + if (window.__ssOriginalFetch) { + window.__ssReady = true; + } else { + window.fetch = window.__ssInterceptFetch; + } + // ============================================================ // XMLHttpRequest Interception — same aggressive approach // ============================================================ From ed8d094320f1447a13aa54152b13219b30ed0b7d Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 1 Apr 2026 16:08:30 +0000 Subject: [PATCH 2/2] Bump version to 0.9.40 https://claude.ai/code/session_01CwcZK8nqL8pyBH9AxDs9qo --- manifest.firefox.json | 2 +- manifest.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest.firefox.json b/manifest.firefox.json index 8396c0b..42ee7ed 100644 --- a/manifest.firefox.json +++ b/manifest.firefox.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Silent Send", - "version": "0.9.39", + "version": "0.9.40", "description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.", "browser_specific_settings": { "gecko": { diff --git a/manifest.json b/manifest.json index f6b01c3..a7a3b0f 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Silent Send", - "version": "0.9.39", + "version": "0.9.40", "description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.", "permissions": [ "storage", diff --git a/package.json b/package.json index 6e63fd5..827e55c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "silent-send", - "version": "0.9.39", + "version": "0.9.40", "private": true, "license": "MIT", "description": "Browser extension that substitutes personal data before sending to AI services",