UNPKG

fortify2-js

Version:

MOST POWERFUL JavaScript Security Library! Military-grade cryptography + 19 enhanced object methods + quantum-resistant algorithms + perfect TypeScript support. More powerful than Lodash with built-in security.

646 lines (643 loc) 21.4 kB
'use strict'; /** * Runtime Security Verification Module * * This module provides functionality for verifying the security of the runtime * environment and detecting potential security issues or tampering attempts. * * It can detect debuggers, browser extensions that might intercept crypto operations, * compromised JavaScript environments, and other security threats. */ /** * Security issue type */ exports.SecurityIssueType = void 0; (function (SecurityIssueType) { SecurityIssueType["DEBUGGER"] = "debugger"; SecurityIssueType["EXTENSION_INTERFERENCE"] = "extension_interference"; SecurityIssueType["COMPROMISED_ENVIRONMENT"] = "compromised_environment"; SecurityIssueType["WEAK_RANDOM"] = "weak_random"; SecurityIssueType["PROTOTYPE_POLLUTION"] = "prototype_pollution"; SecurityIssueType["FUNCTION_HIJACKING"] = "function_hijacking"; SecurityIssueType["INSECURE_CONTEXT"] = "insecure_context"; SecurityIssueType["BROWSER_EXTENSION"] = "browser_extension"; SecurityIssueType["IFRAME_EMBEDDING"] = "iframe_embedding"; SecurityIssueType["DEVTOOLS_OPEN"] = "devtools_open"; })(exports.SecurityIssueType || (exports.SecurityIssueType = {})); /** * Verifies the security of the runtime environment * * @param options - Verification options * @returns Verification result */ function verifyRuntimeSecurity(options = {}) { const checks = []; const issues = []; // Set default options const opts = { checkDebugger: options.checkDebugger !== false, checkExtensions: options.checkExtensions !== false, checkEnvironment: options.checkEnvironment !== false, checkRandom: options.checkRandom !== false, checkPrototypePollution: options.checkPrototypePollution !== false, checkFunctionHijacking: options.checkFunctionHijacking !== false, checkSecureContext: options.checkSecureContext !== false, checkIframeEmbedding: options.checkIframeEmbedding !== false, checkDevTools: options.checkDevTools !== false, customChecks: options.customChecks || [] }; // Run checks if (opts.checkDebugger) { const check = checkForDebugger(); checks.push(check); if (!check.passed) { issues.push({ type: exports.SecurityIssueType.DEBUGGER, description: 'Debugger detected', severity: 70, mitigations: [ 'Close any debugging tools', 'Restart the browser', 'Use a different browser' ] }); } } if (opts.checkExtensions) { const check = checkForExtensionInterference(); checks.push(check); if (!check.passed) { issues.push({ type: exports.SecurityIssueType.EXTENSION_INTERFERENCE, description: 'Browser extension interference detected', severity: 60, mitigations: [ 'Disable browser extensions', 'Use incognito/private browsing mode', 'Use a different browser' ] }); } } if (opts.checkEnvironment) { const check = checkForCompromisedEnvironment(); checks.push(check); if (!check.passed) { issues.push({ type: exports.SecurityIssueType.COMPROMISED_ENVIRONMENT, description: 'Potentially compromised JavaScript environment', severity: 90, mitigations: [ 'Restart the browser', 'Update your browser', 'Scan for malware', 'Use a different device' ] }); } } if (opts.checkRandom) { const check = checkForWeakRandom(); checks.push(check); if (!check.passed) { issues.push({ type: exports.SecurityIssueType.WEAK_RANDOM, description: 'Weak random number generation detected', severity: 80, mitigations: [ 'Update your browser', 'Use a different browser', 'Use a more modern device' ] }); } } if (opts.checkPrototypePollution) { const check = checkForPrototypePollution(); checks.push(check); if (!check.passed) { issues.push({ type: exports.SecurityIssueType.PROTOTYPE_POLLUTION, description: 'Prototype pollution detected', severity: 85, mitigations: [ 'Check for malicious scripts', 'Clear browser cache and cookies', 'Update your browser' ] }); } } if (opts.checkFunctionHijacking) { const check = checkForFunctionHijacking(); checks.push(check); if (!check.passed) { issues.push({ type: exports.SecurityIssueType.FUNCTION_HIJACKING, description: 'Function hijacking detected', severity: 85, mitigations: [ 'Check for malicious scripts', 'Disable browser extensions', 'Update your browser' ] }); } } if (opts.checkSecureContext) { const check = checkForSecureContext(); checks.push(check); if (!check.passed) { issues.push({ type: exports.SecurityIssueType.INSECURE_CONTEXT, description: 'Insecure context (non-HTTPS)', severity: 75, mitigations: [ 'Use HTTPS instead of HTTP', 'Contact the website administrator' ] }); } } if (opts.checkIframeEmbedding) { const check = checkForIframeEmbedding(); checks.push(check); if (!check.passed) { issues.push({ type: exports.SecurityIssueType.IFRAME_EMBEDDING, description: 'Page is embedded in an iframe', severity: 50, mitigations: [ 'Access the website directly', 'Contact the website administrator' ] }); } } if (opts.checkDevTools) { const check = checkForDevTools(); checks.push(check); if (!check.passed) { issues.push({ type: exports.SecurityIssueType.DEVTOOLS_OPEN, description: 'Developer tools are open', severity: 40, mitigations: [ 'Close developer tools' ] }); } } // Run custom checks for (const customCheck of opts.customChecks) { try { const check = customCheck(); checks.push(check); if (!check.passed) { issues.push({ type: exports.SecurityIssueType.COMPROMISED_ENVIRONMENT, description: `Custom check failed: ${check.name}`, severity: 50 }); } } catch (e) { checks.push({ name: 'Custom check', description: 'A custom security check failed to run', passed: false, details: { error: e.message } }); issues.push({ type: exports.SecurityIssueType.COMPROMISED_ENVIRONMENT, description: 'A custom security check failed to run', severity: 30 }); } } // Calculate security score let score = 100; for (const issue of issues) { // Weight by severity score -= issue.severity / issues.length; } // Ensure score is between 0 and 100 score = Math.max(0, Math.min(100, Math.round(score))); return { secure: issues.length === 0, issues, score, checks }; } /** * Checks for the presence of a debugger * * @returns Check result */ function checkForDebugger() { let debuggerDetected = false; // Check execution time of a simple operation // Debuggers typically slow down execution const start = Date.now(); const end = Date.now(); const executionTime = end - start; // If execution is suspiciously slow, a debugger might be present // This is a heuristic and may have false positives if (executionTime > 50) { debuggerDetected = true; } // Check for debugger keyword (this will trigger if a debugger is attached) try { const originalDebugger = Function.prototype.constructor; const debuggerFn = new Function('debugger; return true;'); // If a debugger is attached, this will pause execution // We can detect this by measuring execution time const debuggerStart = Date.now(); debuggerFn(); const debuggerEnd = Date.now(); if (debuggerEnd - debuggerStart > 100) { debuggerDetected = true; } } catch (e) { // An error here might indicate a debugger or other interference debuggerDetected = true; } return { name: 'Debugger Detection', description: 'Checks for the presence of a debugger', passed: !debuggerDetected, details: { executionTime } }; } /** * Checks for browser extension interference * * @returns Check result */ function checkForExtensionInterference() { let interferenceDetected = false; const details = {}; // Check for modifications to crypto API if (typeof crypto !== 'undefined') { try { // Store original methods const originalGetRandomValues = crypto.getRandomValues; const originalSubtle = crypto.subtle; // Check if methods have been tampered with if (crypto.getRandomValues.toString().length < 50) { interferenceDetected = true; details.cryptoModified = true; } if (crypto.subtle && Object.keys(crypto.subtle).length !== Object.keys(Object.getPrototypeOf(crypto.subtle)).length) { interferenceDetected = true; details.subtleModified = true; } } catch (e) { // Error accessing crypto properties might indicate tampering interferenceDetected = true; details.cryptoError = e.message; } } // Check for content scripts if (typeof document !== 'undefined') { const scripts = document.querySelectorAll('script'); const suspiciousScripts = Array.from(scripts).filter(script => { const src = script.src || ''; return src.includes('chrome-extension://') || src.includes('moz-extension://') || src.includes('extension://'); }); if (suspiciousScripts.length > 0) { interferenceDetected = true; details.extensionScripts = suspiciousScripts.length; } } return { name: 'Extension Interference', description: 'Checks for browser extensions that might interfere with cryptographic operations', passed: !interferenceDetected, details }; } /** * Checks for a compromised JavaScript environment * * @returns Check result */ function checkForCompromisedEnvironment() { let compromised = false; const details = {}; // Check for overridden Object methods try { const originalCreate = Object.create; const originalDefineProperty = Object.defineProperty; const originalFreeze = Object.freeze; if (Object.create.toString() !== originalCreate.toString()) { compromised = true; details.createOverridden = true; } if (Object.defineProperty.toString() !== originalDefineProperty.toString()) { compromised = true; details.definePropertyOverridden = true; } if (Object.freeze.toString() !== originalFreeze.toString()) { compromised = true; details.freezeOverridden = true; } } catch (e) { compromised = true; details.objectError = e.message; } // Check for suspicious global variables const suspiciousGlobals = [ '__REACT_DEVTOOLS_GLOBAL_HOOK__', '__REDUX_DEVTOOLS_EXTENSION__', '__VUE_DEVTOOLS_GLOBAL_HOOK__', 'XSS', 'alert', 'prompt', 'confirm' ]; const foundGlobals = []; for (const global of suspiciousGlobals) { if (typeof window !== 'undefined' && window[global]) { foundGlobals.push(global); } } if (foundGlobals.length > 0) { details.suspiciousGlobals = foundGlobals; // Don't mark as compromised just for dev tools } return { name: 'Environment Integrity', description: 'Checks for a compromised JavaScript environment', passed: !compromised, details }; } /** * Checks for weak random number generation * * @returns Check result */ function checkForWeakRandom() { let weakRandom = false; const details = {}; // Check if crypto.getRandomValues is available if (typeof crypto === 'undefined' || typeof crypto.getRandomValues !== 'function') { weakRandom = true; details.noCrypto = true; } else { try { // Test crypto.getRandomValues const testArray = new Uint8Array(10); crypto.getRandomValues(testArray); // Check if all values are the same (very unlikely with proper RNG) const allSame = testArray.every(val => val === testArray[0]); if (allSame) { weakRandom = true; details.suspiciousOutput = true; } // Simple statistical test let zeros = 0; let ones = 0; for (let i = 0; i < testArray.length; i++) { for (let bit = 0; bit < 8; bit++) { if ((testArray[i] & (1 << bit)) === 0) { zeros++; } else { ones++; } } } // Extremely skewed distribution is suspicious const ratio = Math.max(zeros, ones) / Math.min(zeros, ones); if (ratio > 3) { weakRandom = true; details.skewedDistribution = true; details.ratio = ratio; } } catch (e) { weakRandom = true; details.cryptoError = e.message; } } return { name: 'Random Number Generation', description: 'Checks for weak random number generation', passed: !weakRandom, details }; } /** * Checks for prototype pollution * * @returns Check result */ function checkForPrototypePollution() { let polluted = false; const details = {}; // Check Object prototype const objectProto = Object.prototype; const originalToString = Object.prototype.toString; const originalHasOwnProperty = Object.prototype.hasOwnProperty; // Check for unexpected properties on Object.prototype const expectedProperties = [ 'constructor', 'toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', '__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__' ]; const unexpectedProperties = Object.getOwnPropertyNames(objectProto) .filter(prop => !expectedProperties.includes(prop)); if (unexpectedProperties.length > 0) { polluted = true; details.unexpectedProperties = unexpectedProperties; } // Check if toString or hasOwnProperty have been tampered with if (Object.prototype.toString !== originalToString) { polluted = true; details.toStringModified = true; } if (Object.prototype.hasOwnProperty !== originalHasOwnProperty) { polluted = true; details.hasOwnPropertyModified = true; } // Test for actual pollution by creating a new object const testObject = {}; JSON.parse('{"__proto__": {"pollutionTest": true}}'); if (testObject.pollutionTest === true) { polluted = true; details.activePrototypePollution = true; } return { name: 'Prototype Pollution', description: 'Checks for JavaScript prototype pollution', passed: !polluted, details }; } /** * Checks for function hijacking * * @returns Check result */ function checkForFunctionHijacking() { let hijacked = false; const details = {}; // Check Function constructor const originalFunction = Function; const originalFunctionToString = Function.prototype.toString; if (Function !== originalFunction) { hijacked = true; details.functionConstructorModified = true; } if (Function.prototype.toString !== originalFunctionToString) { hijacked = true; details.toStringModified = true; } // Check eval if (typeof eval !== 'function') { hijacked = true; details.evalMissing = true; } else { try { const testValue = 42; const evalResult = eval('testValue'); if (evalResult !== testValue) { hijacked = true; details.evalModified = true; } } catch (e) { hijacked = true; details.evalError = e.message; } } // Check setTimeout if (typeof setTimeout !== 'function') { hijacked = true; details.setTimeoutMissing = true; } else { const originalSetTimeout = setTimeout; if (setTimeout !== originalSetTimeout) { hijacked = true; details.setTimeoutModified = true; } } return { name: 'Function Hijacking', description: 'Checks for hijacked JavaScript functions', passed: !hijacked, details }; } /** * Checks if the page is running in a secure context (HTTPS) * * @returns Check result */ function checkForSecureContext() { let secure = true; const details = {}; if (typeof window !== 'undefined') { if (typeof window.isSecureContext === 'boolean') { secure = window.isSecureContext; details.isSecureContext = secure; } else if (typeof window.location === 'object') { secure = window.location.protocol === 'https:'; details.protocol = window.location.protocol; } } return { name: 'Secure Context', description: 'Checks if the page is running in a secure context (HTTPS)', passed: secure, details }; } /** * Checks if the page is embedded in an iframe * * @returns Check result */ function checkForIframeEmbedding() { let embedded = false; const details = {}; if (typeof window !== 'undefined') { try { embedded = window.self !== window.top; details.embedded = embedded; if (embedded && window.parent) { try { details.parentOrigin = document.referrer; } catch (e) { details.crossOrigin = true; } } } catch (e) { // Error accessing window.top usually means cross-origin iframe embedded = true; details.crossOrigin = true; } } return { name: 'Iframe Embedding', description: 'Checks if the page is embedded in an iframe', passed: !embedded, details }; } /** * Checks if developer tools are open * * @returns Check result */ function checkForDevTools() { let devToolsOpen = false; const details = {}; if (typeof window !== 'undefined') { // Method 1: Check window size const widthThreshold = window.outerWidth - window.innerWidth > 160; const heightThreshold = window.outerHeight - window.innerHeight > 160; if (widthThreshold || heightThreshold) { devToolsOpen = true; details.sizeDiscrepancy = true; } // Method 2: Debugger detection try { const element = new Image(); Object.defineProperty(element, 'id', { get: function () { devToolsOpen = true; details.elementInspection = true; return ''; } }); console.log(element); console.clear(); } catch (e) { // Ignore errors } } return { name: 'DevTools Detection', description: 'Checks if developer tools are open', passed: !devToolsOpen, details }; } exports.verifyRuntimeSecurity = verifyRuntimeSecurity; //# sourceMappingURL=runtime-verification.js.map