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:
+10
-3
@@ -891,8 +891,11 @@
|
||||
// ============================================================
|
||||
// Fetch Interception — scans ALL POST requests with a body.
|
||||
// Service-agnostic: doesn't depend on URL patterns.
|
||||
// Uses __ssOriginalFetch from the early hook (injected synchronously
|
||||
// before any page JS) to ensure we have the real fetch, even if
|
||||
// frameworks like Next.js (ChatGPT) store a reference early.
|
||||
// ============================================================
|
||||
const originalFetch = window.fetch;
|
||||
const originalFetch = window.__ssOriginalFetch || window.fetch;
|
||||
|
||||
// URLs to never touch (static assets, analytics, etc.)
|
||||
const SKIP_URL_PATTERNS = [
|
||||
@@ -1012,6 +1015,10 @@
|
||||
return originalFetch.call(this, url, options);
|
||||
};
|
||||
|
||||
// Register our fetch interceptor so the early hook proxy can use it
|
||||
window.__ssInterceptFetch = window.fetch;
|
||||
window.__ssReady = true;
|
||||
|
||||
// ============================================================
|
||||
// Document Upload Processing
|
||||
//
|
||||
@@ -1376,8 +1383,8 @@
|
||||
// ============================================================
|
||||
// XMLHttpRequest Interception — same aggressive approach
|
||||
// ============================================================
|
||||
const origOpen = XMLHttpRequest.prototype.open;
|
||||
const origSend = XMLHttpRequest.prototype.send;
|
||||
const origOpen = window.__ssOriginalXHROpen || XMLHttpRequest.prototype.open;
|
||||
const origSend = window.__ssOriginalXHRSend || XMLHttpRequest.prototype.send;
|
||||
|
||||
XMLHttpRequest.prototype.open = function (method, url, ...rest) {
|
||||
this._ssUrl = url;
|
||||
|
||||
+26
-1
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user