UNPKG

nehonix-uri-processor

Version:

A powerful URI processor for encoding, decoding, and analyzing URI data securely.

1,067 lines 60.9 kB
import { toAscii } from "idna-uts46-hx"; import { AppLogger } from "../common/AppLogger"; import { PATTERNS } from "../utils/attacks_parttens"; import { MaliciousPatternType, } from "./MaliciousPatterns.service"; import NDS from "./NehonixDec.service"; /** * Enhanced service for detecting various malicious patterns in URLs and general input * Nehonix Malicious Parttens Service => NMPS * Nehonix Security Service => NSS */ export class NSS { static isSafeHighEntropy(input) { // JWT pattern if (/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(input)) { return true; } // Data URI for images if (/^data:image\/(png|jpeg|gif);base64,[A-Za-z0-9+/=]+$/.test(input)) { return true; } // API key pattern (example: 32+ alphanumeric characters) if (/^[A-Za-z0-9]{32,}$/.test(input)) { return true; } return false; } /** * Analyzes input for malicious patterns and returns detailed detection results * * @param input - The string to analyze * @param options - Configuration options for detection * @returns Detailed analysis result */ static detectMaliciousPatterns(receivedInput, options = {}) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k; try { // Set default options const opts = { minScore: (_a = options.minScore) !== null && _a !== void 0 ? _a : 50, debug: (_b = options.debug) !== null && _b !== void 0 ? _b : false, ignorePatterns: (_c = options.ignorePatterns) !== null && _c !== void 0 ? _c : [], sensitivity: (_d = options.sensitivity) !== null && _d !== void 0 ? _d : 1.0, customPatterns: (_e = options.customPatterns) !== null && _e !== void 0 ? _e : [], enableContextualAnalysis: (_f = options.enableContextualAnalysis) !== null && _f !== void 0 ? _f : true, enableEntropyAnalysis: (_g = options.enableEntropyAnalysis) !== null && _g !== void 0 ? _g : true, enableStatisticalAnalysis: (_h = options.enableStatisticalAnalysis) !== null && _h !== void 0 ? _h : true, componentSensitivity: (_j = options.componentSensitivity) !== null && _j !== void 0 ? _j : { protocol: 1.0, hostname: 1.2, path: 1.0, query: 1.5, fragment: 1.3, }, characterSet: (_k = options.characterSet) !== null && _k !== void 0 ? _k : "all", }; const parsedInput = NDS.decodeAnyToPlaintext(receivedInput).val(); if (opts.debug) { AppLogger.debug("NMPS: Analyzing input with options:", opts); } // Normalize Unicode input const normalizedInput = parsedInput.normalize("NFC"); // Use normalized input for checks const inputsToCheck = [normalizedInput, parsedInput].filter((i, idx, arr) => i !== arr[idx - 1]); let decodedInput = ""; let totalScore = 0; let contextAnalysis; const detectedPatterns = []; inputsToCheck.forEach((ipt) => { const input = NDS.decodeAnyToPlaintext(ipt).val(); // Store all detected patterns decodedInput = input; const encodingLayers = this.detectEncodingLayers(input); if (encodingLayers > 0) { let tempInput = input; for (let i = 0; i < encodingLayers; i++) { try { tempInput = decodeURIComponent(tempInput.replace(/%25/g, "%")); tempInput = tempInput .replace(/&lt;/g, "<") .replace(/&gt;/g, ">") .replace(/&amp;/g, "&") .replace(/&quot;/g, '"') .replace(/&#x[0-9a-fA-F]+;/g, (match) => { const hex = match.substring(3, match.length - 1); return String.fromCodePoint(parseInt(hex, 16)); }) .replace(/&#\d+;/g, (match) => { const decimal = match.substring(2, match.length - 1); return String.fromCodePoint(parseInt(decimal, 10)); }); tempInput = tempInput.replace(/\\u([0-9a-fA-F]{4})/g, (_, hex) => { return String.fromCodePoint(parseInt(hex, 16)); }); } catch (_a) { } decodedInput = tempInput; } } if (opts.enableEntropyAnalysis && !this.isSafeHighEntropy(input)) { const entropyScore = this.calculateEntropy(input); if (entropyScore > 4.5) { detectedPatterns.push({ type: MaliciousPatternType.ENCODED_PAYLOAD, pattern: "high_entropy", location: "full_input", severity: "medium", confidence: "medium", description: "High entropy content may indicate obfuscated payload", contextScore: entropyScore, }); if (contextAnalysis) { contextAnalysis.entropyScore = entropyScore; } } } // Check for SQL injection patterns if (!opts.ignorePatterns.includes(MaliciousPatternType.SQL_INJECTION)) { this.checkPatterns(input, this.SQL_INJECTION_PATTERNS, MaliciousPatternType.SQL_INJECTION, "SQL injection attempt", "high", detectedPatterns, opts); } // Check for XSS patterns if (!opts.ignorePatterns.includes(MaliciousPatternType.XSS)) { this.checkPatterns(input, this.XSS_PATTERNS, MaliciousPatternType.XSS, "Cross-site scripting attempt", "high", detectedPatterns, opts); } // Check for RFI patterns if (!opts.ignorePatterns.includes(MaliciousPatternType.RFI)) { this.checkPatterns(input, this.RFI_PATTERNS, MaliciousPatternType.RFI, "Remote file inclusion attempt", "high", detectedPatterns, opts); } // Check for command injection patterns if (!opts.ignorePatterns.includes(MaliciousPatternType.COMMAND_INJECTION)) { this.checkPatterns(input, this.COMMAND_INJECTION_PATTERNS, MaliciousPatternType.COMMAND_INJECTION, "Command injection attempt", "high", detectedPatterns, opts); } // Check for path traversal patterns if (!opts.ignorePatterns.includes(MaliciousPatternType.PATH_TRAVERSAL)) { this.checkPatterns(input, this.PATH_TRAVERSAL_PATTERNS, MaliciousPatternType.PATH_TRAVERSAL, "Path traversal attempt", "high", detectedPatterns, opts); } // Check for open redirect patterns if (!opts.ignorePatterns.includes(MaliciousPatternType.OPEN_REDIRECT)) { this.checkPatterns(input, this.OPEN_REDIRECT_PATTERNS, MaliciousPatternType.OPEN_REDIRECT, "Open redirect attempt", "medium", detectedPatterns, opts); } // Check for SSRF patterns if (!opts.ignorePatterns.includes(MaliciousPatternType.SSRF)) { this.checkPatterns(input, this.SSRF_PATTERNS, MaliciousPatternType.SSRF, "Server-side request forgery attempt", "high", detectedPatterns, opts); } // Check for CRLF injection patterns if (!opts.ignorePatterns.includes(MaliciousPatternType.CRLF_INJECTION)) { this.checkPatterns(input, this.CRLF_INJECTION_PATTERNS, MaliciousPatternType.CRLF_INJECTION, "CRLF injection attempt", "medium", detectedPatterns, opts); } // Check for template injection patterns if (!opts.ignorePatterns.includes(MaliciousPatternType.TEMPLATE_INJECTION)) { this.checkPatterns(input, this.TEMPLATE_INJECTION_PATTERNS, MaliciousPatternType.TEMPLATE_INJECTION, "Template injection attempt", "high", detectedPatterns, opts); } // Check for NoSQL injection patterns if (!opts.ignorePatterns.includes(MaliciousPatternType.NOSQL_INJECTION)) { this.checkPatterns(input, this.NOSQL_INJECTION_PATTERNS, MaliciousPatternType.NOSQL_INJECTION, "NoSQL injection attempt", "high", detectedPatterns, opts); } // Check for GraphQL injection patterns if (!opts.ignorePatterns.includes(MaliciousPatternType.GRAPHQL_INJECTION)) { this.checkPatterns(input, this.GRAPHQL_INJECTION_PATTERNS, MaliciousPatternType.GRAPHQL_INJECTION, "GraphQL injection attempt", "high", detectedPatterns, opts); } // Check for encoded payload patterns if (!opts.ignorePatterns.includes(MaliciousPatternType.ENCODED_PAYLOAD)) { this.checkPatterns(input, this.ENCODED_PAYLOAD_PATTERNS, MaliciousPatternType.ENCODED_PAYLOAD, "Suspicious encoded payload", "medium", detectedPatterns, opts); } // Check for suspicious TLD patterns if (!opts.ignorePatterns.includes(MaliciousPatternType.SUSPICIOUS_TLD)) { this.checkPatterns(input, this.SUSPICIOUS_TLD_PATTERNS, MaliciousPatternType.SUSPICIOUS_TLD, "Suspicious TLD detected", "low", detectedPatterns, opts); } // Check for homograph attack patterns if (!opts.ignorePatterns.includes(MaliciousPatternType.HOMOGRAPH_ATTACK)) { this.checkPatterns(input, this.HOMOGRAPH_ATTACK_PATTERNS, MaliciousPatternType.HOMOGRAPH_ATTACK, "Potential homograph attack", "medium", detectedPatterns, opts); } // Check for multi-encoding patterns if (!opts.ignorePatterns.includes(MaliciousPatternType.MULTI_ENCODING)) { this.checkPatterns(input, this.MULTI_ENCODING_PATTERNS, MaliciousPatternType.MULTI_ENCODING, "Multi-layer encoding detected", "medium", detectedPatterns, opts); } // Add encoding layer detection if (encodingLayers > 1) { detectedPatterns.push({ type: MaliciousPatternType.MULTI_ENCODING, pattern: "multi_layer_encoding", location: "full_input", severity: "medium", confidence: "high", description: `Multiple encoding layers detected (${encodingLayers})`, matchedValue: input, contextScore: encodingLayers * 1.5, }); } // Check for suspicious parameter names if (!opts.ignorePatterns.includes(MaliciousPatternType.SUSPICIOUS_PARAMETER)) { this.checkSuspiciousParameters(input, detectedPatterns, opts); } // Check custom patterns if provided if (opts.customPatterns && opts.customPatterns.length > 0) { for (const customPattern of opts.customPatterns) { const match = customPattern.pattern.exec(input); if (match) { const matchedValue = match[0]; const confidence = this.calculateConfidence(matchedValue, input); const contextScore = opts.enableContextualAnalysis ? this.calculateContextScore(match, input) : undefined; detectedPatterns.push({ type: customPattern.type, pattern: customPattern.pattern.toString(), location: `index: ${match.index}`, severity: customPattern.severity, confidence, description: customPattern.description, matchedValue, contextScore, }); } } } // Perform contextual analysis if enabled if (opts.enableContextualAnalysis && detectedPatterns.length > 0) { contextAnalysis = this.performContextualAnalysis(detectedPatterns, input, opts); } // Calculate entropy if enabled if (opts.enableEntropyAnalysis) { const entropyScore = this.calculateEntropy(input); // High entropy can indicate obfuscation if (entropyScore > 4.5) { detectedPatterns.push({ type: MaliciousPatternType.ENCODED_PAYLOAD, pattern: "high_entropy", location: "full_input", severity: "medium", confidence: "medium", description: "High entropy content may indicate obfuscated payload", contextScore: entropyScore, }); if (contextAnalysis) { contextAnalysis.entropyScore = entropyScore; } } } // Calculate total score based on detected patterns totalScore = this.calculateTotalScore(detectedPatterns, opts.sensitivity); }); // Determine overall confidence level const confidence = this.determineConfidence(totalScore, detectedPatterns.length); // Generate appropriate recommendation const recommendation = this.generateRecommendation(detectedPatterns, totalScore); return { isMalicious: totalScore >= opts.minScore, detectedPatterns, score: totalScore, confidence, recommendation, contextAnalysis, }; } catch (error) { AppLogger.error("Error in NMPS.detectMaliciousPatterns:", error); return { isMalicious: false, detectedPatterns: [], score: 0, confidence: "low", recommendation: "Error analyzing input. Please try again with simplified input.", }; } } /** * Analyzes a URL for malicious patterns with specific sensitivity per URL component * * @param url - The URL to analyze * @param options - Configuration options for detection * @returns Detailed analysis result */ static async analyzeUrl(url, options = {}) { var _a, _b, _c; try { const parsedUrl = new URL(url); const detectedPatterns = []; const componentResults = {}; const opts = { ...options }; // Normalize hostname for homograph detection const normalizedHostname = toAscii(parsedUrl.hostname); if (normalizedHostname !== parsedUrl.hostname) { detectedPatterns.push({ type: MaliciousPatternType.HOMOGRAPH_ATTACK, pattern: "punycode_conversion", location: `hostname:${parsedUrl.hostname}`, severity: "medium", confidence: "high", description: `Hostname converted from punycode: ${parsedUrl.hostname} -> ${normalizedHostname}`, matchedValue: parsedUrl.hostname, }); } // Analyze each component separately const components = [ { type: "protocol", value: parsedUrl.protocol }, { type: "hostname", value: parsedUrl.hostname }, { type: "path", value: parsedUrl.pathname }, { type: "query", value: parsedUrl.search }, { type: "fragment", value: parsedUrl.hash }, ]; // Analyze each component with its custom sensitivity for (const component of components) { if (component.value) { let valueToCheck = await NDS.asyncDecodeAnyToPlainText(component.value, { maxIterations: 20, }).then((res) => res.val()); // Decode fragment specifically if (component.type === "fragment" && valueToCheck.startsWith("#")) { valueToCheck = valueToCheck.substring(1); try { valueToCheck = decodeURIComponent(valueToCheck); } catch (_d) { } } if (opts.componentSensitivity && component.type) { opts.sensitivity = (options.sensitivity || 1.0) * (((_a = options === null || options === void 0 ? void 0 : options.componentSensitivity) === null || _a === void 0 ? void 0 : _a[component.type]) || 1.0); } const result = this.detectMaliciousPatterns(valueToCheck, opts); componentResults[component.type] = result; if (result.detectedPatterns.length > 0) { for (const pattern of result.detectedPatterns) { detectedPatterns.push({ ...pattern, location: `${component.type}:${pattern.location}`, }); } } } } // Check for mixed scripts in hostname const mixedScriptPattern = /([a-zA-Z])([\u0400-\u04FF])/i; if (mixedScriptPattern.test(parsedUrl.hostname)) { detectedPatterns.push({ type: MaliciousPatternType.HOMOGRAPH_ATTACK, pattern: "mixed_script", location: `hostname:${parsedUrl.hostname}`, severity: "medium", confidence: "high", description: "Hostname contains mixed Latin and non-Latin scripts, potential homograph attack", matchedValue: parsedUrl.hostname, }); } // Special check for URL parameters if (parsedUrl.searchParams) { for (const [key, value] of parsedUrl.searchParams.entries()) { // Check for suspicious parameter names if (this.SUSPICIOUS_PARAMETER_NAMES.includes(key.toLowerCase())) { detectedPatterns.push({ type: MaliciousPatternType.SUSPICIOUS_PARAMETER, pattern: "suspicious_param_name", location: `query:parameter_name:${key}`, severity: "low", confidence: "medium", description: `Suspicious parameter name "${key}" detected`, matchedValue: key, }); } // Check for malicious parameter values const valueResult = this.detectMaliciousPatterns(value, { ...opts, sensitivity: (options.sensitivity || 1.0) * ((((_b = options.componentSensitivity) === null || _b === void 0 ? void 0 : _b.query) || 1.0) * 1.2), // Extra sensitivity for parameter values }); if (valueResult.detectedPatterns.length > 0) { for (const pattern of valueResult.detectedPatterns) { AppLogger.log("pattern", pattern); detectedPatterns.push({ ...pattern, location: `query:parameter_value:${key}:${pattern.location}`, }); } } } } // Calculate combined score with weighted components let totalScore = this.calculateTotalScore(detectedPatterns, options.sensitivity || 1.0); // Determine overall confidence level const confidence = this.determineConfidence(totalScore, detectedPatterns.length); // Generate contextualAnalysis if multiple components have issues let contextAnalysis; if (((_c = options.enableContextualAnalysis) !== null && _c !== void 0 ? _c : true) && detectedPatterns.length > 0) { contextAnalysis = this.performContextualAnalysis(detectedPatterns, url, options); // Add cross-component analysis if (detectedPatterns.length > 0) { // Find related patterns across components const relatedGroups = this.findRelatedPatternGroups(detectedPatterns); if (contextAnalysis && relatedGroups.length > 0) { contextAnalysis.relatedPatterns = relatedGroups; // Add additional score for sophisticated multi-component attacks if (relatedGroups.some((g) => g.riskMultiplier > 1.5)) { totalScore *= 1.25; } } } } // Generate URL-specific recommendation const recommendation = this.generateUrlRecommendation(detectedPatterns, componentResults); return { isMalicious: totalScore >= (options.minScore || 50), detectedPatterns, score: totalScore, confidence, recommendation, contextAnalysis, }; } catch (error) { AppLogger.error("Error in NMPS.analyzeUrl:", error); // Fall back to basic analysis if URL parsing fails if (error instanceof TypeError && error.message.includes("Invalid URL")) { AppLogger.warn("Invalid URL format, falling back to basic analysis"); return this.detectMaliciousPatterns(url, options); } return { isMalicious: false, detectedPatterns: [], score: 0, confidence: "low", recommendation: "Error analyzing URL. Please verify the URL format is correct.", }; } } /** * Generates a recommendation specifically for URLs based on detected patterns */ static generateUrlRecommendation(detectedPatterns, componentResults) { if (detectedPatterns.length === 0) { return "No suspicious patterns detected in the URL."; } const componentIssues = []; let hasCriticalIssue = false; // Check severity of issues by component Object.entries(componentResults).forEach(([component, result]) => { if (result.detectedPatterns.length > 0) { const highSeverity = result.detectedPatterns.some((p) => p.severity === "high"); if (highSeverity) { hasCriticalIssue = true; componentIssues.push(`Critical issues found in the ${component} component`); } else { componentIssues.push(`Suspicious patterns found in the ${component} component`); } } }); // Generate overall recommendation if (hasCriticalIssue) { return `This URL contains potentially malicious patterns. ${componentIssues.join(". ")}. Consider blocking this URL and scanning related systems for compromise.`; } else if (componentIssues.length > 1) { return `This URL has multiple suspicious components: ${componentIssues.join("; ")}. Recommend further review before processing this URL.`; } else { return `This URL contains suspicious patterns. ${componentIssues[0]}. Proceed with caution and validate the URL source.`; } } /** * Finds related patterns across different components that might indicate a sophisticated attack */ static findRelatedPatternGroups(patterns) { const groups = []; // Find cross-site scripting patterns across multiple components const xssPatterns = patterns.filter((p) => p.type === MaliciousPatternType.XSS); if (xssPatterns.length > 1) { groups.push({ patterns: [MaliciousPatternType.XSS], description: "Multiple XSS vectors detected across different URL components", riskMultiplier: 1.5, }); } // Find SQL injection patterns across multiple components const sqlInjectionPatterns = patterns.filter((p) => p.type === MaliciousPatternType.SQL_INJECTION); if (sqlInjectionPatterns.length > 1) { groups.push({ patterns: [MaliciousPatternType.SQL_INJECTION], description: "Multiple SQL injection vectors detected across different URL components", riskMultiplier: 1.6, }); } // Find encoding obfuscation techniques const encodingPatterns = patterns.filter((p) => p.type === MaliciousPatternType.ENCODED_PAYLOAD || p.type === MaliciousPatternType.MULTI_ENCODING); // Check for encoding + injection combination (sophisticated attack) if (encodingPatterns.length > 0) { const injectionPatterns = patterns.filter((p) => p.type === MaliciousPatternType.SQL_INJECTION || p.type === MaliciousPatternType.XSS || p.type === MaliciousPatternType.COMMAND_INJECTION || p.type === MaliciousPatternType.TEMPLATE_INJECTION || p.type === MaliciousPatternType.NOSQL_INJECTION); if (injectionPatterns.length > 0) { groups.push({ patterns: [ MaliciousPatternType.ENCODED_PAYLOAD, ...injectionPatterns.map((p) => p.type), ], description: "Encoded payload combined with injection attempt - sophisticated evasion technique", riskMultiplier: 2.0, }); } } // Detect potential combination attacks const hasRedirect = patterns.some((p) => p.type === MaliciousPatternType.OPEN_REDIRECT); const hasXss = patterns.some((p) => p.type === MaliciousPatternType.XSS); if (hasRedirect && hasXss) { groups.push({ patterns: [ MaliciousPatternType.OPEN_REDIRECT, MaliciousPatternType.XSS, ], description: "Combined redirect and XSS attack vector detected", riskMultiplier: 1.8, }); } // Detect protocol confusion attacks const hasProtocolConfusion = patterns.some((p) => p.type === MaliciousPatternType.PROTOCOL_CONFUSION); const hasSsrf = patterns.some((p) => p.type === MaliciousPatternType.SSRF); if (hasProtocolConfusion && hasSsrf) { groups.push({ patterns: [ MaliciousPatternType.PROTOCOL_CONFUSION, MaliciousPatternType.SSRF, ], description: "Protocol confusion combined with SSRF attempt", riskMultiplier: 1.9, }); } return groups; } /** * Checks a string against a set of regex patterns for a specific attack type */ static checkPatterns(input, patterns, type, description, severity, results, options) { for (const pattern of patterns) { const match = pattern.exec(input); if (match) { const matchedValue = match[0]; const confidence = this.calculateConfidence(matchedValue, input); const contextScore = options.enableContextualAnalysis ? this.calculateContextScore(match, input) : undefined; results.push({ type, pattern: pattern.toString(), location: `index: ${match.index}`, severity, confidence, description, matchedValue, contextScore, }); if (options.debug) { AppLogger.debug(`NMPS: Detected ${type} pattern: '${matchedValue}' at index ${match.index}`); } // Only report the first match for each pattern to avoid redundancy break; } } } /** * Checks for suspicious parameter names in a URL */ static checkSuspiciousParameters(input, results, options) { try { // Try to extract parameter names from URL query string let params = []; // Check if input contains URL parameters const queryStart = input.indexOf("?"); if (queryStart !== -1) { const queryString = input.substring(queryStart + 1); const pairs = queryString.split("&"); for (const pair of pairs) { const eqIndex = pair.indexOf("="); if (eqIndex !== -1) { const paramName = pair.substring(0, eqIndex).toLowerCase(); params.push(paramName); } } } // Check parameter names against suspicious list for (const param of params) { if (this.SUSPICIOUS_PARAMETER_NAMES.includes(param)) { results.push({ type: MaliciousPatternType.SUSPICIOUS_PARAMETER, pattern: "suspicious_param_name", location: `parameter: ${param}`, severity: "low", confidence: "medium", description: `Suspicious parameter name "${param}" detected`, matchedValue: param, }); if (options.debug) { AppLogger.debug(`NMPS: Detected suspicious parameter: '${param}'`); } } } } catch (error) { AppLogger.error("Error in checkSuspiciousParameters:", error); } } /** * Calculates confidence level for a pattern match based on match characteristics */ // In NehonixSecurity.service.txt static calculateConfidence(matchedValue, fullInput) { const matchRatio = matchedValue.length / fullInput.length; // High-confidence patterns const highConfidencePatterns = [ /<script/i, /javascript:/i, /onload=/i, /alert\(/i, /select.*from/i, /union.*select/i, /whoami/i, /ping/i, /etc\/passwd/i, /localhost/i, ]; if (highConfidencePatterns.some((pattern) => pattern.test(matchedValue))) { return "high"; } if (matchRatio < 0.1 && matchedValue.length < 5) { return "low"; } if (this.isLikelyFalsePositive(matchedValue, fullInput)) { return "low"; } if (matchedValue.length >= 10 || matchRatio > 0.5) { return "high"; } return "medium"; } /** * Checks if a match is likely a false positive based on context */ // In NehonixSecurity.service.txt static isLikelyFalsePositive(match, fullInput) { const falsePositiveContexts = [ /https?:\/\/[^\/]+\/documentation\/examples?\/.*sql/i, /https?:\/\/[^\/]+\/blog\/.*security/i, /code example/i, /security training/i, /\/\/\s*Example/i, // Ignore common URL parameters /\?(sort|filter|category|page|limit|offset)=/i, ]; for (const context of falsePositiveContexts) { if (context.test(fullInput)) { return true; } } // Ignore matches in legitimate query parameters if (/[&?]sort=/.test(fullInput) && match.includes("sort")) { return true; } /** * TODO: Add a feed back for false positives * */ return false; } /** * Calculates additional context score for pattern matches */ static calculateContextScore(match, fullInput) { let score = 1.0; const start = Math.max(0, match.index - 10); const end = Math.min(fullInput.length, match.index + match[0].length + 10); const context = fullInput.substring(start, end); if (/\\x|\\u|%u|%[0-9a-f]{2}|&#\d+;|&#x[0-9a-f]+;/i.test(context)) { score *= 1.5; } if (/\/\*|\*\/|--|#|\/{2}/i.test(context)) { score *= 1.3; } const criticalPatterns = [ /etc\/passwd/i, /localhost/i, /192\.168\.\d{1,3}\.\d{1,3}/i, /file=https?:\/\//i, ]; for (const pattern of criticalPatterns) { if (pattern.test(context)) { score *= 1.5; break; } } return score; } /** * Performs contextual analysis on detected patterns to improve detection accuracy */ static performContextualAnalysis(patterns, fullInput, options) { // Find relationships between detected patterns const relatedGroups = this.findRelatedPatternGroups(patterns); // Calculate entropy score const entropyScore = this.calculateEntropy(fullInput); // Detect potential encoding layers const encodingLayers = this.detectEncodingLayers(fullInput); // Calculate anomaly score based on character distribution const anomalyScore = this.calculateAnomalyScore(fullInput); return { relatedPatterns: relatedGroups, entropyScore, anomalyScore, encodingLayers, }; } /** * Calculates Shannon entropy of a string to detect random or encoded content * Higher entropy often indicates encryption or encoding */ static calculateEntropy(input) { const len = input.length; const charCounts = {}; // Count character occurrences for (let i = 0; i < len; i++) { const char = input[i]; charCounts[char] = (charCounts[char] || 0) + 1; } // Calculate entropy let entropy = 0; for (const char in charCounts) { const probability = charCounts[char] / len; entropy -= probability * Math.log2(probability); } return entropy; } /** * Detects number of potential encoding layers in a string */ static detectEncodingLayers(input) { let layers = 0; let currentInput = input; const maxLayers = 5; // Prevent infinite loops let attempts = 0; while (attempts < maxLayers) { let decoded = currentInput; // URL decoding if (/%[0-9A-Fa-f]{2}/.test(decoded)) { try { decoded = decodeURIComponent(decoded); if (decoded !== currentInput) { layers++; currentInput = decoded; } } catch (_a) { } } // HTML entities if (/&[#a-zA-Z0-9]+;/.test(decoded)) { const temp = decoded .replace(/&lt;/g, "<") .replace(/&gt;/g, ">") .replace(/&amp;/g, "&") .replace(/&quot;/g, '"') .replace(/&#x[0-9a-fA-F]+;/g, (match) => { try { const hex = match.substring(3, match.length - 1); return String.fromCodePoint(parseInt(hex, 16)); } catch (_a) { return match; } }) .replace(/&#\d+;/g, (match) => { try { const decimal = match.substring(2, match.length - 1); return String.fromCodePoint(parseInt(decimal, 10)); } catch (_a) { return match; } }); if (temp !== decoded) { layers++; currentInput = temp; } } // Unicode escapes if (/\\u[0-9a-fA-F]{4}/.test(decoded)) { try { const temp = decoded.replace(/\\u([0-9a-fA-F]{4})/g, (_, hex) => { return String.fromCodePoint(parseInt(hex, 16)); }); if (temp !== decoded) { layers++; currentInput = temp; } } catch (_b) { } } // Base64 if (/^[A-Za-z0-9+/=]+$/.test(decoded)) { try { const temp = Buffer.from(decoded, "base64").toString(); if (temp !== decoded && temp.length > 0) { layers++; currentInput = temp; } } catch (_c) { } } // If no further decoding occurred, break if (decoded === currentInput) { break; } attempts++; } return layers; } /** * Calculates statistical anomaly score based on character distribution */ static calculateAnomalyScore(input) { const len = input.length; if (len === 0) return 0; // Character type counts let lowerCount = 0; let upperCount = 0; let digitCount = 0; let specialCount = 0; let controlCount = 0; let nonAsciiCount = 0; // Count character types for (let i = 0; i < len; i++) { const code = input.charCodeAt(i); if (code >= 97 && code <= 122) { // a-z lowerCount++; } else if (code >= 65 && code <= 90) { // A-Z upperCount++; } else if (code >= 48 && code <= 57) { // 0-9 digitCount++; } else if (code < 32 || code === 127) { // Control characters controlCount++; } else if (code > 127) { // Non-ASCII nonAsciiCount++; } else { specialCount++; // Other special characters } } // Calculate percentages const lowerPercent = lowerCount / len; const upperPercent = upperCount / len; const digitPercent = digitCount / len; const specialPercent = specialCount / len; const controlPercent = controlCount / len; const nonAsciiPercent = nonAsciiCount / len; // Calculate anomaly score - higher means more unusual distribution let anomalyScore = 0; // Unusual amount of special characters if (specialPercent > 0.3) { anomalyScore += specialPercent * 2; } // Unusual amount of non-ASCII characters if (nonAsciiPercent > 0.1) { anomalyScore += nonAsciiPercent * 3; } // Presence of control characters is very suspicious if (controlPercent > 0) { anomalyScore += controlPercent * 5; } // Unusual character type distribution const alphaPercent = lowerPercent + upperPercent; if (alphaPercent < 0.2 && len > 10) { anomalyScore += 1; } // Very high percentage of digits can be suspicious if (digitPercent > 0.7 && len > 10) { anomalyScore += 0.5; } return Math.min(anomalyScore, 5); // Cap at 5 } /** * Calculates total risk score based on detected patterns */ // In NehonixSecurity.service.txt static calculateTotalScore(patterns, sensitivityMultiplier) { if (patterns.length === 0) return 0; let score = 0; const severityScores = { high: 40, medium: 20, low: 10, }; const criticalPatternMultipliers = { [MaliciousPatternType.PATH_TRAVERSAL]: 1.5, [MaliciousPatternType.SSRF]: 1.5, [MaliciousPatternType.RFI]: 1.5, }; const confidenceMultipliers = { high: 1.5, medium: 1.0, low: 0.5, }; for (const pattern of patterns) { let patternScore = severityScores[pattern.severity] * confidenceMultipliers[pattern.confidence]; // Apply critical pattern multiplier if (criticalPatternMultipliers[pattern.type]) { patternScore *= criticalPatternMultipliers[pattern.type]; } if (pattern.contextScore) { patternScore *= pattern.contextScore; } score += patternScore; } score *= sensitivityMultiplier; const patternTypeCounts = {}; for (const pattern of patterns) { patternTypeCounts[pattern.type] = (patternTypeCounts[pattern.type] || 0) + 1; } for (const type in patternTypeCounts) { if (patternTypeCounts[type] > 1) { score *= 1 + (patternTypeCounts[type] - 1) * 0.2; } } return Math.min(Math.round(score), 100); } /** * Determines overall confidence level based on score and pattern count */ static determineConfidence(score, patternCount) { if (score >= 75 || (score >= 50 && patternCount >= 3)) { return "high"; } else if (score >= 40 || (score >= 25 && patternCount >= 2)) { return "medium"; } else { return "low"; } } /** * Generates appropriate recommendation based on detected patterns */ static generateRecommendation(patterns, score) { if (patterns.length === 0) { return "No malicious patterns detected. Input appears safe."; } const patternTypes = new Set(patterns.map((p) => p.type)); const recommendations = []; // Critical recommendations first if (score >= 75) { recommendations.push("HIGH RISK: Input contains malicious patterns. Block and investigate immediately."); } else if (score >= 50) { recommendations.push("MEDIUM RISK: Input contains suspicious patterns. Validate before processing."); } else { recommendations.push("LOW RISK: Input contains potentially suspicious patterns. Use caution."); } // Specific recommendations based on pattern types if (patternTypes.has(MaliciousPatternType.SQL_INJECTION)) { recommendations.push("Implement prepared statements or parameterized queries for database operations."); } if (patternTypes.has(MaliciousPatternType.XSS)) { recommendations.push("Implement output encoding and content security policy (CSP) headers."); } if (patternTypes.has(MaliciousPatternType.COMMAND_INJECTION)) { recommendations.push("Avoid direct command execution. Use restricted APIs and allowlists."); } if (patternTypes.has(MaliciousPatternType.PATH_TRAVERSAL)) { recommendations.push("Validate file paths and use path canonicalization before file operations."); } if (patternTypes.has(MaliciousPatternType.SSRF)) { recommendations.push("Implement allowlists for external resource access and validate URLs."); } if (patternTypes.has(MaliciousPatternType.RFI)) { recommendations.push("Validate file inclusions against a whitelist of allowed sources and disable remote file access."); } if (patternTypes.has(MaliciousPatternType.ENCODED_PAYLOAD) || patternTypes.has(MaliciousPatternType.MULTI_ENCODING)) { recommendations.push("Decode and normalize input before validation to prevent evasion techniques."); } return recommendations.join(" "); } /** * Analyzes input for a specific malicious pattern type * * @param input - The string to analyze * @param patternType - The specific pattern type to check for * @param options - Configuration options for detection * @returns Boolean indicating if pattern was detected */ static detectSpecificPatternType(input, patternType, options = {}) { // Use full detection but filter for specific pattern type const result = this.detectMaliciousPatterns(input, { ...options, minScore: 1, // Set minimum score low to catch any matches }); return result.detectedPatterns.some((p) => p.type === patternType); } /** * Sanitizes input by removing potentially malicious patterns * * @param input - The string to sanitize * @param options - Additional sanitization options * @returns Sanitized string */ /** * Sanitizes input by removing potentially malicious patterns * * @param input - The string to sanitize * @param options - Additional sanitization options * @returns Sanitized string */ static sanitizeInput(input, options = {}) { var _a, _b, _c, _d, _e; try { if (!input) return ""; let sanitized = input; // Default options const opts = { allowHtml: (_a = options.allowHtml) !== null && _a !== void 0 ? _a : false, allowMarkdown: (_b = options.allowMarkdown) !== null && _b !== void 0 ? _b : true, strictMode: (_c = options.strictMode) !== null && _c !== void 0 ? _c : false, preserveLength: (_d = options.preserveLength) !== null && _d !== void 0 ? _d : false, customPatterns: (_e = options.customPatterns) !== null && _e !== void 0 ? _e : [], }; // HTML sanitization if HTML not allowed if (!opts.allowHtml) { // More comprehensive script tag handling including variants sanitized = sanitized .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, opts.preserveLength ? this.createPlaceholder("script-block", 0) : "") .replace(/<\s*script[^>]*>(.*?)<\s*\/\s*script\s*>/gi, opts.preserveLength ? this.createPlaceholder("script-inline", 0) : ""); // More comprehensive HTML tag handling sanitized = sanitized.replace(/<(\/?\w+)((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)\/?>/gi, (match, tag, attrs) => { // Check against allowed tags if not in strict mode const safeTag = this.isSafeTag(tag) && !opts.strictMode; return safeTag ? match : opts.preserveLength ? this.createPlaceholder("tag", match.length) : `&lt;${tag}${attrs}&gt;`; }); // Remove event handlers with more extensive coverage const eventHandlerPattern = /\s+(on\w+)\s*=\s*["']?[^"']*["']?/gi; sanitized = sanitized.replace(eventHandlerPattern, (_, handler) => { return opts.preserveLength ? ` data-blocked-${handler}=""` : ""; }); // More comprehensive JavaScript URL blocking sanitized = sanitized.replace(/\b(href|src|data|action|formaction)\s*=\s*["']?\s*(javascript|data|vbscript):/gi, (_, attr, protocol) => `${attr}="${protocol}_blocked:"`); } // SQL injection protection with more patterns const sqlPatterns = [ { pattern: /(\b)(select|insert|update|delete|drop|a