Initial commit: StreamFlow IPTV platform

This commit is contained in:
aiulian25 2025-12-17 00:42:43 +00:00
commit 73a8ae9ffd
1240 changed files with 278451 additions and 0 deletions

View file

@ -0,0 +1,479 @@
const express = require('express');
const router = express.Router();
const { authenticate } = require('../middleware/auth');
const { requirePermission } = require('../middleware/rbac');
const { readLimiter } = require('../middleware/rateLimiter');
const { db } = require('../database/db');
const logger = require('../utils/logger');
const fs = require('fs').promises;
const path = require('path');
const { exec } = require('child_process');
const { promisify } = require('util');
const execPromise = promisify(exec);
/**
* Security Monitoring & Dependency Management
* Provides comprehensive security status and vulnerability tracking
*/
// Get comprehensive security status
router.get('/status', authenticate, requirePermission('security.view_audit'), readLimiter, async (req, res) => {
try {
const securityStatus = {
timestamp: new Date().toISOString(),
dependencies: await checkDependencies(),
vulnerabilities: await checkVulnerabilities(),
securityHeaders: await checkSecurityHeaders(),
auditSummary: await getAuditSummary(),
systemHealth: await getSystemHealth()
};
res.json(securityStatus);
} catch (error) {
logger.error('Error fetching security status:', error);
res.status(500).json({ error: 'Failed to fetch security status' });
}
});
// Check dependencies for updates
async function checkDependencies() {
try {
const backendPackage = JSON.parse(
await fs.readFile(path.join(__dirname, '../package.json'), 'utf8')
);
const frontendPackage = JSON.parse(
await fs.readFile(path.join(__dirname, '../../frontend/package.json'), 'utf8')
);
return {
backend: {
dependencies: Object.keys(backendPackage.dependencies || {}).length,
devDependencies: Object.keys(backendPackage.devDependencies || {}).length,
lastChecked: new Date().toISOString()
},
frontend: {
dependencies: Object.keys(frontendPackage.dependencies || {}).length,
devDependencies: Object.keys(frontendPackage.devDependencies || {}).length,
lastChecked: new Date().toISOString()
}
};
} catch (error) {
logger.error('Error checking dependencies:', error);
return { error: 'Unable to check dependencies' };
}
}
// Check for known vulnerabilities
async function checkVulnerabilities() {
try {
// Run npm audit in both backend and frontend
const backendAudit = await runNpmAudit('backend');
const frontendAudit = await runNpmAudit('frontend');
return {
backend: backendAudit,
frontend: frontendAudit,
lastScanned: new Date().toISOString()
};
} catch (error) {
logger.error('Error checking vulnerabilities:', error);
return { error: 'Unable to scan for vulnerabilities' };
}
}
async function runNpmAudit(project) {
try {
const projectPath = project === 'backend'
? path.join(__dirname, '..')
: path.join(__dirname, '../../frontend');
const { stdout } = await execPromise(`cd ${projectPath} && npm audit --json`, {
timeout: 30000
});
const auditData = JSON.parse(stdout);
return {
total: auditData.metadata?.vulnerabilities?.total || 0,
critical: auditData.metadata?.vulnerabilities?.critical || 0,
high: auditData.metadata?.vulnerabilities?.high || 0,
moderate: auditData.metadata?.vulnerabilities?.moderate || 0,
low: auditData.metadata?.vulnerabilities?.low || 0,
info: auditData.metadata?.vulnerabilities?.info || 0
};
} catch (error) {
// npm audit returns non-zero exit code when vulnerabilities are found
if (error.stdout) {
try {
const auditData = JSON.parse(error.stdout);
return {
total: auditData.metadata?.vulnerabilities?.total || 0,
critical: auditData.metadata?.vulnerabilities?.critical || 0,
high: auditData.metadata?.vulnerabilities?.high || 0,
moderate: auditData.metadata?.vulnerabilities?.moderate || 0,
low: auditData.metadata?.vulnerabilities?.low || 0,
info: auditData.metadata?.vulnerabilities?.info || 0
};
} catch {
return { error: 'Unable to parse audit results' };
}
}
return { error: error.message };
}
}
// Check security headers configuration
async function checkSecurityHeaders() {
return {
helmet: {
enabled: true,
features: [
'Content-Security-Policy',
'X-Content-Type-Options',
'X-Frame-Options',
'X-XSS-Protection',
'Strict-Transport-Security',
'Referrer-Policy'
]
},
csp: {
enabled: true,
mode: process.env.NODE_ENV === 'production' ? 'enforcing' : 'report-only'
},
cors: {
enabled: true
}
};
}
// Get security audit summary
async function getAuditSummary() {
return new Promise((resolve, reject) => {
db.all(
`SELECT
action,
result,
COUNT(*) as count,
MAX(timestamp) as last_occurrence
FROM security_audit_log
WHERE timestamp > datetime('now', '-7 days')
GROUP BY action, result
ORDER BY count DESC
LIMIT 20`,
[],
(err, rows) => {
if (err) {
logger.error('Error fetching audit summary:', err);
resolve({ error: 'Unable to fetch audit summary' });
} else {
resolve(rows || []);
}
}
);
});
}
// Get system health metrics
async function getSystemHealth() {
return new Promise((resolve, reject) => {
Promise.all([
// Active sessions count
new Promise((res) => {
db.get('SELECT COUNT(*) as count FROM sessions WHERE expires_at > ?',
[new Date().toISOString()],
(err, row) => res(row?.count || 0)
);
}),
// Failed login attempts in last hour
new Promise((res) => {
db.get(
`SELECT COUNT(*) as count FROM security_audit_log
WHERE action = 'login' AND result = 'failed'
AND timestamp > datetime('now', '-1 hour')`,
[],
(err, row) => res(row?.count || 0)
);
}),
// Locked accounts
new Promise((res) => {
db.get(
'SELECT COUNT(*) as count FROM users WHERE locked_until > ?',
[new Date().toISOString()],
(err, row) => res(row?.count || 0)
);
}),
// Total users
new Promise((res) => {
db.get('SELECT COUNT(*) as count FROM users', [], (err, row) => res(row?.count || 0));
})
]).then(([activeSessions, failedLogins, lockedAccounts, totalUsers]) => {
resolve({
activeSessions,
failedLogins,
lockedAccounts,
totalUsers,
timestamp: new Date().toISOString()
});
});
});
}
// Get detailed vulnerability report
router.get('/vulnerabilities/detailed', authenticate, requirePermission('security.view_audit'), readLimiter, async (req, res) => {
try {
const backendPath = path.join(__dirname, '..');
const frontendPath = path.join(__dirname, '../../frontend');
const backendAudit = await execPromise(`cd ${backendPath} && npm audit --json`, {
timeout: 30000
}).catch(e => ({ stdout: e.stdout }));
const frontendAudit = await execPromise(`cd ${frontendPath} && npm audit --json`, {
timeout: 30000
}).catch(e => ({ stdout: e.stdout }));
const backendData = JSON.parse(backendAudit.stdout || '{}');
const frontendData = JSON.parse(frontendAudit.stdout || '{}');
res.json({
backend: {
vulnerabilities: backendData.vulnerabilities || {},
metadata: backendData.metadata || {}
},
frontend: {
vulnerabilities: frontendData.vulnerabilities || {},
metadata: frontendData.metadata || {}
},
timestamp: new Date().toISOString()
});
} catch (error) {
logger.error('Error fetching detailed vulnerabilities:', error);
res.status(500).json({ error: 'Failed to fetch vulnerability details' });
}
});
// Get security audit log with filtering
router.get('/audit-log', authenticate, requirePermission('security.view_audit'), readLimiter, async (req, res) => {
try {
const { action, result, userId, startDate, endDate, limit = 100, offset = 0 } = req.query;
let query = 'SELECT * FROM security_audit_log WHERE 1=1';
const params = [];
if (action) {
query += ' AND action = ?';
params.push(action);
}
if (result) {
query += ' AND result = ?';
params.push(result);
}
if (userId) {
query += ' AND user_id = ?';
params.push(userId);
}
if (startDate) {
query += ' AND timestamp >= ?';
params.push(startDate);
}
if (endDate) {
query += ' AND timestamp <= ?';
params.push(endDate);
}
query += ' ORDER BY timestamp DESC LIMIT ? OFFSET ?';
params.push(parseInt(limit), parseInt(offset));
db.all(query, params, (err, rows) => {
if (err) {
logger.error('Error fetching audit log:', err);
return res.status(500).json({ error: 'Failed to fetch audit log' });
}
// Get total count for pagination
let countQuery = 'SELECT COUNT(*) as total FROM security_audit_log WHERE 1=1';
const countParams = params.slice(0, -2); // Remove limit and offset
db.get(countQuery, countParams, (err, countRow) => {
if (err) {
logger.error('Error counting audit log:', err);
return res.json({ logs: rows || [], total: 0 });
}
res.json({
logs: rows || [],
total: countRow?.total || 0,
limit: parseInt(limit),
offset: parseInt(offset)
});
});
});
} catch (error) {
logger.error('Error fetching audit log:', error);
res.status(500).json({ error: 'Failed to fetch audit log' });
}
});
// Export audit log
router.get('/audit-log/export', authenticate, requirePermission('security.view_audit'), readLimiter, async (req, res) => {
try {
const { format = 'json', startDate, endDate } = req.query;
let query = 'SELECT * FROM security_audit_log WHERE 1=1';
const params = [];
if (startDate) {
query += ' AND timestamp >= ?';
params.push(startDate);
}
if (endDate) {
query += ' AND timestamp <= ?';
params.push(endDate);
}
query += ' ORDER BY timestamp DESC';
db.all(query, params, (err, rows) => {
if (err) {
logger.error('Error exporting audit log:', err);
return res.status(500).json({ error: 'Failed to export audit log' });
}
if (format === 'csv') {
const csv = convertToCSV(rows);
res.setHeader('Content-Type', 'text/csv');
res.setHeader('Content-Disposition', `attachment; filename=security-audit-${Date.now()}.csv`);
res.send(csv);
} else {
res.setHeader('Content-Type', 'application/json');
res.setHeader('Content-Disposition', `attachment; filename=security-audit-${Date.now()}.json`);
res.json(rows);
}
});
} catch (error) {
logger.error('Error exporting audit log:', error);
res.status(500).json({ error: 'Failed to export audit log' });
}
});
function convertToCSV(data) {
if (!data || data.length === 0) return '';
const headers = Object.keys(data[0]);
const csvRows = [headers.join(',')];
for (const row of data) {
const values = headers.map(header => {
const value = row[header];
return typeof value === 'string' && value.includes(',')
? `"${value}"`
: value;
});
csvRows.push(values.join(','));
}
return csvRows.join('\n');
}
// Get security recommendations
router.get('/recommendations', authenticate, requirePermission('security.view_audit'), readLimiter, async (req, res) => {
try {
const recommendations = [];
// Check for locked accounts
const lockedAccounts = await new Promise((resolve) => {
db.get(
'SELECT COUNT(*) as count FROM users WHERE locked_until > ?',
[new Date().toISOString()],
(err, row) => resolve(row?.count || 0)
);
});
if (lockedAccounts > 0) {
recommendations.push({
severity: 'warning',
category: 'account_security',
title: 'Locked Accounts',
description: `${lockedAccounts} account(s) are currently locked due to failed login attempts`,
action: 'Review locked accounts and consider unlocking legitimate users'
});
}
// Check for users with old passwords
const oldPasswords = await new Promise((resolve) => {
db.all(
`SELECT username, password_changed_at FROM users
WHERE password_changed_at < datetime('now', '-90 days')`,
[],
(err, rows) => resolve(rows || [])
);
});
if (oldPasswords.length > 0) {
recommendations.push({
severity: 'info',
category: 'password_policy',
title: 'Old Passwords',
description: `${oldPasswords.length} user(s) haven't changed their password in over 90 days`,
action: 'Encourage users to update their passwords regularly'
});
}
// Check for recent failed logins
const recentFailures = await new Promise((resolve) => {
db.get(
`SELECT COUNT(*) as count FROM security_audit_log
WHERE action = 'login' AND result = 'failed'
AND timestamp > datetime('now', '-1 hour')`,
[],
(err, row) => resolve(row?.count || 0)
);
});
if (recentFailures > 10) {
recommendations.push({
severity: 'high',
category: 'threat_detection',
title: 'High Failed Login Rate',
description: `${recentFailures} failed login attempts in the last hour`,
action: 'Investigate potential brute-force attack'
});
}
// Check for users without 2FA
const no2FA = await new Promise((resolve) => {
db.get(
'SELECT COUNT(*) as count FROM users WHERE two_factor_secret IS NULL',
[],
(err, row) => resolve(row?.count || 0)
);
});
if (no2FA > 0) {
recommendations.push({
severity: 'warning',
category: 'authentication',
title: 'Two-Factor Authentication',
description: `${no2FA} user(s) don't have 2FA enabled`,
action: 'Encourage users to enable two-factor authentication'
});
}
res.json({
recommendations,
timestamp: new Date().toISOString()
});
} catch (error) {
logger.error('Error generating recommendations:', error);
res.status(500).json({ error: 'Failed to generate recommendations' });
}
});
module.exports = router;