UNPKG

zyph

Version:

A dependency scanner that detects suspicious code.

342 lines (337 loc) 14 kB
/** * heuristics.js * * Advanced Heuristics for Zyph Dependency Scanner. * * This file defines an array of heuristic objects that describe patterns and contexts * which might indicate malicious or risky behavior in JavaScript code. * * Each heuristic includes: * - id: A unique identifier for the heuristic. * - pattern: The pattern(s) or keyword(s) to detect. * - description: A detailed explanation of the risk. * - severity: A level indicator (e.g., "low", "medium", "high"). * - context: Additional context or conditions that might affect the interpretation. * - detection: A function to perform additional validation on a given AST node. * * Developers can update the `detection` functions with custom logic to make the scan * more context-sensitive. */ const heuristics = [ // Existing heuristics: { id: "EVAL_USAGE", pattern: "eval", description: "Direct use of eval() can execute arbitrary code. Unsanitized input can lead to remote code execution.", severity: "high", context: "Any code invoking eval() without strict input validation is flagged for further review.", detection: (node, filePath, code) => { // Basic check: if the callee is exactly 'eval' return true; } }, { id: "FUNCTION_CONSTRUCTOR", pattern: "Function", description: "Usage of the Function constructor allows dynamic code creation, which poses severe security risks if misused.", severity: "high", context: "Look for instantiation patterns that generate functions from string input.", detection: (node, filePath, code) => true }, { id: "DYNAMIC_TIMEOUT_INTERVAL", pattern: ["setTimeout", "setInterval"], description: "Using setTimeout or setInterval with string arguments may execute dynamically generated code, leading to deferred execution of malicious code.", severity: "medium", context: "If the first argument is a string literal rather than a function, it is considered unsafe.", detection: (node, filePath, code) => { return node.arguments && node.arguments[0] && node.arguments[0].type === "Literal" && typeof node.arguments[0].value === "string"; } }, { id: "DOCUMENT_WRITE", pattern: "document.write", description: "Using document.write() can inject scripts into a page, potentially leading to cross-site scripting (XSS) vulnerabilities.", severity: "high", context: "Mainly applies to client-side code and legacy systems; verify if output is properly escaped.", detection: (node, filePath, code) => true }, { id: "XMLHTTPREQUEST", pattern: "XMLHttpRequest", description: "XMLHttpRequest is used for making network calls. If used to send data to untrusted endpoints, it could be used to exfiltrate sensitive information.", severity: "medium", context: "Scrutinize the destination URL and request methods to ensure they align with the module's purpose.", detection: (node, filePath, code) => true }, { id: "FETCH_USAGE", pattern: "fetch", description: "The fetch() API is modern and widely used for HTTP requests; however, unexpected use may indicate data exfiltration or command-and-control callbacks.", severity: "medium", context: "Verify the request URL and parameters. Unexpected domains or HTTP methods may raise the alert level.", detection: (node, filePath, code) => true }, { id: "ENCODED_PAYLOAD", pattern: ["atob", "btoa"], description: "atob() and btoa() are used for Base64 encoding/decoding, which might indicate attempts to hide encoded payloads or obfuscate strings.", severity: "medium", context: "Common in obfuscation techniques. Check if encoded strings are used in dangerous contexts.", detection: (node, filePath, code) => true }, { id: "CHILD_PROCESS_REQUIRE", pattern: "require", description: "Requiring the 'child_process' module can allow execution of shell commands, which poses a critical security risk if exploited.", severity: "high", context: "Specifically check if the argument to require() is the string 'child_process'.", detection: (node, filePath, code) => { if (node.arguments && node.arguments.length > 0 && node.arguments[0].type === "Literal") { return node.arguments[0].value === "child_process"; } return false; } }, { id: "PROCESS_ENV_ACCESS", pattern: "process.env", description: "Accessing process.env may expose sensitive environmental variables. This is risky if the values are logged, transmitted, or improperly sanitized.", severity: "medium", context: "Commonly used in configuration; flag when used in contexts where data leakage could occur.", detection: (node, filePath, code) => true }, { id: "OBFUSCATION_HEX", pattern: ["\\x", "\\u"], description: "The presence of hexadecimal or Unicode escape sequences may indicate that the code is obfuscated to hide malicious behavior.", severity: "medium", context: "Detection here is based on a raw string search, suggesting that the code may be intentionally obscured.", detection: (node, filePath, code) => { return code.includes("\\x") || code.includes("\\u"); } }, { id: "DYNAMIC_PROPERTY_ACCESS", pattern: "[]", description: "Dynamic property access using bracket notation may be used to obfuscate code or bypass static analysis. Although not inherently malicious, it should be reviewed in sensitive contexts.", severity: "low", context: "Often seen in cases where property names are constructed at runtime.", detection: (node, filePath, code) => true }, { id: "INDIRECT_EVAL", pattern: "indirect eval", description: "Indirect invocation of eval (e.g., via window['eval']) is a common obfuscation tactic to hide dynamic code execution.", severity: "high", context: "This pattern is frequently used to bypass static analysis; review its usage carefully.", detection: (node, filePath, code) => true }, { id: "SUSPICIOUS_IMPORT_EXPORT", pattern: "import/export anomalies", description: "Unusual patterns in module import/export, such as dynamically constructing module paths, may be used to load malicious code.", severity: "medium", context: "Check for dynamically generated module names or unexpected re-exports that could hide vulnerabilities.", detection: (node, filePath, code) => true }, { id: "DYNAMIC_REQUIRE", pattern: "dynamic require", description: "Using variables or expressions within require() calls can obscure which module is being loaded, potentially masking malicious code.", severity: "medium", context: "This is especially concerning if the variable is influenced by external inputs or is not properly sanitized.", detection: (node, filePath, code) => { if (node.arguments && node.arguments.length > 0) { return node.arguments[0].type !== "Literal"; } return false; } }, { id: "INLINE_EVENT_HANDLER", pattern: "onerror|onclick|onload", description: "Inline event handlers (e.g., onerror, onclick, onload) in HTML or JavaScript may be exploited for injecting malicious scripts, particularly in unsanitized contexts.", severity: "low", context: "Often found in client-side code; verify that the inline handlers are properly secured.", detection: (node, filePath, code) => true }, { id: "DYNAMIC_EVAL_CONCAT", pattern: "dynamic eval concatenation", description: "Using eval() with dynamically concatenated strings can hide the true intent of the code by assembling it at runtime, making it harder to analyze statically.", severity: "high", context: "Particularly dangerous if parts of the string originate from external sources.", detection: (node, filePath, code) => { return node.arguments && node.arguments[0] && node.arguments[0].type === "BinaryExpression"; } }, { id: "SUSPICIOUS_REGEX", pattern: "regex anomalies", description: "Overly complex or obfuscated regular expressions might be used to bypass input validation or hide filtering logic, potentially facilitating injection attacks.", severity: "low", context: "Pay attention to dynamically constructed regex patterns that seem more complex than needed.", detection: (node, filePath, code) => true }, // New heuristics for additional malicious injection vectors: { id: "UNEXPECTED_NETWORK_ACTIVITY", pattern: ["fetch", "XMLHttpRequest", "child_process"], description: "Unexpected network or process activity detected in a module not expected to perform such operations.", severity: "high", context: "Modules not intended for network or process operations (e.g., math or utility modules) should not contain code that makes network requests or spawns processes.", detection: (node, filePath, code) => { const nonNetworkRegex = /math|numeric|calc/i; if (nonNetworkRegex.test(filePath)) { const suspiciousKeywords = ["fetch", "XMLHttpRequest", "child_process"]; return suspiciousKeywords.some(keyword => code.includes(keyword)); } return false; } }, { id: "UNEXPECTED_FS_ACCESS", pattern: ["fs.readFile", "fs.writeFile", "fs.unlink", "fs.appendFile"], description: "Suspicious file system access detected in a module that is not expected to perform file operations.", severity: "medium", context: "Modules not designed for file system interaction should not contain code that reads or writes files unexpectedly.", detection: (node, filePath, code) => { const nonFSRegex = /math|numeric|calc/i; if (nonFSRegex.test(filePath)) { const fsOperations = ["fs.readFile", "fs.writeFile", "fs.unlink", "fs.appendFile"]; return fsOperations.some(op => code.includes(op)); } return false; } }, { id: "MATH_MODULE_MINER", pattern: ["worker", "crypto", "nonce", "bitcoin", "mining"], description: "Suspicious activity: module appears to be a math/numeric library but contains potential cryptomining or cryptocurrency-related code.", severity: "high", context: "Modules intended for math or numeric computations should not include cryptocurrency mining operations.", detection: (node, filePath, code) => { if (/math|numeric|calc/i.test(filePath)) { const miningKeywords = ["worker", "crypto", "nonce", "bitcoin", "mining"]; return miningKeywords.some(keyword => code.includes(keyword)); } return false; } }, { id: "CONDITIONAL_MALICIOUS_BEHAVIOR", pattern: ["process.env", "if"], description: "Conditional execution based on environment variables leading to potentially malicious behavior detected.", severity: "medium", context: "The use of conditional checks on environment variables to enable risky operations can be a red flag if not clearly justified.", detection: (node, filePath, code) => { // Simple check: if code contains both 'process.env' and 'if', and also any risky functions if (code.includes("process.env") && code.includes("if")) { const riskyFunctions = ["eval", "require('child_process')", "new Function"]; return riskyFunctions.some(func => code.includes(func)); } return false; } }, { id: "SELF_MODIFYING_CODE", pattern: ["fs.writeFileSync", "__filename"], description: "Code attempting to modify its own source code detected, which is a common tactic for self-propagation or obfuscation.", severity: "high", context: "Legitimate modules should not modify their own source code at runtime.", detection: (node, filePath, code) => { return code.includes("fs.writeFileSync") && code.includes("__filename"); } }, { id: "PROTOTYPE_MODIFICATION", pattern: ["Object.prototype", "Array.prototype"], description: "Modification of built-in prototypes detected. This can be used to introduce unexpected behavior in all objects or arrays.", severity: "medium", context: "Modifying global prototypes is a risky practice that can lead to vulnerabilities if abused.", detection: (node, filePath, code) => { return (code.includes("Object.prototype") && /Object\.prototype\s*=/.test(code)) || (code.includes("Array.prototype") && /Array\.prototype\s*=/.test(code)); } }, { id: "DYNAMIC_IMPORT", pattern: "import()", description: "Dynamic import using non-literal expressions can hide malicious module loading.", severity: "medium", context: "Static imports should be used whenever possible.", detection: (node, filePath, code) => { if (node.type === "ImportExpression" && node.source) { return node.source.type !== "Literal"; } return false; } }, { id: "DANGEROUS_DOM_MANIPULATION", pattern: ["innerHTML", "document.createElement", "document.body"], description: "Direct manipulation of the DOM using innerHTML or similar methods can be used to inject malicious scripts.", severity: "high", context: "Modules should sanitize any user input before injecting into the DOM.", detection: (node, filePath, code) => { return code.includes("innerHTML") && code.includes("document."); } } ]; module.exports = heuristics;