UNPKG

page-integrity-js

Version:

A library for monitoring and controlling DOM mutations and script execution, essential for PCI DSS compliance and security audits

243 lines 10.5 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; /** * Intercepts a script element and checks if it should be blocked. * @param script The script element to check * @param scriptBlocker The script blocker instance */ export function interceptScriptElement(script, scriptBlocker) { return __awaiter(this, void 0, void 0, function* () { if (!scriptBlocker) { console.error('ScriptBlocker is not initialized'); return; } try { let result; if (script.src) { result = yield scriptBlocker.shouldBlockScript(script.src, ''); } else if (script.textContent) { result = yield scriptBlocker.shouldBlockScript('inline', script.textContent); } if (result === null || result === void 0 ? void 0 : result.blocked) { // Remove the script element script.remove(); console.warn(`Blocked script: ${script.src || 'inline'} - ${result.reason}`); } } catch (error) { console.error('Error intercepting script:', error); } }); } /** * Intercepts global methods that can execute scripts. * @param scriptBlocker The script blocker instance */ export function interceptGlobalMethods(scriptBlocker) { if (!scriptBlocker) { console.error('ScriptBlocker is not initialized'); return; } const originalEval = window.eval; const originalFunction = Function; const originalSetTimeout = window.setTimeout; const originalSetInterval = window.setInterval; const originalXHROpen = XMLHttpRequest.prototype.open; const originalXHRSend = XMLHttpRequest.prototype.send; const originalFetch = window.fetch; window.eval = function (code) { const result = scriptBlocker.shouldBlockScript('eval', code); if (result instanceof Promise) { return result.then((res) => { if (res === null || res === void 0 ? void 0 : res.blocked) { console.warn(`Blocked eval: ${res.reason}`); return undefined; } return originalEval.call(window, code); }); } return originalEval.call(window, code); }; Function = function (...args) { const code = args[args.length - 1]; const result = scriptBlocker.shouldBlockScript('Function', code); if (result instanceof Promise) { console.warn('Async script blocking not supported for Function constructor'); return function () { return undefined; }; } const blockResult = result; if (blockResult === null || blockResult === void 0 ? void 0 : blockResult.blocked) { console.warn(`Blocked Function: ${blockResult.reason}`); return function () { return undefined; }; } return originalFunction.apply(null, args); }; window.setTimeout = function (handler, timeout, ...args) { if (typeof handler === 'string') { const result = scriptBlocker.shouldBlockScript('setTimeout', handler); if (result instanceof Promise) { const promise = result.then((res) => { if (res === null || res === void 0 ? void 0 : res.blocked) { console.warn(`Blocked setTimeout: ${res.reason}`); return 0; } return originalSetTimeout.call(window, handler, timeout, ...args); }); return promise; } } return originalSetTimeout.call(window, handler, timeout, ...args); }; window.setInterval = function (handler, timeout, ...args) { if (typeof handler === 'string') { const result = scriptBlocker.shouldBlockScript('setInterval', handler); if (result instanceof Promise) { const promise = result.then((res) => { if (res === null || res === void 0 ? void 0 : res.blocked) { console.warn(`Blocked setInterval: ${res.reason}`); return 0; } return originalSetInterval.call(window, handler, timeout, ...args); }); return promise; } } return originalSetInterval.call(window, handler, timeout, ...args); }; XMLHttpRequest.prototype.open = function (method_1, url_1) { return __awaiter(this, arguments, void 0, function* (method, url, async = true, username = null, password = null) { if (url.toString().endsWith('.js')) { try { const result = yield scriptBlocker.shouldBlockScript(url.toString(), ''); if (result === null || result === void 0 ? void 0 : result.blocked) { console.warn(`Blocked XHR: ${result.reason}`); throw new Error(`Blocked script: ${result.reason}`); } } catch (error) { console.error('Error in XHR open:', error); throw error; } } return originalXHROpen.call(this, method, url, async, username, password); }); }; XMLHttpRequest.prototype.send = function (body) { return originalXHRSend.call(this, body); }; window.fetch = function (input, init) { return __awaiter(this, void 0, void 0, function* () { const url = input instanceof Request ? input.url : input.toString(); if (url.endsWith('.js')) { try { const result = yield scriptBlocker.shouldBlockScript(url, ''); if (result === null || result === void 0 ? void 0 : result.blocked) { console.warn(`Blocked fetch: ${result.reason}`); throw new Error(`Blocked script: ${result.reason}`); } } catch (error) { console.error('Error in fetch:', error); throw error; } } return originalFetch.call(window, input, init); }); }; } /** * Manages script interception and monitoring. */ export class ScriptInterceptor { constructor(scriptBlocker) { this._isRunning = false; this.scriptBlocker = scriptBlocker; this.originalCreateElement = document.createElement; this.originalSetAttribute = Element.prototype.setAttribute; this.originalAppendChild = Node.prototype.appendChild; this.originalInsertBefore = Node.prototype.insertBefore; this.originalReplaceChild = Node.prototype.replaceChild; } static createInstance(scriptBlocker) { return new ScriptInterceptor(scriptBlocker); } start() { if (this._isRunning) return; this._isRunning = true; this.interceptCreateElement(); this.interceptSetAttribute(); this.interceptAppendChild(); this.interceptInsertBefore(); this.interceptReplaceChild(); interceptGlobalMethods(this.scriptBlocker); } stop() { if (!this._isRunning) return; this._isRunning = false; // Restore original methods document.createElement = this.originalCreateElement; Element.prototype.setAttribute = this.originalSetAttribute; Node.prototype.appendChild = this.originalAppendChild; Node.prototype.insertBefore = this.originalInsertBefore; Node.prototype.replaceChild = this.originalReplaceChild; } interceptCreateElement() { const self = this; document.createElement = function (tagName, options) { const element = self.originalCreateElement.call(document, tagName, options); if (tagName.toLowerCase() === 'script') { const script = element; Promise.resolve().then(() => interceptScriptElement(script, self.scriptBlocker)); } return element; }; } interceptSetAttribute() { const self = this; Element.prototype.setAttribute = function (name, value) { self.originalSetAttribute.call(this, name, value); if (this instanceof HTMLScriptElement) { const script = this; Promise.resolve().then(() => interceptScriptElement(script, self.scriptBlocker)); } }; } interceptAppendChild() { const self = this; Node.prototype.appendChild = function (newChild) { if (newChild instanceof HTMLScriptElement) { Promise.resolve().then(() => interceptScriptElement(newChild, self.scriptBlocker)); } return self.originalAppendChild.call(this, newChild); }; } interceptInsertBefore() { const self = this; Node.prototype.insertBefore = function (newChild, refChild) { if (newChild instanceof HTMLScriptElement) { Promise.resolve().then(() => interceptScriptElement(newChild, self.scriptBlocker)); } return self.originalInsertBefore.call(this, newChild, refChild); }; } interceptReplaceChild() { const self = this; Node.prototype.replaceChild = function (newChild, oldChild) { if (newChild instanceof HTMLScriptElement) { Promise.resolve().then(() => interceptScriptElement(newChild, self.scriptBlocker)); } return self.originalReplaceChild.call(this, newChild, oldChild); }; } } //# sourceMappingURL=script-interceptor.js.map