Initial commit: StreamFlow IPTV platform
This commit is contained in:
commit
73a8ae9ffd
1240 changed files with 278451 additions and 0 deletions
481
backend/utils/thresholdManager.js
Normal file
481
backend/utils/thresholdManager.js
Normal file
|
|
@ -0,0 +1,481 @@
|
|||
/**
|
||||
* Threshold Manager
|
||||
* Configurable notification thresholds for security threat detection
|
||||
* CWE-778 Compliance: Logs all threshold configurations and evaluations
|
||||
*/
|
||||
|
||||
const logger = require('./logger');
|
||||
const logAggregator = require('./logAggregator');
|
||||
const { db } = require('../database/db');
|
||||
|
||||
class ThresholdManager {
|
||||
constructor() {
|
||||
this.thresholds = new Map();
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize threshold manager
|
||||
*/
|
||||
async initialize() {
|
||||
await this.createThresholdsTable();
|
||||
await this.loadThresholds();
|
||||
|
||||
logger.info('[ThresholdManager] Initialized with configurable thresholds');
|
||||
|
||||
// Log initialization (CWE-778)
|
||||
logAggregator.aggregate('threshold_manager', 'info', 'security', 'Threshold manager initialized', {
|
||||
totalThresholds: this.thresholds.size
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create thresholds table
|
||||
*/
|
||||
async createThresholdsTable() {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.run(`
|
||||
CREATE TABLE IF NOT EXISTS security_thresholds (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
threshold_id TEXT UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
pattern_type TEXT NOT NULL,
|
||||
metric_name TEXT NOT NULL,
|
||||
operator TEXT NOT NULL,
|
||||
threshold_value INTEGER NOT NULL,
|
||||
time_window_minutes INTEGER DEFAULT 30,
|
||||
severity TEXT NOT NULL,
|
||||
enabled INTEGER DEFAULT 1,
|
||||
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_thresholds_pattern ON security_thresholds(pattern_type, enabled)`);
|
||||
db.run(`CREATE INDEX IF NOT EXISTS idx_thresholds_enabled ON security_thresholds(enabled)`);
|
||||
await this.createDefaultThresholds();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create default thresholds for common security patterns
|
||||
*/
|
||||
async createDefaultThresholds() {
|
||||
const defaultThresholds = [
|
||||
{
|
||||
threshold_id: 'THRESHOLD-BRUTE-FORCE',
|
||||
name: 'Brute Force Attack Threshold',
|
||||
description: 'Alert when failed login attempts exceed threshold',
|
||||
pattern_type: 'brute_force_attack',
|
||||
metric_name: 'failed_login_count',
|
||||
operator: '>=',
|
||||
threshold_value: 5,
|
||||
time_window_minutes: 10,
|
||||
severity: 'critical'
|
||||
},
|
||||
{
|
||||
threshold_id: 'THRESHOLD-CREDENTIAL-STUFFING',
|
||||
name: 'Credential Stuffing Threshold',
|
||||
description: 'Alert on multiple username attempts from same IP',
|
||||
pattern_type: 'credential_stuffing',
|
||||
metric_name: 'unique_username_count',
|
||||
operator: '>=',
|
||||
threshold_value: 5,
|
||||
time_window_minutes: 5,
|
||||
severity: 'critical'
|
||||
},
|
||||
{
|
||||
threshold_id: 'THRESHOLD-PRIVILEGE-ESC',
|
||||
name: 'Privilege Escalation Threshold',
|
||||
description: 'Alert on repeated unauthorized access attempts',
|
||||
pattern_type: 'privilege_escalation',
|
||||
metric_name: 'escalation_attempt_count',
|
||||
operator: '>=',
|
||||
threshold_value: 3,
|
||||
time_window_minutes: 30,
|
||||
severity: 'critical'
|
||||
},
|
||||
{
|
||||
threshold_id: 'THRESHOLD-SUSPICIOUS-IP',
|
||||
name: 'Suspicious IP Activity Threshold',
|
||||
description: 'Alert on excessive requests from single IP',
|
||||
pattern_type: 'suspicious_ip',
|
||||
metric_name: 'request_count',
|
||||
operator: '>=',
|
||||
threshold_value: 100,
|
||||
time_window_minutes: 15,
|
||||
severity: 'high'
|
||||
},
|
||||
{
|
||||
threshold_id: 'THRESHOLD-DATA-EXFIL',
|
||||
name: 'Data Exfiltration Threshold',
|
||||
description: 'Alert on excessive data downloads',
|
||||
pattern_type: 'data_exfiltration',
|
||||
metric_name: 'download_count',
|
||||
operator: '>=',
|
||||
threshold_value: 10,
|
||||
time_window_minutes: 60,
|
||||
severity: 'high'
|
||||
},
|
||||
{
|
||||
threshold_id: 'THRESHOLD-SESSION-ANOMALY',
|
||||
name: 'Session Anomaly Threshold',
|
||||
description: 'Alert on unusual session patterns',
|
||||
pattern_type: 'session_anomaly',
|
||||
metric_name: 'anomaly_score',
|
||||
operator: '>=',
|
||||
threshold_value: 70,
|
||||
time_window_minutes: 30,
|
||||
severity: 'medium'
|
||||
},
|
||||
{
|
||||
threshold_id: 'THRESHOLD-IMPOSSIBLE-TRAVEL',
|
||||
name: 'Impossible Travel Threshold',
|
||||
description: 'Alert on geographically impossible travel speed',
|
||||
pattern_type: 'impossible_travel',
|
||||
metric_name: 'travel_speed_kmh',
|
||||
operator: '>=',
|
||||
threshold_value: 800,
|
||||
time_window_minutes: 60,
|
||||
severity: 'high'
|
||||
},
|
||||
{
|
||||
threshold_id: 'THRESHOLD-THREAT-SCORE',
|
||||
name: 'Critical Threat Score Threshold',
|
||||
description: 'Alert when overall threat score is critical',
|
||||
pattern_type: 'threat_score',
|
||||
metric_name: 'threat_score',
|
||||
operator: '>=',
|
||||
threshold_value: 80,
|
||||
time_window_minutes: 60,
|
||||
severity: 'critical'
|
||||
}
|
||||
];
|
||||
|
||||
for (const threshold of defaultThresholds) {
|
||||
await new Promise((resolve, reject) => {
|
||||
db.run(
|
||||
`INSERT OR IGNORE INTO security_thresholds
|
||||
(threshold_id, name, description, pattern_type, metric_name, operator, threshold_value, time_window_minutes, severity)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
threshold.threshold_id,
|
||||
threshold.name,
|
||||
threshold.description,
|
||||
threshold.pattern_type,
|
||||
threshold.metric_name,
|
||||
threshold.operator,
|
||||
threshold.threshold_value,
|
||||
threshold.time_window_minutes,
|
||||
threshold.severity
|
||||
],
|
||||
(err) => {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
logger.info(`[ThresholdManager] Created ${defaultThresholds.length} default thresholds`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load thresholds from database into memory
|
||||
*/
|
||||
async loadThresholds() {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.all(
|
||||
`SELECT * FROM security_thresholds WHERE enabled = 1`,
|
||||
[],
|
||||
(err, rows) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
this.thresholds.clear();
|
||||
rows.forEach(row => {
|
||||
this.thresholds.set(row.threshold_id, row);
|
||||
});
|
||||
logger.info(`[ThresholdManager] Loaded ${rows.length} active thresholds`);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate if a metric value exceeds threshold
|
||||
* CWE-778: Logs all threshold evaluations
|
||||
*/
|
||||
async evaluateThreshold(patternType, metricName, value, context = {}) {
|
||||
const matchingThresholds = Array.from(this.thresholds.values()).filter(
|
||||
t => t.pattern_type === patternType && t.metric_name === metricName
|
||||
);
|
||||
|
||||
if (matchingThresholds.length === 0) {
|
||||
return { exceeded: false, thresholds: [] };
|
||||
}
|
||||
|
||||
const exceededThresholds = [];
|
||||
|
||||
for (const threshold of matchingThresholds) {
|
||||
const exceeded = this.compareValue(value, threshold.operator, threshold.threshold_value);
|
||||
|
||||
// Log threshold evaluation (CWE-778)
|
||||
logAggregator.aggregate('threshold_manager', 'info', 'security', 'Threshold evaluated', {
|
||||
thresholdId: threshold.threshold_id,
|
||||
patternType,
|
||||
metricName,
|
||||
value,
|
||||
operator: threshold.operator,
|
||||
thresholdValue: threshold.threshold_value,
|
||||
exceeded,
|
||||
severity: threshold.severity,
|
||||
context
|
||||
});
|
||||
|
||||
if (exceeded) {
|
||||
exceededThresholds.push({
|
||||
...threshold,
|
||||
actualValue: value,
|
||||
context
|
||||
});
|
||||
|
||||
logger.warn(`[ThresholdManager] Threshold exceeded: ${threshold.name} (${value} ${threshold.operator} ${threshold.threshold_value})`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
exceeded: exceededThresholds.length > 0,
|
||||
thresholds: exceededThresholds
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare value against threshold using operator
|
||||
*/
|
||||
compareValue(value, operator, threshold) {
|
||||
switch (operator) {
|
||||
case '>=': return value >= threshold;
|
||||
case '>': return value > threshold;
|
||||
case '<=': return value <= threshold;
|
||||
case '<': return value < threshold;
|
||||
case '==': return value == threshold;
|
||||
case '!=': return value != threshold;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all thresholds
|
||||
*/
|
||||
async getThresholds(filters = {}) {
|
||||
const { patternType, enabled, limit = 100 } = filters;
|
||||
|
||||
let whereClause = [];
|
||||
let params = [];
|
||||
|
||||
if (patternType) {
|
||||
whereClause.push('pattern_type = ?');
|
||||
params.push(patternType);
|
||||
}
|
||||
|
||||
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 security_thresholds ${where}
|
||||
ORDER BY pattern_type, threshold_value DESC
|
||||
LIMIT ?`,
|
||||
params,
|
||||
(err, rows) => {
|
||||
if (err) reject(err);
|
||||
else resolve(rows);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get threshold by ID
|
||||
*/
|
||||
async getThresholdById(thresholdId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.get(
|
||||
`SELECT * FROM security_thresholds WHERE threshold_id = ?`,
|
||||
[thresholdId],
|
||||
(err, row) => {
|
||||
if (err) reject(err);
|
||||
else resolve(row);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new threshold
|
||||
* CWE-778: Logs threshold creation
|
||||
*/
|
||||
async createThreshold(data, userId) {
|
||||
const thresholdId = `THRESHOLD-${Date.now()}-${Math.random().toString(36).substr(2, 9).toUpperCase()}`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
db.run(
|
||||
`INSERT INTO security_thresholds
|
||||
(threshold_id, name, description, pattern_type, metric_name, operator, threshold_value, time_window_minutes, severity, enabled)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
thresholdId,
|
||||
data.name,
|
||||
data.description || '',
|
||||
data.pattern_type,
|
||||
data.metric_name,
|
||||
data.operator,
|
||||
data.threshold_value,
|
||||
data.time_window_minutes || 30,
|
||||
data.severity,
|
||||
data.enabled !== undefined ? (data.enabled ? 1 : 0) : 1
|
||||
],
|
||||
async (err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
await this.loadThresholds();
|
||||
|
||||
// Log threshold creation (CWE-778)
|
||||
logAggregator.aggregate('threshold_manager', 'info', 'security', 'Threshold created', {
|
||||
thresholdId,
|
||||
userId,
|
||||
name: data.name,
|
||||
patternType: data.pattern_type,
|
||||
metricName: data.metric_name,
|
||||
thresholdValue: data.threshold_value,
|
||||
severity: data.severity
|
||||
});
|
||||
|
||||
logger.info(`[ThresholdManager] Threshold created: ${thresholdId} by user ${userId}`);
|
||||
resolve({ thresholdId });
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update threshold
|
||||
* CWE-778: Logs threshold modifications
|
||||
*/
|
||||
async updateThreshold(thresholdId, updates, userId) {
|
||||
const allowedFields = ['name', 'description', 'operator', 'threshold_value', 'time_window_minutes', 'severity', 'enabled'];
|
||||
const setClause = [];
|
||||
const params = [];
|
||||
|
||||
for (const [key, value] of Object.entries(updates)) {
|
||||
if (allowedFields.includes(key)) {
|
||||
setClause.push(`${key} = ?`);
|
||||
params.push(key === 'enabled' ? (value ? 1 : 0) : value);
|
||||
}
|
||||
}
|
||||
|
||||
if (setClause.length === 0) {
|
||||
throw new Error('No valid fields to update');
|
||||
}
|
||||
|
||||
setClause.push('updated_at = CURRENT_TIMESTAMP');
|
||||
params.push(thresholdId);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
db.run(
|
||||
`UPDATE security_thresholds
|
||||
SET ${setClause.join(', ')}
|
||||
WHERE threshold_id = ?`,
|
||||
params,
|
||||
async (err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
await this.loadThresholds();
|
||||
|
||||
// Log threshold update (CWE-778)
|
||||
logAggregator.aggregate('threshold_manager', 'info', 'security', 'Threshold updated', {
|
||||
thresholdId,
|
||||
userId,
|
||||
updates
|
||||
});
|
||||
|
||||
logger.info(`[ThresholdManager] Threshold updated: ${thresholdId} by user ${userId}`);
|
||||
resolve({ success: true });
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete threshold
|
||||
* CWE-778: Logs threshold deletion
|
||||
*/
|
||||
async deleteThreshold(thresholdId, userId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.run(
|
||||
`DELETE FROM security_thresholds WHERE threshold_id = ?`,
|
||||
[thresholdId],
|
||||
async (err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
await this.loadThresholds();
|
||||
|
||||
// Log threshold deletion (CWE-778)
|
||||
logAggregator.aggregate('threshold_manager', 'warn', 'security', 'Threshold deleted', {
|
||||
thresholdId,
|
||||
userId
|
||||
});
|
||||
|
||||
logger.info(`[ThresholdManager] Threshold deleted: ${thresholdId} by user ${userId}`);
|
||||
resolve({ success: true });
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get threshold 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,
|
||||
COUNT(DISTINCT pattern_type) as unique_patterns,
|
||||
COUNT(DISTINCT severity) as unique_severities
|
||||
FROM security_thresholds`,
|
||||
[],
|
||||
(err, row) => {
|
||||
if (err) reject(err);
|
||||
else resolve(row);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Create singleton instance
|
||||
const thresholdManager = new ThresholdManager();
|
||||
|
||||
module.exports = thresholdManager;
|
||||
Loading…
Add table
Add a link
Reference in a new issue