// @name Network Interceptor // @description Intercept and inspect XHR and Fetch network requests on any webpage // @image https://raw.githubusercontent.com/H1D/network-interceptor-bookmarklet/main/usage_landscape_optimized.gif // @video https://raw.githubusercontent.com/H1D/network-interceptor-bookmarklet/main/usage.mp4 (function() { if (window.xhrInterceptorActive) { alert('XHR Interceptor is already active!'); return; } window.xhrInterceptorActive = true; const style = document.createElement('style'); style.textContent = ` #xhr-interceptor-overlay { position: fixed; top: 10px; right: 10px; width: 600px; max-height: 90vh; background: rgba(0, 0, 0, 0.95); color: #fff; font-family: monospace; font-size: 12px; z-index: 2147483647; border-radius: 8px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); display: flex; flex-direction: column; } #xhr-interceptor-header { padding: 15px; background: #1a1a1a; border-bottom: 2px solid #333; display: flex; justify-content: space-between; align-items: center; cursor: move; } #xhr-interceptor-header h3 { margin: 0; font-size: 16px; color: #4CAF50; } #xhr-interceptor-controls { display: flex; gap: 10px; } #xhr-interceptor-controls button { background: #333; color: #fff; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; font-size: 11px; } #xhr-interceptor-controls button:hover { background: #555; } #xhr-interceptor-content { overflow-y: auto; padding: 10px; flex: 1; } .xhr-request { background: #1a1a1a; margin: 10px 0; padding: 12px; border-radius: 6px; border-left: 4px solid #4CAF50; cursor: pointer; transition: background 0.2s; } .xhr-request:hover { background: #252525; } .xhr-request-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; } .xhr-method { padding: 2px 8px; border-radius: 3px; font-weight: bold; font-size: 11px; } .xhr-method.GET { background: #2196F3; color: #fff; } .xhr-method.POST { background: #4CAF50; color: #fff; } .xhr-method.PUT { background: #FF9800; color: #fff; } .xhr-method.DELETE { background: #f44336; color: #fff; } .xhr-method.PATCH { background: #9C27B0; color: #fff; } .xhr-url { color: #64B5F6; flex: 1; margin: 0 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; } .xhr-status { padding: 2px 8px; border-radius: 3px; font-size: 11px; font-weight: bold; } .xhr-status.success { background: #4CAF50; color: #fff; } .xhr-status.error { background: #f44336; color: #fff; } .xhr-status.pending { background: #FF9800; color: #fff; } .xhr-timing { color: #999; font-size: 10px; } .xhr-details { margin-top: 10px; padding-top: 10px; border-top: 1px solid #333; display: none; } .xhr-details.expanded { display: block; } .xhr-section { margin: 10px 0; } .xhr-section-title { color: #4CAF50; font-weight: bold; margin-bottom: 5px; font-size: 11px; } .xhr-section-content { background: #0a0a0a; padding: 8px; border-radius: 4px; overflow-x: auto; max-height: 200px; overflow-y: auto; font-size: 10px; line-height: 1.4; } .xhr-section-content pre { margin: 0; white-space: pre-wrap; word-wrap: break-word; } `; document.head.appendChild(style); const overlay = document.createElement('div'); overlay.id = 'xhr-interceptor-overlay'; overlay.innerHTML = `

Network Interceptor

`; document.body.appendChild(overlay); const content = document.getElementById('xhr-interceptor-content'); const header = document.getElementById('xhr-interceptor-header'); let requests = []; let requestCounter = 0; // Dragging functionality let isDragging = false; let currentX, currentY, initialX, initialY, xOffset = 0, yOffset = 0; header.addEventListener('mousedown', (e) => { if (e.target.tagName !== 'BUTTON') { initialX = e.clientX - xOffset; initialY = e.clientY - yOffset; isDragging = true; } }); document.addEventListener('mousemove', (e) => { if (isDragging) { e.preventDefault(); currentX = e.clientX - initialX; currentY = e.clientY - initialY; xOffset = currentX; yOffset = currentY; overlay.style.transform = `translate3d(${currentX}px, ${currentY}px, 0)`; } }); document.addEventListener('mouseup', () => { isDragging = false; }); // Control buttons document.getElementById('xhr-clear').addEventListener('click', () => { requests = []; content.innerHTML = '
No requests captured yet...
'; }); document.getElementById('xhr-close').addEventListener('click', () => { overlay.remove(); window.xhrInterceptorActive = false; }); function addRequest(req) { requests.push(req); renderRequests(); } function updateRequest(id, updates) { const req = requests.find(r => r.id === id); if (req) { Object.assign(req, updates); renderRequests(); } } function renderRequests() { if (requests.length === 0) { content.innerHTML = '
No requests captured yet...
'; return; } content.innerHTML = requests.map(req => { const statusClass = req.status >= 200 && req.status < 300 ? 'success' : req.status >= 400 ? 'error' : 'pending'; const timing = req.endTime ? `${req.endTime - req.startTime}ms` : 'pending'; return `
${req.method} ${req.url} ${req.status ? `${req.status}` : ''} ${timing}
Request URL:
${req.url}
${req.requestHeaders ? `
Request Headers:
${JSON.stringify(req.requestHeaders, null, 2)}
` : ''} ${req.payload ? `
Request Payload:
${typeof req.payload === 'string' ? req.payload : JSON.stringify(req.payload, null, 2)}
` : ''} ${req.responseHeaders ? `
Response Headers:
${req.responseHeaders}
` : ''} ${req.response ? `
Response:
${typeof req.response === 'string' ? req.response.substring(0, 5000) : JSON.stringify(req.response, null, 2).substring(0, 5000)}${(typeof req.response === 'string' ? req.response.length : JSON.stringify(req.response).length) > 5000 ? ' ... (truncated)' : ''}
` : ''}
`; }).join(''); document.querySelectorAll('.xhr-request').forEach(el => { el.addEventListener('click', function() { const details = this.querySelector('.xhr-details'); details.classList.toggle('expanded'); }); }); } // Intercept XMLHttpRequest const OriginalXHR = window.XMLHttpRequest; function NewXHR() { const xhr = new OriginalXHR(); const id = requestCounter++; let requestData = { id, method: '', url: '', startTime: Date.now(), requestHeaders: {}, payload: null, status: null, response: null, responseHeaders: null, endTime: null }; const originalOpen = xhr.open; xhr.open = function(method, url) { requestData.method = method; requestData.url = url; return originalOpen.apply(this, arguments); }; const originalSetRequestHeader = xhr.setRequestHeader; xhr.setRequestHeader = function(header, value) { requestData.requestHeaders[header] = value; return originalSetRequestHeader.apply(this, arguments); }; const originalSend = xhr.send; xhr.send = function(body) { if (body) { try { requestData.payload = typeof body === 'string' ? body : JSON.stringify(body); } catch (e) { requestData.payload = '[Unable to serialize]'; } } addRequest({...requestData}); return originalSend.apply(this, arguments); }; xhr.addEventListener('readystatechange', function() { if (this.readyState === 4) { requestData.status = this.status; requestData.endTime = Date.now(); requestData.responseHeaders = this.getAllResponseHeaders(); try { requestData.response = this.responseText; } catch (e) { requestData.response = '[Unable to read response]'; } updateRequest(id, requestData); } }); return xhr; } window.XMLHttpRequest = NewXHR; // Intercept fetch const originalFetch = window.fetch; window.fetch = async function(...args) { const id = requestCounter++; const startTime = Date.now(); let url, options = {}; if (typeof args[0] === 'string') { url = args[0]; options = args[1] || {}; } else { url = args[0].url; options = args[0]; } const method = (options.method || 'GET').toUpperCase(); let payload = null; if (options.body) { try { payload = typeof options.body === 'string' ? options.body : JSON.stringify(options.body); } catch (e) { payload = '[Unable to serialize]'; } } const requestData = { id, method, url, startTime, requestHeaders: options.headers || {}, payload, status: null, response: null, responseHeaders: null, endTime: null }; addRequest(requestData); try { const response = await originalFetch.apply(this, args); const clonedResponse = response.clone(); const endTime = Date.now(); let responseText; try { responseText = await clonedResponse.text(); } catch (e) { responseText = '[Unable to read response]'; } const responseHeaders = {}; response.headers.forEach((value, key) => { responseHeaders[key] = value; }); updateRequest(id, { status: response.status, endTime, response: responseText, responseHeaders: JSON.stringify(responseHeaders, null, 2) }); return response; } catch (error) { updateRequest(id, { status: 0, endTime: Date.now(), response: `Error: ${error.message}` }); throw error; } }; content.innerHTML = '
Interceptor Active - Waiting for requests...
'; })();