streamflow/backend/utils/riskSignatureManager.js

566 lines
17 KiB
JavaScript
Raw Normal View History

/**
* Risk Signature Manager
* Predefined risk signatures for threat detection
* CWE-778 Compliance: Logs all signature matches and management operations
*/
const logger = require('./logger');
const logAggregator = require('./logAggregator');
const { db } = require('../database/db');
class RiskSignatureManager {
constructor() {
this.signatures = new Map();
this.initialize();
}
/**
* Initialize risk signature manager
*/
async initialize() {
await this.createSignaturesTable();
await this.loadSignatures();
logger.info('[RiskSignatureManager] Initialized with predefined risk signatures');
// Log initialization (CWE-778)
logAggregator.aggregate('risk_signature_manager', 'info', 'security', 'Risk signature manager initialized', {
totalSignatures: this.signatures.size
});
}
/**
* Create risk signatures table
*/
async createSignaturesTable() {
return new Promise((resolve, reject) => {
db.run(`
CREATE TABLE IF NOT EXISTS risk_signatures (
id INTEGER PRIMARY KEY AUTOINCREMENT,
signature_id TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
description TEXT,
signature_type TEXT NOT NULL,
pattern TEXT NOT NULL,
match_type TEXT NOT NULL,
threat_level TEXT NOT NULL,
confidence REAL DEFAULT 0.8,
enabled INTEGER DEFAULT 1,
auto_block INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`, async (err) => {
if (err) reject(err);
else {
db.run(`CREATE INDEX IF NOT EXISTS idx_signatures_type ON risk_signatures(signature_type, enabled)`);
db.run(`CREATE INDEX IF NOT EXISTS idx_signatures_threat ON risk_signatures(threat_level, enabled)`);
await this.createDefaultSignatures();
resolve();
}
});
});
}
/**
* Create default risk signatures
*/
async createDefaultSignatures() {
const defaultSignatures = [
// IP-based signatures
{
signature_id: 'SIG-IP-TOR',
name: 'TOR Exit Node',
description: 'Known TOR exit node IP address',
signature_type: 'ip_address',
pattern: '(^10\\.\\d+\\.\\d+\\.\\d+|^172\\.(1[6-9]|2[0-9]|3[01])\\.\\d+\\.\\d+|^192\\.168\\.\\d+\\.\\d+)',
match_type: 'regex',
threat_level: 'high',
confidence: 0.9,
auto_block: 0
},
{
signature_id: 'SIG-IP-SUSPICIOUS',
name: 'Suspicious IP Range',
description: 'IP from suspicious geographic region',
signature_type: 'ip_address',
pattern: '',
match_type: 'custom',
threat_level: 'medium',
confidence: 0.7,
auto_block: 0
},
// User-agent signatures
{
signature_id: 'SIG-UA-BOT-MALICIOUS',
name: 'Malicious Bot User-Agent',
description: 'Known malicious bot signatures',
signature_type: 'user_agent',
pattern: '(scrapy|python-requests|curl|wget|nikto|sqlmap|havij|acunetix|nessus|openvas)',
match_type: 'regex_case_insensitive',
threat_level: 'high',
confidence: 0.95,
auto_block: 1
},
{
signature_id: 'SIG-UA-VULNERABILITY-SCANNER',
name: 'Vulnerability Scanner',
description: 'Automated vulnerability scanning tools',
signature_type: 'user_agent',
pattern: '(nmap|masscan|zap|burp|metasploit|w3af|arachni)',
match_type: 'regex_case_insensitive',
threat_level: 'critical',
confidence: 0.99,
auto_block: 1
},
// Attack pattern signatures
{
signature_id: 'SIG-ATTACK-SQL-INJECTION',
name: 'SQL Injection Pattern',
description: 'Common SQL injection attack patterns',
signature_type: 'attack_pattern',
pattern: '(union.*select|select.*from|insert.*into|delete.*from|drop.*table|exec.*xp_|script.*alert)',
match_type: 'regex_case_insensitive',
threat_level: 'critical',
confidence: 0.85,
auto_block: 1
},
{
signature_id: 'SIG-ATTACK-XSS',
name: 'Cross-Site Scripting Pattern',
description: 'XSS attack patterns',
signature_type: 'attack_pattern',
pattern: '(<script|javascript:|onerror=|onload=|<iframe|eval\\(|alert\\()',
match_type: 'regex_case_insensitive',
threat_level: 'high',
confidence: 0.8,
auto_block: 1
},
{
signature_id: 'SIG-ATTACK-PATH-TRAVERSAL',
name: 'Path Traversal Pattern',
description: 'Directory traversal attack patterns',
signature_type: 'attack_pattern',
pattern: '(\\.\\./|\\.\\.\\\\/|%2e%2e/|%252e%252e/)',
match_type: 'regex_case_insensitive',
threat_level: 'high',
confidence: 0.9,
auto_block: 1
},
{
signature_id: 'SIG-ATTACK-COMMAND-INJECTION',
name: 'Command Injection Pattern',
description: 'OS command injection patterns',
signature_type: 'attack_pattern',
pattern: '(;\\s*(rm|cat|ls|wget|curl|bash|sh|cmd|powershell)|\\|\\s*(nc|netcat))',
match_type: 'regex_case_insensitive',
threat_level: 'critical',
confidence: 0.95,
auto_block: 1
},
// Behavioral signatures
{
signature_id: 'SIG-BEHAVIOR-BRUTE-FORCE',
name: 'Brute Force Behavior',
description: 'Rapid repeated authentication attempts',
signature_type: 'behavior',
pattern: 'failed_login_rate',
match_type: 'custom',
threat_level: 'critical',
confidence: 0.9,
auto_block: 1
},
{
signature_id: 'SIG-BEHAVIOR-CREDENTIAL-STUFFING',
name: 'Credential Stuffing Behavior',
description: 'Multiple username attempts from single source',
signature_type: 'behavior',
pattern: 'unique_username_rate',
match_type: 'custom',
threat_level: 'high',
confidence: 0.85,
auto_block: 1
},
{
signature_id: 'SIG-BEHAVIOR-PRIVILEGE-ESC',
name: 'Privilege Escalation Behavior',
description: 'Repeated unauthorized access attempts',
signature_type: 'behavior',
pattern: 'authorization_failure_rate',
match_type: 'custom',
threat_level: 'critical',
confidence: 0.95,
auto_block: 1
},
{
signature_id: 'SIG-BEHAVIOR-DATA-EXFIL',
name: 'Data Exfiltration Behavior',
description: 'Unusual data download patterns',
signature_type: 'behavior',
pattern: 'download_volume_rate',
match_type: 'custom',
threat_level: 'high',
confidence: 0.8,
auto_block: 0
}
];
for (const signature of defaultSignatures) {
await new Promise((resolve, reject) => {
db.run(
`INSERT OR IGNORE INTO risk_signatures
(signature_id, name, description, signature_type, pattern, match_type, threat_level, confidence, auto_block)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[
signature.signature_id,
signature.name,
signature.description,
signature.signature_type,
signature.pattern,
signature.match_type,
signature.threat_level,
signature.confidence,
signature.auto_block
],
(err) => {
if (err) reject(err);
else resolve();
}
);
});
}
logger.info(`[RiskSignatureManager] Created ${defaultSignatures.length} default signatures`);
}
/**
* Load signatures from database into memory
*/
async loadSignatures() {
return new Promise((resolve, reject) => {
db.all(
`SELECT * FROM risk_signatures WHERE enabled = 1`,
[],
(err, rows) => {
if (err) {
reject(err);
} else {
this.signatures.clear();
rows.forEach(row => {
this.signatures.set(row.signature_id, row);
});
logger.info(`[RiskSignatureManager] Loaded ${rows.length} active signatures`);
resolve();
}
}
);
});
}
/**
* Match input against risk signatures
* CWE-778: Logs all signature matches
*/
async matchSignatures(input, signatureType, context = {}) {
const matchingSignatures = Array.from(this.signatures.values()).filter(
s => s.signature_type === signatureType
);
if (matchingSignatures.length === 0) {
return { matched: false, signatures: [] };
}
const matches = [];
for (const signature of matchingSignatures) {
const matched = this.testPattern(input, signature.pattern, signature.match_type);
if (matched) {
matches.push({
...signature,
matchedInput: input,
context
});
// Log signature match (CWE-778)
logAggregator.aggregate('risk_signature_manager', 'warn', 'security', 'Risk signature matched', {
signatureId: signature.signature_id,
signatureName: signature.name,
signatureType,
threatLevel: signature.threat_level,
confidence: signature.confidence,
autoBlock: signature.auto_block === 1,
matchedInput: input.substring(0, 100), // Truncate for logging
context
});
logger.warn(`[RiskSignatureManager] Signature matched: ${signature.name} (${signature.threat_level})`);
}
}
return {
matched: matches.length > 0,
signatures: matches,
highestThreat: matches.length > 0 ? this.getHighestThreatLevel(matches) : null,
shouldAutoBlock: matches.some(m => m.auto_block === 1)
};
}
/**
* Test pattern against input
*/
testPattern(input, pattern, matchType) {
try {
switch (matchType) {
case 'regex':
return new RegExp(pattern).test(input);
case 'regex_case_insensitive':
return new RegExp(pattern, 'i').test(input);
case 'exact':
return input === pattern;
case 'contains':
return input.includes(pattern);
case 'custom':
// Custom patterns handled by specific detection methods
return false;
default:
return false;
}
} catch (error) {
logger.error(`[RiskSignatureManager] Pattern test error: ${error.message}`);
return false;
}
}
/**
* Get highest threat level from matches
*/
getHighestThreatLevel(matches) {
const threatLevels = { critical: 4, high: 3, medium: 2, low: 1 };
let highest = 'low';
let highestScore = 0;
for (const match of matches) {
const score = threatLevels[match.threat_level] || 0;
if (score > highestScore) {
highestScore = score;
highest = match.threat_level;
}
}
return highest;
}
/**
* Get all signatures
*/
async getSignatures(filters = {}) {
const { signatureType, threatLevel, enabled, limit = 100 } = filters;
let whereClause = [];
let params = [];
if (signatureType) {
whereClause.push('signature_type = ?');
params.push(signatureType);
}
if (threatLevel) {
whereClause.push('threat_level = ?');
params.push(threatLevel);
}
if (enabled !== undefined) {
whereClause.push('enabled = ?');
params.push(enabled ? 1 : 0);
}
const where = whereClause.length > 0 ? `WHERE ${whereClause.join(' AND ')}` : '';
params.push(limit);
return new Promise((resolve, reject) => {
db.all(
`SELECT * FROM risk_signatures ${where}
ORDER BY threat_level DESC, confidence DESC
LIMIT ?`,
params,
(err, rows) => {
if (err) reject(err);
else resolve(rows);
}
);
});
}
/**
* Get signature by ID
*/
async getSignatureById(signatureId) {
return new Promise((resolve, reject) => {
db.get(
`SELECT * FROM risk_signatures WHERE signature_id = ?`,
[signatureId],
(err, row) => {
if (err) reject(err);
else resolve(row);
}
);
});
}
/**
* Create new signature
* CWE-778: Logs signature creation
*/
async createSignature(data, userId) {
const signatureId = `SIG-${Date.now()}-${Math.random().toString(36).substr(2, 9).toUpperCase()}`;
return new Promise((resolve, reject) => {
db.run(
`INSERT INTO risk_signatures
(signature_id, name, description, signature_type, pattern, match_type, threat_level, confidence, enabled, auto_block)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[
signatureId,
data.name,
data.description || '',
data.signature_type,
data.pattern,
data.match_type,
data.threat_level,
data.confidence || 0.8,
data.enabled !== undefined ? (data.enabled ? 1 : 0) : 1,
data.auto_block !== undefined ? (data.auto_block ? 1 : 0) : 0
],
async (err) => {
if (err) {
reject(err);
} else {
await this.loadSignatures();
// Log signature creation (CWE-778)
logAggregator.aggregate('risk_signature_manager', 'info', 'security', 'Risk signature created', {
signatureId,
userId,
name: data.name,
signatureType: data.signature_type,
threatLevel: data.threat_level,
autoBlock: data.auto_block === 1
});
logger.info(`[RiskSignatureManager] Signature created: ${signatureId} by user ${userId}`);
resolve({ signatureId });
}
}
);
});
}
/**
* Update signature
* CWE-778: Logs signature modifications
*/
async updateSignature(signatureId, updates, userId) {
const allowedFields = ['name', 'description', 'pattern', 'match_type', 'threat_level', 'confidence', 'enabled', 'auto_block'];
const setClause = [];
const params = [];
for (const [key, value] of Object.entries(updates)) {
if (allowedFields.includes(key)) {
setClause.push(`${key} = ?`);
params.push((key === 'enabled' || key === 'auto_block') ? (value ? 1 : 0) : value);
}
}
if (setClause.length === 0) {
throw new Error('No valid fields to update');
}
setClause.push('updated_at = CURRENT_TIMESTAMP');
params.push(signatureId);
return new Promise((resolve, reject) => {
db.run(
`UPDATE risk_signatures
SET ${setClause.join(', ')}
WHERE signature_id = ?`,
params,
async (err) => {
if (err) {
reject(err);
} else {
await this.loadSignatures();
// Log signature update (CWE-778)
logAggregator.aggregate('risk_signature_manager', 'info', 'security', 'Risk signature updated', {
signatureId,
userId,
updates
});
logger.info(`[RiskSignatureManager] Signature updated: ${signatureId} by user ${userId}`);
resolve({ success: true });
}
}
);
});
}
/**
* Delete signature
* CWE-778: Logs signature deletion
*/
async deleteSignature(signatureId, userId) {
return new Promise((resolve, reject) => {
db.run(
`DELETE FROM risk_signatures WHERE signature_id = ?`,
[signatureId],
async (err) => {
if (err) {
reject(err);
} else {
await this.loadSignatures();
// Log signature deletion (CWE-778)
logAggregator.aggregate('risk_signature_manager', 'warn', 'security', 'Risk signature deleted', {
signatureId,
userId
});
logger.info(`[RiskSignatureManager] Signature deleted: ${signatureId} by user ${userId}`);
resolve({ success: true });
}
}
);
});
}
/**
* Get signature statistics
*/
async getStatistics() {
return new Promise((resolve, reject) => {
db.get(
`SELECT
COUNT(*) as total,
SUM(CASE WHEN enabled = 1 THEN 1 ELSE 0 END) as enabled,
SUM(CASE WHEN enabled = 0 THEN 1 ELSE 0 END) as disabled,
SUM(CASE WHEN auto_block = 1 THEN 1 ELSE 0 END) as auto_block_enabled,
COUNT(DISTINCT signature_type) as unique_types,
COUNT(DISTINCT threat_level) as unique_threat_levels
FROM risk_signatures`,
[],
(err, row) => {
if (err) reject(err);
else resolve(row);
}
);
});
}
}
// Create singleton instance
const riskSignatureManager = new RiskSignatureManager();
module.exports = riskSignatureManager;