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
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "Silent Send",
|
"name": "Silent Send",
|
||||||
"version": "2.0.10",
|
"version": "2.0.11",
|
||||||
"description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.",
|
"description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.",
|
||||||
"browser_specific_settings": {
|
"browser_specific_settings": {
|
||||||
"gecko": {
|
"gecko": {
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
],
|
],
|
||||||
"web_accessible_resources": [
|
"web_accessible_resources": [
|
||||||
{
|
{
|
||||||
"resources": ["src/content/content.js"],
|
"resources": ["src/content/content.js", "src/content/early-hook.js"],
|
||||||
"matches": ["<all_urls>"]
|
"matches": ["<all_urls>"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "Silent Send",
|
"name": "Silent Send",
|
||||||
"version": "2.0.10",
|
"version": "2.0.11",
|
||||||
"description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.",
|
"description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"storage",
|
"storage",
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
],
|
],
|
||||||
"web_accessible_resources": [
|
"web_accessible_resources": [
|
||||||
{
|
{
|
||||||
"resources": ["src/content/content.js"],
|
"resources": ["src/content/content.js", "src/content/early-hook.js"],
|
||||||
"matches": ["<all_urls>"]
|
"matches": ["<all_urls>"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "silent-send",
|
"name": "silent-send",
|
||||||
"version": "2.0.10",
|
"version": "2.0.11",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "BSL-1.1",
|
"license": "BSL-1.1",
|
||||||
"description": "Browser extension that substitutes personal data before sending to AI services",
|
"description": "Browser extension that substitutes personal data before sending to AI services",
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Silent Send - Early Fetch Hook
|
||||||
|
*
|
||||||
|
* Injected synchronously BEFORE any page JavaScript to capture the
|
||||||
|
* real fetch() and XMLHttpRequest before frameworks (Next.js, React)
|
||||||
|
* can store their own references.
|
||||||
|
*
|
||||||
|
* Must be loaded as an external <script src="..."> (not inline)
|
||||||
|
* because sites like claude.ai have strict CSP that blocks inline scripts.
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
window.__ssOriginalFetch = window.fetch;
|
||||||
|
window.__ssOriginalXHROpen = XMLHttpRequest.prototype.open;
|
||||||
|
window.__ssOriginalXHRSend = XMLHttpRequest.prototype.send;
|
||||||
|
window.__ssReady = false;
|
||||||
|
|
||||||
|
window.fetch = function () {
|
||||||
|
if (window.__ssReady && window.__ssInterceptFetch) {
|
||||||
|
return window.__ssInterceptFetch.apply(this, arguments);
|
||||||
|
}
|
||||||
|
return window.__ssOriginalFetch.apply(this, arguments);
|
||||||
|
};
|
||||||
|
})();
|
||||||
+15
-24
@@ -15,24 +15,23 @@
|
|||||||
if (window.__silentSendInjected) return;
|
if (window.__silentSendInjected) return;
|
||||||
window.__silentSendInjected = true;
|
window.__silentSendInjected = true;
|
||||||
|
|
||||||
// IMMEDIATELY inject a synchronous fetch hook into the page world
|
// Cross-browser API
|
||||||
// BEFORE any async operations. This must run before any page JS
|
const api =
|
||||||
// (like ChatGPT's Next.js) can store a reference to the original fetch.
|
typeof browser !== 'undefined' && browser.runtime
|
||||||
|
? browser
|
||||||
|
: typeof chrome !== 'undefined'
|
||||||
|
? chrome
|
||||||
|
: null;
|
||||||
|
|
||||||
|
// IMMEDIATELY inject the early fetch hook into the page world as an
|
||||||
|
// EXTERNAL file. Must be external (not inline) because sites like
|
||||||
|
// claude.ai have strict CSP that blocks inline scripts. Firefox
|
||||||
|
// enforces this strictly; Chrome is more permissive but external
|
||||||
|
// works everywhere.
|
||||||
const earlyHook = document.createElement('script');
|
const earlyHook = document.createElement('script');
|
||||||
earlyHook.textContent = `(function(){
|
earlyHook.src = api.runtime.getURL('src/content/early-hook.js');
|
||||||
window.__ssOriginalFetch = window.fetch;
|
|
||||||
window.__ssOriginalXHROpen = XMLHttpRequest.prototype.open;
|
|
||||||
window.__ssOriginalXHRSend = XMLHttpRequest.prototype.send;
|
|
||||||
window.__ssReady = false;
|
|
||||||
window.fetch = function() {
|
|
||||||
if (window.__ssReady && window.__ssInterceptFetch) {
|
|
||||||
return window.__ssInterceptFetch.apply(this, arguments);
|
|
||||||
}
|
|
||||||
return window.__ssOriginalFetch.apply(this, arguments);
|
|
||||||
};
|
|
||||||
})();`;
|
|
||||||
(document.head || document.documentElement).appendChild(earlyHook);
|
(document.head || document.documentElement).appendChild(earlyHook);
|
||||||
earlyHook.remove();
|
earlyHook.onload = () => earlyHook.remove();
|
||||||
|
|
||||||
// Merge active profiles into flat identity object
|
// Merge active profiles into flat identity object
|
||||||
function mergeProfiles(data) {
|
function mergeProfiles(data) {
|
||||||
@@ -66,14 +65,6 @@
|
|||||||
return merged;
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cross-browser API
|
|
||||||
const api =
|
|
||||||
typeof browser !== 'undefined' && browser.runtime
|
|
||||||
? browser
|
|
||||||
: typeof chrome !== 'undefined'
|
|
||||||
? chrome
|
|
||||||
: null;
|
|
||||||
|
|
||||||
// Load mappings and settings, then inject into page
|
// Load mappings and settings, then inject into page
|
||||||
async function init() {
|
async function init() {
|
||||||
const result = await api.storage.local.get(['ss_mappings', 'ss_identity', 'ss_settings']);
|
const result = await api.storage.local.get(['ss_mappings', 'ss_identity', 'ss_settings']);
|
||||||
|
|||||||
@@ -591,7 +591,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<p>Silent Send v2.0.10</p>
|
<p>Silent Send v2.0.11</p>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user