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
This commit is contained in:
Claude
2026-03-27 04:27:29 +00:00
parent 02c635c4d7
commit 271f036749
2 changed files with 36 additions and 4 deletions
+26 -1
View File
@@ -70,7 +70,32 @@
// Merge active profiles into a flat identity object for the content script
const identity = mergeProfiles(identityData);
// Inject the main interception script into the page's world
// STEP 1: Inject a synchronous inline script that patches fetch/XHR
// IMMEDIATELY, before any page JS can store a reference to the originals.
// This thin proxy queues calls until the full content.js loads.
const earlyHook = document.createElement('script');
earlyHook.textContent = `(function(){
// Store the real fetch/XHR before any page script can
window.__ssOriginalFetch = window.fetch;
window.__ssOriginalXHROpen = XMLHttpRequest.prototype.open;
window.__ssOriginalXHRSend = XMLHttpRequest.prototype.send;
window.__ssReady = false;
window.__ssQueue = [];
// Replace fetch with a proxy that queues until content.js is ready
window.fetch = function() {
if (window.__ssReady && window.__ssInterceptFetch) {
return window.__ssInterceptFetch.apply(this, arguments);
}
// If not ready yet, call original (no substitution possible)
return window.__ssOriginalFetch.apply(this, arguments);
};
})();`;
(document.head || document.documentElement).appendChild(earlyHook);
earlyHook.remove();
// STEP 2: Load the full content.js which will use __ssOriginalFetch
// and set __ssReady = true when it's done hooking
const script = document.createElement('script');
script.setAttribute('data-ss-config', JSON.stringify({ mappings, identity, settings }));
script.src = api.runtime.getURL('src/content/content.js');