pury
Version:
🛡️ AI-powered security scanner with advanced threat detection, dual reporting system (detailed & summary), and comprehensive code analysis
304 lines • 12.6 kB
JavaScript
import { FindingType, Severity } from '../types/index.js';
import { logger } from '../utils/logger.js';
import { extractLineContext } from '../utils/file-utils.js';
export class MalwareAnalyzer {
patterns;
sensitivity;
constructor(sensitivity = 'medium') {
this.sensitivity = sensitivity;
this.patterns = this.loadPatterns();
}
async analyze(files, onProgress) {
const startTime = Date.now();
const findings = [];
for (let i = 0; i < files.length; i++) {
const file = files[i];
onProgress?.(i + 1, files.length, file.path);
try {
const fileFindings = await this.analyzeFile(file);
findings.push(...fileFindings);
}
catch (error) {
logger.warn(`Failed to analyze file ${file.path}: ${error.message}`);
}
}
const processingTime = Date.now() - startTime;
logger.debug(`Malware analysis completed in ${processingTime}ms`);
return {
findings,
processingTime,
filesAnalyzed: files.length
};
}
async analyzeFile(file) {
const findings = [];
const lines = file.content.split('\n');
// Check each pattern against the file content
for (const pattern of this.patterns) {
if (!this.shouldApplyPattern(pattern)) {
continue;
}
const matches = this.findPatternMatches(file.content, lines, pattern);
findings.push(...matches.map(match => this.createFinding(match, file, pattern)));
}
// Additional heuristic analysis
const heuristicFindings = this.performHeuristicAnalysis(file, lines);
findings.push(...heuristicFindings);
return findings;
}
shouldApplyPattern(pattern) {
switch (this.sensitivity) {
case 'low':
return pattern.severity === Severity.CRITICAL || pattern.severity === Severity.HIGH;
case 'medium':
return pattern.severity !== Severity.LOW;
case 'high':
return true;
default:
return true;
}
}
findPatternMatches(content, lines, pattern) {
const matches = [];
if (pattern.pattern instanceof RegExp) {
// Line-by-line regex matching for better line number tracking
lines.forEach((line, index) => {
const match = line.match(pattern.pattern);
if (match) {
matches.push({
line: index + 1,
match: match[0],
context: extractLineContext(content, index + 1, 2)
});
}
});
}
else {
// String pattern matching
const searchString = pattern.pattern;
lines.forEach((line, index) => {
if (line.toLowerCase().includes(searchString.toLowerCase())) {
matches.push({
line: index + 1,
match: line.trim(),
context: extractLineContext(content, index + 1, 2)
});
}
});
}
return matches;
}
createFinding(match, file, pattern) {
return {
id: `malware-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
type: FindingType.MALWARE,
severity: pattern.severity,
title: pattern.name,
description: pattern.description,
file: file.path,
line: match.line,
evidence: match.match,
suggestion: this.getSuggestion(pattern),
references: [`Pattern ID: ${pattern.id}`, `Category: ${pattern.category}`]
};
}
performHeuristicAnalysis(file, lines) {
const findings = [];
// Entropy analysis for obfuscated code
const highEntropyLines = this.findHighEntropyLines(lines);
highEntropyLines.forEach(({ line, entropy, content }) => {
if (entropy > 4.5) {
// Threshold for suspicious entropy
findings.push({
id: `entropy-${Date.now()}-${line}`,
type: FindingType.MALWARE,
severity: entropy > 5.5 ? Severity.HIGH : Severity.MEDIUM,
title: 'High Entropy Content Detected',
description: `Line contains high entropy content (${entropy.toFixed(2)}) which may indicate obfuscated or encoded data`,
file: file.path,
line,
evidence: content.substring(0, 100) + (content.length > 100 ? '...' : ''),
suggestion: 'Review this content for potential obfuscation or encoding. Ensure it serves a legitimate purpose.'
});
}
});
// Check for suspicious concatenation patterns
const suspiciousConcatenation = this.findSuspiciousConcatenation(lines);
suspiciousConcatenation.forEach(({ line, content }) => {
findings.push({
id: `concat-${Date.now()}-${line}`,
type: FindingType.MALWARE,
severity: Severity.MEDIUM,
title: 'Suspicious String Concatenation',
description: 'Detected string concatenation that may be used to evade static analysis',
file: file.path,
line,
evidence: content,
suggestion: 'Review the purpose of this string concatenation. Consider using clearer string construction methods.'
});
});
return findings;
}
findHighEntropyLines(lines) {
return lines
.map((content, index) => ({
line: index + 1,
entropy: this.calculateEntropy(content),
content
}))
.filter(item => item.entropy > 4.0 && item.content.length > 20);
}
calculateEntropy(str) {
const charCounts = new Map();
for (const char of str) {
charCounts.set(char, (charCounts.get(char) || 0) + 1);
}
let entropy = 0;
const { length } = str;
for (const count of charCounts.values()) {
const probability = count / length;
entropy -= probability * Math.log2(probability);
}
return entropy;
}
findSuspiciousConcatenation(lines) {
const suspiciousPatterns = [
/['"]\s*\+\s*['"][^'"]+['"]\s*\+\s*['"][^'"]+/g, // Multiple string concatenations
/String\.fromCharCode\s*\(/g, // Character code conversion
/atob\s*\(/g, // Base64 decoding
/eval\s*\(\s*[^)]*\+/g // Eval with concatenation
];
const results = [];
lines.forEach((line, index) => {
for (const pattern of suspiciousPatterns) {
if (pattern.test(line)) {
results.push({
line: index + 1,
content: line.trim()
});
break; // Only add one finding per line
}
}
});
return results;
}
getSuggestion(pattern) {
const suggestions = {
obfuscation: 'Remove code obfuscation or provide clear documentation if obfuscation is necessary for legitimate purposes.',
eval: 'Avoid using eval() or similar dynamic code execution functions. Use safer alternatives like JSON.parse() for data parsing.',
base64: 'Review base64 encoded content. Ensure it contains legitimate data and is not being used to hide malicious code.',
'suspicious-import': 'Review imported modules for legitimacy. Ensure they come from trusted sources and serve necessary purposes.',
network: 'Review network operations for necessity and security. Ensure proper input validation and error handling.',
filesystem: 'Review file system operations for security implications. Ensure proper path validation and access controls.'
};
return (suggestions[pattern.category] ||
'Review this code pattern for potential security implications and ensure it serves a legitimate purpose.');
}
loadPatterns() {
return [
// Obfuscation patterns
{
id: 'obf-001',
name: 'JavaScript Obfuscation',
pattern: /\\x[0-9a-fA-F]{2}/g,
severity: Severity.MEDIUM,
description: 'Detected hexadecimal character encoding which may indicate obfuscated code',
category: 'obfuscation'
},
{
id: 'obf-002',
name: 'Unicode Escape Sequences',
pattern: /\\u[0-9a-fA-F]{4}/g,
severity: Severity.MEDIUM,
description: 'Detected Unicode escape sequences which may be used for obfuscation',
category: 'obfuscation'
},
{
id: 'obf-003',
name: 'Eval with Dynamic Code',
pattern: /eval\s*\(\s*[^)]*[\+\[\]]/g,
severity: Severity.HIGH,
description: 'Detected eval() with dynamic code construction which is highly suspicious',
category: 'eval'
},
// Base64 patterns
{
id: 'b64-001',
name: 'Base64 Encoded Content',
pattern: /[A-Za-z0-9+/]{40,}={0,2}/g,
severity: Severity.MEDIUM,
description: 'Detected long base64 encoded string which may contain executable code',
category: 'base64'
},
{
id: 'b64-002',
name: 'Base64 Decode Function',
pattern: /atob\s*\(/g,
severity: Severity.MEDIUM,
description: 'Detected base64 decoding function which may be used to decode malicious content',
category: 'base64'
},
// Suspicious function calls
{
id: 'func-001',
name: 'Dynamic Function Creation',
pattern: /new\s+Function\s*\(/g,
severity: Severity.HIGH,
description: 'Detected dynamic function creation which can be used for code injection',
category: 'eval'
},
{
id: 'func-002',
name: 'String fromCharCode',
pattern: /String\.fromCharCode\s*\(/g,
severity: Severity.MEDIUM,
description: 'Detected String.fromCharCode which may be used to construct strings dynamically',
category: 'obfuscation'
},
// Network operations
{
id: 'net-001',
name: 'XMLHttpRequest Creation',
pattern: /new\s+XMLHttpRequest\s*\(/g,
severity: Severity.LOW,
description: 'Detected XMLHttpRequest which may be used for data exfiltration',
category: 'network'
},
{
id: 'net-002',
name: 'Fetch API Usage',
pattern: /fetch\s*\(\s*['"`][^'"`]*['"`]/g,
severity: Severity.LOW,
description: 'Detected fetch API usage with external URLs',
category: 'network'
},
// File system operations (Node.js)
{
id: 'fs-001',
name: 'File System Write',
pattern: /fs\.writeFile|fs\.writeFileSync/g,
severity: Severity.MEDIUM,
description: 'Detected file system write operations',
category: 'filesystem'
},
{
id: 'fs-002',
name: 'Child Process Execution',
pattern: /child_process\.exec|spawn|fork/g,
severity: Severity.HIGH,
description: 'Detected child process execution which can be used for malicious commands',
category: 'eval'
}
];
}
addCustomPattern(pattern) {
this.patterns.push(pattern);
}
getPatternCount() {
return this.patterns.length;
}
setSensitivity(level) {
this.sensitivity = level;
}
}
//# sourceMappingURL=malware-analyzer.js.map