Commit inicial

This commit is contained in:
Paulo Porto
2026-04-30 12:38:36 -03:00
commit 45cfceff0e
5 changed files with 8182 additions and 0 deletions

471
page-bridge.js Normal file
View File

@@ -0,0 +1,471 @@
(() => {
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
);
})();