From b3eec070855a9cb5a085f4aad53473e966372e42 Mon Sep 17 00:00:00 2001 From: TPD94 Date: Mon, 9 Jun 2025 12:01:39 -0400 Subject: [PATCH] Update inject.js --- inject.js | 907 +++++++++++++----------------------------------------- 1 file changed, 209 insertions(+), 698 deletions(-) diff --git a/inject.js b/inject.js index d537fc4..d4fb687 100644 --- a/inject.js +++ b/inject.js @@ -1,29 +1,33 @@ -let customBase64 = "PlaceHolder"; -let psshFound = false; -let postRequestFound = false; -let firstValidLicenseResponse = false; -let firstValidServiceCertificate = false; -let remoteCDM = null; -let decryptionKeys = null; -let messageSuppressed = false; -let interceptType = "DISABLED"; // Default to LICENSE, can be changed to 'EME' for EME interception -let originalChallenge = null; let widevineDeviceInfo = null; let playreadyDeviceInfo = null; -let drmOveride = "DISABLED" +let originalMessage = null; +let originalChallenge = null +let serviceCertFound = false; +let drmType = "NONE"; +let psshFound = false; +let firstValidLicenseChallenge = false; +let pssh = null; +let drmOverride = "DISABLED"; +let interceptType = "DISABLED"; +let remoteCDM = null; + +// Post message to content.js to get DRM override window.postMessage({ type: "__GET_DRM_OVERRIDE__" }, "*"); +// Add listener for DRM override messages window.addEventListener("message", function(event) { if (event.source !== window) return; if (event.data.type === "__DRM_OVERRIDE__") { - drmOveride = event.data.drmOverride || "DISABLED"; - console.log("DRM Override set to:", drmOveride); + drmOverride = event.data.drmOverride || "DISABLED"; + console.log("DRM Override set to:", drmOverride); } }); +// Post message to content.js to get injection type window.postMessage({ type: "__GET_INJECTION_TYPE__" }, "*"); +// Add listener for injection type messages window.addEventListener("message", function(event) { if (event.source !== window) return; @@ -33,25 +37,25 @@ window.addEventListener("message", function(event) { } }); - +// Post message to get CDM devices window.postMessage({ type: "__GET_CDM_DEVICES__" }, "*"); +// Add listener for CDM device messages window.addEventListener("message", function(event) { if (event.source !== window) return; if (event.data.type === "__CDM_DEVICES__") { const { widevine_device, playready_device } = event.data; - // Now you can use widevine_device and playready_device! console.log("Received device info:", widevine_device, playready_device); - // Store them globally widevineDeviceInfo = widevine_device; playreadyDeviceInfo = playready_device; } }); +// PlayReady Remote CDM Class class remotePlayReadyCDM { constructor(security_level, host, secret, device_name) { this.security_level = security_level; @@ -60,373 +64,267 @@ class remotePlayReadyCDM { this.device_name = device_name; this.session_id = null; this.challenge = null; + this.keys = null; } + // Open PlayReady session async openSession() { const url = `${this.host}/remotecdm/playready/${this.device_name}/open`; - - try { - const response = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json' - } - }); - - const jsonData = await response.json(); - - if (response.ok && jsonData.data?.session_id) { - this.session_id = jsonData.data.session_id; - return { success: true, session_id: this.session_id }; - } else { - return { success: false, error: jsonData.message || 'Unknown error occurred.' }; + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', } - - } catch (error) { - return { success: false, error: error.message }; + }) + const jsonData = await response.json(); + if (response.ok && jsonData.data?.session_id) { + this.session_id = jsonData.data.session_id; + console.log("PlayReady session opened:", this.session_id); + } else { + console.error("Failed to open PlayReady session:", jsonData.message); + throw new Error("Failed to open PlayReady session"); } } + // Get PlayReady challenge async getChallenge(init_data) { const url = `${this.host}/remotecdm/playready/${this.device_name}/get_license_challenge`; - const body = { session_id: this.session_id, - init_data: init_data, + init_data: init_data }; - - try { - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(body) - }); - - const jsonData = await response.json(); - - if (response.ok && jsonData.data?.challenge) { - return { - success: true, - challenge: jsonData.data.challenge - }; - } else { - return { - success: false, - error: jsonData.message || 'Failed to retrieve license challenge.' - }; - } - - } catch (error) { - return { - success: false, - error: error.message - }; + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body) + }); + const jsonData = await response.json(); + if (response.ok && jsonData.data?.challenge) { + this.challenge = jsonData.data.challenge; + console.log("PlayReady challenge received:", this.challenge); + } else { + console.error("Failed to get PlayReady challenge:", jsonData.message); + throw new Error("Failed to get PlayReady challenge"); } } - async parseLicense(license_message) { + // Parse PlayReady license response + async parseLicenseResponse(license_message) { const url = `${this.host}/remotecdm/playready/${this.device_name}/parse_license`; - const body = { session_id: this.session_id, license_message: license_message - }; - - try { - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(body) - }); - - const jsonData = await response.json(); - - if (response.ok && jsonData.message === "Successfully parsed and loaded the Keys from the License message") { - return { - success: true, - message: jsonData.message - }; - } else { - return { - success: false, - error: jsonData.message || 'Failed to parse license.' - }; - } - } catch (error) { - return { - success: false, - error: error.message - }; + } + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body) + }); + const jsonData = await response.json(); + if (response.ok && jsonData.message === "Successfully parsed and loaded the Keys from the License message") + { + console.log("PlayReady license response parsed successfully"); + return true; + } else { + console.error("Failed to parse PlayReady license response:", jsonData.message); + throw new Error("Failed to parse PlayReady license response"); } } - async closeSession() { - const url = `${this.host}/remotecdm/playready/${this.device_name}/close/${this.session_id}`; - - try { - const response = await fetch(url, { - method: 'GET', - headers: { 'Content-Type': 'application/json' } - }); - - const jsonData = await response.json(); - - if (response.ok) { - return { success: true, message: jsonData.message }; - } else { - return { success: false, error: jsonData.message || 'Failed to close session.' }; - } - } catch (error) { - return { success: false, error: error.message }; - } - } - - + // Get PlayReady keys async getKeys() { const url = `${this.host}/remotecdm/playready/${this.device_name}/get_keys`; - const body = { session_id: this.session_id - }; + } + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body) + }); + const jsonData = await response.json() + if (response.ok && jsonData.data?.keys) { + this.keys = jsonData.data.keys; + console.log("PlayReady keys received:", this.keys); + } else { + console.error("Failed to get PlayReady keys:", jsonData.message); + throw new Error("Failed to get PlayReady keys"); + } + } - try { - const response = await fetch(url, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(body) - }); - - const jsonData = await response.json(); - - if (response.ok && jsonData.data?.keys) { - decryptionKeys = jsonData.data.keys; - - // Automatically close the session after key retrieval - await this.closeSession(); - - return { success: true, keys: decryptionKeys }; - } else { - return { - success: false, - error: jsonData.message || 'Failed to retrieve decryption keys.' - }; + // Close PlayReady session + async closeSession () { + const url = `${this.host}/remotecdm/playready/${this.device_name}/close/${this.session_id}`; + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', } - - } catch (error) { - return { success: false, error: error.message }; + }); + const jsonData = await response.json(); + if (response.ok) { + console.log("PlayReady session closed successfully"); + } else { + console.error("Failed to close PlayReady session:", jsonData.message); + throw new Error("Failed to close PlayReady session"); } } } +// Widevine Remote CDM Class class remoteWidevineCDM { - constructor(device_type, system_id, security_level, host, secret, device_name) { - this.device_type = device_type; - this.system_id = system_id; - this.security_level = security_level; - this.host = host; - this.secret = secret; - this.device_name = device_name; - this.session_id = null; - this.challenge = null; - } + constructor(device_type, system_id, security_level, host, secret, device_name) { + this.device_type = device_type; + this.system_id = system_id; + this.security_level = security_level; + this.host = host; + this.secret = secret; + this.device_name = device_name; + this.session_id = null; + this.challenge = null; + this.keys = null; + } - async openSession() { + // Open Widevine session + async openSession () { const url = `${this.host}/remotecdm/widevine/${this.device_name}/open`; - - try { - const response = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json' - } - }); - - const jsonData = await response.json(); - - if (response.ok && jsonData.status === 200 && jsonData.data?.session_id) { - this.session_id = jsonData.data.session_id; - return { success: true, session_id: this.session_id }; - } else { - return { success: false, error: jsonData.message || 'Unknown error occurred.' }; + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', } - - } catch (error) { - return { success: false, error: error.message }; + }); + const jsonData = await response.json(); + if (response.ok && jsonData.data?.session_id) { + this.session_id = jsonData.data.session_id; + console.log("Widevine session opened:", this.session_id); + } else { + console.error("Failed to open Widevine session:", jsonData.message); + throw new Error("Failed to open Widevine session"); } } + // Set Widevine service certificate async setServiceCertificate(certificate) { const url = `${this.host}/remotecdm/widevine/${this.device_name}/set_service_certificate`; - const body = { session_id: this.session_id, certificate: certificate ?? null - }; - - try { - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(body) - }); - - const jsonData = await response.json(); - - if (response.ok && jsonData.status === 200) { - return { success: true }; - } else { - return { success: false, error: jsonData.message || 'Failed to set service certificate.' }; - } - - } catch (error) { - return { success: false, error: error.message }; + } + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body) + }); + const jsonData = await response.json(); + if (response.ok && jsonData.status === 200) { + console.log("Service certificate set successfully"); + } else { + console.error("Failed to set service certificate:", jsonData.message); + throw new Error("Failed to set service certificate"); } } - async getChallenge(init_data, license_type = 'STREAMING', privacy_mode = false) { + // Get Widevine challenge + async getChallenge(init_data, license_type = 'STREAMING') { const url = `${this.host}/remotecdm/widevine/${this.device_name}/get_license_challenge/${license_type}`; - const body = { session_id: this.session_id, init_data: init_data, - privacy_mode: privacy_mode + privacy_mode: serviceCertFound }; - - try { - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(body) - }); - - const jsonData = await response.json(); - - if (response.ok && jsonData.status === 200 && jsonData.data?.challenge_b64) { - return { - success: true, - challenge: jsonData.data.challenge_b64 - }; - } else { - return { - success: false, - error: jsonData.message || 'Failed to retrieve license challenge.' - }; - } - - } catch (error) { - return { - success: false, - error: error.message - }; + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body) + }); + const jsonData = await response.json(); + if (response.ok && jsonData.data?.challenge_b64) { + this.challenge = jsonData.data.challenge_b64; + console.log("Widevine challenge received:", this.challenge); + } else { + console.error("Failed to get Widevine challenge:", jsonData.message); + throw new Error("Failed to get Widevine challenge"); } } - async parseLicense(license_message) { - const url = `${this.host}/remotecdm/widevine/${this.device_name}/parse_license`; + // Parse Widevine license response + async parseLicense(license_message) { + const url = `${this.host}/remotecdm/widevine/${this.device_name}/parse_license`; const body = { session_id: this.session_id, license_message: license_message }; - - try { - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(body) - }); - - const jsonData = await response.json(); - - if (response.ok && jsonData.status === 200) { - return { - success: true, - message: jsonData.message - }; - } else { - return { - success: false, - error: jsonData.message || 'Failed to parse license.' - }; - } - } catch (error) { - return { - success: false, - error: error.message - }; + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body) + }); + const jsonData = await response.json(); + if (response.ok && jsonData.status === 200) { + console.log("Widevine license response parsed successfully"); + return true; + } else { + console.error("Failed to parse Widevine license response:", jsonData.message); + throw new Error("Failed to parse Widevine license response"); } } - async closeSession() { - const url = `${this.host}/remotecdm/widevine/${this.device_name}/close/${this.session_id}`; - - try { - const response = await fetch(url, { - method: 'GET', - headers: { 'Content-Type': 'application/json' } - }); - - const jsonData = await response.json(); - - if (response.ok && jsonData.status === 200) { - return { success: true, message: jsonData.message }; - } else { - return { success: false, error: jsonData.message || 'Failed to close session.' }; - } - } catch (error) { - return { success: false, error: error.message }; - } - } - - + // Get Widevine keys async getKeys() { const url = `${this.host}/remotecdm/widevine/${this.device_name}/get_keys/ALL`; - const body = { session_id: this.session_id }; + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body) + }); + const jsonData = await response.json(); + if (response.ok && jsonData.data?.keys) { + this.keys = jsonData.data.keys; + console.log("Widevine keys received:", this.keys); + } else { + console.error("Failed to get Widevine keys:", jsonData.message); + throw new Error("Failed to get Widevine keys"); + } + } - try { - const response = await fetch(url, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(body) - }); - - const jsonData = await response.json(); - - if (response.ok && jsonData.status === 200 && jsonData.data?.keys) { - decryptionKeys = jsonData.data.keys; - - // Automatically close the session after key retrieval - await this.closeSession(); - - return { success: true, keys: decryptionKeys }; - } else { - return { - success: false, - error: jsonData.message || 'Failed to retrieve decryption keys.' - }; + // Close Widevine session + async closeSession() { + const url = `${this.host}/remotecdm/widevine/${this.device_name}/close/${this.session_id}`; + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', } - - } catch (error) { - return { success: false, error: error.message }; + }); + const jsonData = await response.json(); + if (response.ok) { + console.log("Widevine session closed successfully"); + } else { + console.error("Failed to close Widevine session:", jsonData.message); + throw new Error("Failed to close Widevine session"); } } } - - -// --- Utility functions --- +// Utility functions const hexStrToU8 = hexString => Uint8Array.from(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16))); @@ -476,7 +374,6 @@ const isJson = (str) => { } }; -// --- Widevine-style PSSH extractor --- function getWidevinePssh(buffer) { const hex = u8ToHexStr(new Uint8Array(buffer)); const match = hex.match(/000000(..)?70737368.*/); @@ -487,7 +384,6 @@ function getWidevinePssh(buffer) { return window.btoa(String.fromCharCode(...bytes)); } -// --- PlayReady-style PSSH extractor --- function getPlayReadyPssh(buffer) { const u8 = new Uint8Array(buffer); const systemId = "9a04f07998404286ab92e65be0885f95"; @@ -503,7 +399,6 @@ function getPlayReadyPssh(buffer) { return window.btoa(String.fromCharCode(...psshBytes)); } -// --- Clearkey Support --- function getClearkey(response) { let obj = JSON.parse((new TextDecoder("utf-8")).decode(response)); return obj["keys"].map(o => ({ @@ -512,9 +407,8 @@ function getClearkey(response) { })); } -// --- Convert Base64 to Uint8Array --- function base64ToUint8Array(base64) { - const binaryStr = atob(base64); // Decode base64 to binary string + const binaryStr = atob(base64); const len = binaryStr.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { @@ -527,392 +421,9 @@ function arrayBufferToBase64(uint8array) { let binary = ''; const len = uint8array.length; - // Convert each byte to a character for (let i = 0; i < len; i++) { binary += String.fromCharCode(uint8array[i]); } - // Encode the binary string to Base64 return window.btoa(binary); -} - -// --- Intercepting EME Calls --- -const originalGenerateRequest = MediaKeySession.prototype.generateRequest; - -MediaKeySession.prototype.generateRequest = async function(initDataType, initData) { - console.log(initData); - const session = this; - - let playReadyAttempted = false; - let playReadySucceeded = false; - let playReadyPssh = null; - let widevinePssh = null; - - if (!psshFound && !messageSuppressed && (interceptType === 'EME' || interceptType === 'LICENSE')) { - // === Try PlayReady First === - playReadyPssh = getPlayReadyPssh(initData); - playReadyAttempted = !!playReadyPssh; - - if (playReadyPssh && drmOveride !== "WIDEVINE") { - console.log("[PlayReady PSSH] Found:", playReadyPssh); - const drmType = { - type: "__DRM_TYPE__", - data: 'PlayReady' - }; - window.postMessage(drmType, "*"); - try { - const { - security_level, host, secret, device_name - } = playreadyDeviceInfo; - remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name); - const sessionResult = await remoteCDM.openSession(); - if (sessionResult.success) { - console.log("PlayReady session opened:", sessionResult.session_id); - const challengeResult = await remoteCDM.getChallenge(playReadyPssh); - if (challengeResult.success) { - customBase64 = btoa(challengeResult.challenge); - playReadySucceeded = true; - psshFound = true; - window.postMessage({ type: "__PSSH_DATA__", data: playReadyPssh }, "*"); - } else { - console.warn("PlayReady challenge failed:", challengeResult.error); - } - } else { - console.warn("PlayReady session failed:", sessionResult.error); - } - } catch (err) { - console.error("PlayReady error:", err.message); - } - } else { - console.log("[PlayReady PSSH] Not found."); - } - - // === Fallback to Widevine === - if (!playReadySucceeded) { - widevinePssh = getWidevinePssh(initData); - if (widevinePssh && drmOveride !== "PLAYREADY") { - console.log("[Widevine PSSH] Found:", widevinePssh); - const drmType = { - type: "__DRM_TYPE__", - data: 'Widevine' - }; - window.postMessage(drmType, "*"); - try { - const { - device_type, system_id, security_level, host, secret, device_name - } = widevineDeviceInfo; - remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name); - const sessionResult = await remoteCDM.openSession(); - if (sessionResult.success) { - console.log("Widevine session opened:", sessionResult.session_id); - const challengeResult = await remoteCDM.getChallenge(widevinePssh); - if (challengeResult.success) { - customBase64 = challengeResult.challenge; - psshFound = true; - window.postMessage({ type: "__PSSH_DATA__", data: widevinePssh }, "*"); - } else { - console.warn("Widevine challenge failed:", challengeResult.error); - } - } else { - console.warn("Widevine session failed:", sessionResult.error); - } - } catch (err) { - console.error("Widevine error:", err.message); - } - } else { - console.log("[Widevine PSSH] Not found."); - } - } - - // === Intercept License or EME Messages === - if (!messageSuppressed && interceptType === 'EME') { - session.addEventListener("message", function originalMessageInterceptor(event) { - event.stopImmediatePropagation(); - console.log("[Intercepted EME Message] Injecting custom message."); - console.log(event.data); - - const uint8 = base64ToUint8Array(customBase64); - const arrayBuffer = uint8.buffer; - - const syntheticEvent = new MessageEvent("message", { - data: event.data, - origin: event.origin, - lastEventId: event.lastEventId, - source: event.source, - ports: event.ports - }); - - Object.defineProperty(syntheticEvent, "message", { - get: () => arrayBuffer - }); - console.log(syntheticEvent); - setTimeout(() => session.dispatchEvent(syntheticEvent), 0); - }, { once: true }); - - messageSuppressed = true; - } - - if (!messageSuppressed && interceptType === 'LICENSE') { - session.addEventListener("message", function originalMessageInterceptor(event) { - if (playReadyAttempted && playReadySucceeded) { - const buffer = event.message; - const decoder = new TextDecoder('utf-16'); - const decodedText = decoder.decode(buffer); - const match = decodedText.match(/([^<]+)<\/Challenge>/); - if (match) { - originalChallenge = match[1]; - console.log("[PlayReady Challenge Extracted]"); - messageSuppressed = true; - } - } - - if (!playReadySucceeded && widevinePssh && psshFound) { - const uint8Array = new Uint8Array(event.message); - const b64array = arrayBufferToBase64(uint8Array); - if (b64array !== "CAQ=") { - originalChallenge = b64array; - console.log("[Widevine Challenge Extracted]"); - messageSuppressed = true; - } - } - }, { once: false }); - } - } - - // Proceed with original generateRequest - return originalGenerateRequest.call(session, initDataType, initData); -}; - -// license message handler -const originalUpdate = MediaKeySession.prototype.update; - -MediaKeySession.prototype.update = function(response) { - const uint8 = response instanceof Uint8Array ? response : new Uint8Array(response); - const base64Response = window.btoa(String.fromCharCode(...uint8)); - - // Handle Service Certificate - if (base64Response.startsWith("CAUS") && !firstValidServiceCertificate) { - const base64ServiceCertificateData = { - type: "__CERTIFICATE_DATA__", - data: base64Response - }; - window.postMessage(base64ServiceCertificateData, "*"); - firstValidServiceCertificate = true; - } - - // Handle License Data - if (!base64Response.startsWith("CAUS") && (interceptType === 'EME' || interceptType === 'LICENSE')) { - - // 🔁 Call parseLicense, then getKeys from global remoteCDM - if (remoteCDM !== null && remoteCDM.session_id) { - remoteCDM.parseLicense(base64Response) - .then(result => { - if (result.success) { - console.log("[Base64 Response]", base64Response); - const base64LicenseData = { - type: "__LICENSE_DATA__", - data: base64Response - }; - window.postMessage(base64LicenseData, "*"); - console.log("[remoteCDM] License parsed successfully"); - - // 🚀 Now call getKeys after parsing - return remoteCDM.getKeys(); - } else { - console.warn("[remoteCDM] License parse failed:", result.error); - } - }) - .then(keysResult => { - if (keysResult?.success) { - const keysData = { - type: "__KEYS_DATA__", - data: keysResult.keys - }; - window.postMessage(keysData, "*"); - console.log("[remoteCDM] Decryption keys retrieved:", keysResult.keys); - } else if (keysResult) { - console.warn("[remoteCDM] Failed to retrieve keys:", keysResult.error); - } - }) - .catch(err => { - console.error("[remoteCDM] Unexpected error in license flow:", err); - }); - } else { - console.warn("[remoteCDM] Cannot parse license: remoteCDM not initialized or session_id missing."); - } - } - - const updatePromise = originalUpdate.call(this, response); - - if (!psshFound) { - updatePromise - .then(() => { - let clearKeys = getClearkey(response); - if (clearKeys && clearKeys.length > 0) { - console.log("[CLEARKEY] ", clearKeys); - const drmType = { - type: "__DRM_TYPE__", - data: 'ClearKey' - }; - window.postMessage(drmType, "*"); - const keysData = { - type: "__KEYS_DATA__", - data: clearKeys - }; - window.postMessage(keysData, "*"); - } - }) - .catch(e => { - console.log("[CLEARKEY] Not found"); - }); - } - - return updatePromise; -}; - -// --- Request Interception --- -(function interceptRequests() { - const sendToBackground = (data) => { - window.postMessage({ type: "__INTERCEPTED_POST__", data }, "*"); - }; - -// Intercept fetch -const originalFetch = window.fetch; - -window.fetch = async function(input, init = {}) { - const method = (init.method || 'GET').toUpperCase(); - - if (method === "POST") { - const url = typeof input === "string" ? input : input.url; - let body = init.body; - - // If the body is FormData, convert it to an object (or JSON) - if (body instanceof FormData) { - const formData = {}; - body.forEach((value, key) => { - formData[key] = value; - }); - body = JSON.stringify(formData); // Convert formData to JSON string - } - - const headers = {}; - if (init.headers instanceof Headers) { - init.headers.forEach((v, k) => { headers[k] = v; }); - } else { - Object.assign(headers, init.headers || {}); - } - - try { - let modifiedBody = body; // Keep a reference to the original body - - // Handle body based on its type - if (typeof body === 'string') { - if (isJson(body)) { - const parsed = JSON.parse(body); - if (jsonContainsValue(parsed, customBase64)) { - sendToBackground({ url, method, headers, body }); - } - if (jsonContainsValue(parsed, originalChallenge)) { - newJSONBody = jsonReplaceValue(parsed, originalChallenge, customBase64); - modifiedBody = JSON.stringify(newJSONBody) - sendToBackground({ url, method, headers, modifiedBody }); - } - } else if (body === customBase64) { - sendToBackground({ url, method, headers, body }); - } else if (btoa(body) == originalChallenge) { - modifiedBody = atob(customBase64); - sendToBackground({ url, method, headers, modifiedBody }); - } - }else if (body instanceof ArrayBuffer || body instanceof Uint8Array) { - const buffer = body instanceof Uint8Array ? body : new Uint8Array(body); - const base64Body = window.btoa(String.fromCharCode(...buffer)); - if (base64Body === customBase64) { - sendToBackground({ url, method, headers, body: base64Body }); - } - if (base64Body === originalChallenge) { - modifiedBody = base64ToUint8Array(customBase64); // Modify the body - sendToBackground({ url, method, headers, body: modifiedBody }); - } - } - - // Ensure the modified body is used and passed to the original fetch call - init.body = modifiedBody; - - } catch (e) { - console.warn("Error handling fetch body:", e); - } - } - - // Call the original fetch method with the potentially modified body - return originalFetch(input, init); -}; - -// Intercept XMLHttpRequest -const originalOpen = XMLHttpRequest.prototype.open; -const originalSend = XMLHttpRequest.prototype.send; - -XMLHttpRequest.prototype.open = function(method, url, async, user, password) { - this._method = method; - this._url = url; - return originalOpen.apply(this, arguments); -}; - -XMLHttpRequest.prototype.send = function(body) { - if (this._method?.toUpperCase() === "POST") { - const xhr = this; - const headers = {}; - const originalSetRequestHeader = xhr.setRequestHeader; - - xhr.setRequestHeader = function(header, value) { - headers[header] = value; - return originalSetRequestHeader.apply(this, arguments); - }; - - setTimeout(() => { - try { - let modifiedBody = body; // Start with the original body - - // Check if the body is a string and can be parsed as JSON - if (typeof body === 'string') { - if (isJson(body)) { - const parsed = JSON.parse(body); - if (jsonContainsValue(parsed, customBase64)) { - sendToBackground({ url: xhr._url, method: xhr._method, headers, body }); - } - if (jsonContainsValue(parsed, originalChallenge)) { - newJSONBody = jsonReplaceValue(parsed, originalChallenge, customBase64); - modifiedBody = JSON.stringify(newJSONBody); - sendToBackground({ url: xhr._url, method: xhr._method, headers, modifiedBody }); - } - } else if (body === originalChallenge) { - modifiedBody = customBase64 - sendToBackground({ url: xhr._url, method: xhr._method, headers, body }); - } else if (btoa(body) == originalChallenge) { - modifiedBody = atob(customBase64); - sendToBackground({ url: xhr._url, method: xhr._method, headers, body }); - } - } else if (body instanceof ArrayBuffer || body instanceof Uint8Array) { - const buffer = body instanceof Uint8Array ? body : new Uint8Array(body); - const base64Body = window.btoa(String.fromCharCode(...buffer)); - if (base64Body === customBase64) { - sendToBackground({ url: xhr._url, method: xhr._method, headers, body: base64Body }); - } - if (base64Body === originalChallenge) { - modifiedBody = base64ToUint8Array(customBase64); // Modify the body - sendToBackground({ url: xhr._url, method: xhr._method, headers, body: modifiedBody }); - } - } - - // Ensure original send is called only once with the potentially modified body - originalSend.apply(this, [modifiedBody]); - - } catch (e) { - console.warn("Error handling XHR body:", e); - } - }, 0); - } else { - // Call the original send for non-POST requests - return originalSend.apply(this, arguments); - } -}; -})(); +} \ No newline at end of file