(() => { if (window.__archPanelPageBridgeInstalled) { return; } window.__archPanelPageBridgeInstalled = true; const MESSAGE_SOURCE = "arch-panel-extension"; const API_PATH_FRAGMENT = "/oalcrm/web/api/"; const EXTRA_CAPTURE_ORIGINS = new Set([ "https://comcipapic-oalprod.integration.ocp.oraclecloud.com", ]); const SAFE_HEADER_BLOCKLIST = new Set([ "accept-encoding", "accept-language", "connection", "content-length", "cookie", "host", "origin", "referer", "sec-ch-ua", "sec-ch-ua-mobile", "sec-ch-ua-platform", "sec-fetch-dest", "sec-fetch-mode", "sec-fetch-site", "user-agent", ]); const bridgeState = { globalHeaders: new Map(), scopedHeaders: new Map(), }; const nativeFetch = window.fetch.bind(window); const originalOpen = window.XMLHttpRequest.prototype.open; const originalSetRequestHeader = window.XMLHttpRequest.prototype.setRequestHeader; const originalSend = window.XMLHttpRequest.prototype.send; function isIntegrationOrigin(url) { try { return ( new URL(url, window.location.origin).origin === "https://comcipapic-oalprod.integration.ocp.oraclecloud.com" ); } catch { return false; } } function normalizeUrl(input) { try { return new URL(input, window.location.origin).toString(); } catch { return ""; } } function shouldCapture(url) { if (!url) { return false; } if (url.includes(API_PATH_FRAGMENT)) { return true; } try { return EXTRA_CAPTURE_ORIGINS.has(new URL(url).origin); } catch { return false; } } function normalizeHeaders(input) { const normalized = new Map(); if (!input) { return normalized; } const headers = new Headers(input); headers.forEach((value, key) => { const lowerKey = key.toLowerCase(); if (SAFE_HEADER_BLOCKLIST.has(lowerKey)) { return; } if (lowerKey.startsWith("sec-")) { return; } normalized.set(lowerKey, value); }); return normalized; } function getScopeKeys(url) { const normalizedUrl = normalizeUrl(url); if (!normalizedUrl) { return []; } try { const parsedUrl = new URL(normalizedUrl); const path = parsedUrl.pathname; const keys = [`path:${path}`]; const providerProxyMatch = path.match( /\/provider-proxy\/([^/]+)\// ); const serviceMatch = path.match( /\/provider-proxy\/[^/]+\/service\/([^/]+)\// ); if (path.includes("/provider-proxy/")) { keys.push("scope:provider-proxy"); } if (path.includes("/identity-management/")) { keys.push("scope:identity-management"); } if (path.includes("/workbench-proxy/")) { keys.push("scope:workbench-proxy"); } if (path.includes("/elastic/")) { keys.push("scope:elastic"); } if (providerProxyMatch?.[1]) { keys.push(`provider:${providerProxyMatch[1]}`); } if (serviceMatch?.[1]) { keys.push(`service:${serviceMatch[1]}`); } keys.push("scope:api"); return keys; } catch { return []; } } function mergeIntoBucket(bucket, headers) { headers.forEach((value, key) => { bucket.set(key, value); }); } function isAuthorizationLikeHeader(key) { const normalizedKey = String(key || "").toLowerCase(); return ( normalizedKey === "authorization" || normalizedKey.endsWith("-authorization") || normalizedKey.includes("token") ); } function rememberHeaders(url, input) { const headers = normalizeHeaders(input); if (headers.size === 0) { return; } headers.forEach((value, key) => { if (!isAuthorizationLikeHeader(key)) { bridgeState.globalHeaders.set(key, value); } }); for (const scopeKey of getScopeKeys(url)) { const bucket = bridgeState.scopedHeaders.get(scopeKey) || new Map(); mergeIntoBucket(bucket, headers); bridgeState.scopedHeaders.set(scopeKey, bucket); } } function buildHeaders(url, input, body) { const merged = new Headers(); const activeScopeKeys = getScopeKeys(url); bridgeState.globalHeaders.forEach((value, key) => { merged.set(key, value); }); for (const scopeKey of activeScopeKeys) { const bucket = bridgeState.scopedHeaders.get(scopeKey); if (!bucket) { continue; } bucket.forEach((value, key) => { merged.set(key, value); }); } normalizeHeaders(input).forEach((value, key) => { merged.set(key, value); }); if (!merged.has("accept")) { merged.set("accept", "application/json, text/plain, */*"); } if (!merged.has("x-requested-with")) { merged.set("x-requested-with", "XMLHttpRequest"); } if (activeScopeKeys.includes("scope:provider-proxy")) { const allowedProviderHeaders = new Set([ "accept", "authorization", "content-type", "spa-ts-authorization", "wwb-provider-authorization", "x-id-token", "x-requested-with", ]); for (const key of Array.from(merged.keys())) { if (key.endsWith("-authorization") && !allowedProviderHeaders.has(key)) { merged.delete(key); } } if (merged.has("wwb-provider-authorization")) { merged.delete("authorization"); } } if (isIntegrationOrigin(url)) { const allowedIntegrationHeaders = new Set([ "accept", "authorization", "content-type", "spa-ts-authorization", "wwb-provider-authorization", "x-id-token", "x-requested-with", ]); for (const key of Array.from(merged.keys())) { if (!allowedIntegrationHeaders.has(key)) { merged.delete(key); } } if (merged.has("wwb-provider-authorization")) { merged.delete("authorization"); } } if (!body) { merged.delete("content-type"); } return merged; } function buildHeaderSnapshot(options = {}) { const snapshot = new Headers(); const scopeUrl = options.url || window.location.href; const merged = buildHeaders(scopeUrl, options.headers, options.body); merged.forEach((value, key) => { snapshot.set(key, value); }); if (options.includeAllAuth) { bridgeState.scopedHeaders.forEach((bucket) => { bucket.forEach((value, key) => { if (isAuthorizationLikeHeader(key)) { snapshot.set(key, value); } }); }); } return Object.fromEntries(snapshot.entries()); } const trackedFetch = async function archPanelTrackedFetch(input, init) { const url = normalizeUrl(typeof input === "string" ? input : input?.url); if (shouldCapture(url)) { rememberHeaders(url, init?.headers || input?.headers); } return nativeFetch(input, init); }; window.fetch = trackedFetch; window.XMLHttpRequest.prototype.open = function archPanelTrackedOpen(method, url, ...rest) { this.__archPanelTrackedUrl = normalizeUrl(url); this.__archPanelTrackedHeaders = new Headers(); return originalOpen.call(this, method, url, ...rest); }; window.XMLHttpRequest.prototype.setRequestHeader = function archPanelTrackedSetRequestHeader(name, value) { if (this.__archPanelTrackedHeaders) { this.__archPanelTrackedHeaders.set(name, value); } return originalSetRequestHeader.call(this, name, value); }; window.XMLHttpRequest.prototype.send = function archPanelTrackedSend(body) { if (shouldCapture(this.__archPanelTrackedUrl)) { rememberHeaders(this.__archPanelTrackedUrl, this.__archPanelTrackedHeaders); } return originalSend.call(this, body); }; function requestViaOriginalXhr(url, options, headers) { return new Promise((resolve, reject) => { const xhr = new window.XMLHttpRequest(); originalOpen.call(xhr, options?.method || "GET", url, true); xhr.withCredentials = true; xhr.responseType = "text"; xhr.timeout = 60000; headers.forEach((value, key) => { originalSetRequestHeader.call(xhr, key, value); }); xhr.onload = () => { resolve({ ok: xhr.status >= 200 && xhr.status < 300, status: xhr.status, text: xhr.responseText || "", }); }; xhr.onerror = () => { reject(new Error("Failed to fetch")); }; xhr.onabort = () => { reject(new Error("Request aborted")); }; xhr.ontimeout = () => { reject(new Error(`Timed out while requesting ${url}`)); }; originalSend.call(xhr, options?.body ?? null); }); } window.addEventListener("message", async (event) => { if (event.source !== window) { return; } const message = event.data; if (!message || message.source !== MESSAGE_SOURCE) { return; } if (message.type === "ARCH_PANEL_HEADERS") { window.postMessage( { source: MESSAGE_SOURCE, type: "ARCH_PANEL_HEADERS_RESULT", requestId: message.requestId, headers: buildHeaderSnapshot(message.options), }, window.location.origin ); return; } if (message.type !== "ARCH_PANEL_FETCH") { return; } try { const mergedHeaders = buildHeaders( message.url, message.options?.headers, message.options?.body ); const useXhr = message.options?.transport === "xhr"; const response = useXhr ? await requestViaOriginalXhr(message.url, message.options, mergedHeaders) : await (async () => { const activeFetch = typeof window.fetch === "function" ? window.fetch.bind(window) : nativeFetch; const fetchResponse = await activeFetch(message.url, { method: message.options?.method || "GET", credentials: "include", headers: mergedHeaders, body: message.options?.body, }); return { ok: fetchResponse.ok, status: fetchResponse.status, text: await fetchResponse.text(), }; })(); const rawText = response.text; let payload; try { payload = rawText ? JSON.parse(rawText) : null; } catch { payload = rawText; } window.postMessage( { source: MESSAGE_SOURCE, type: "ARCH_PANEL_FETCH_RESULT", requestId: message.requestId, ok: response.ok, status: response.status, debug: { headerKeys: Array.from(mergedHeaders.keys()), scopeKeys: getScopeKeys(message.url), transport: useXhr ? "xhr" : "fetch", }, payload, }, window.location.origin ); } catch (error) { window.postMessage( { source: MESSAGE_SOURCE, type: "ARCH_PANEL_FETCH_RESULT", requestId: message.requestId, ok: false, status: 0, debug: { scopeKeys: getScopeKeys(message.url), transport: message.options?.transport || "fetch", }, error: error instanceof Error ? error.message : String(error), }, window.location.origin ); } }); window.postMessage( { source: MESSAGE_SOURCE, type: "ARCH_PANEL_BRIDGE_READY", }, window.location.origin ); })();