Update modules/SecurityLogger.js
Browse files- modules/SecurityLogger.js +54 -203
modules/SecurityLogger.js
CHANGED
|
@@ -1,66 +1,41 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 3 |
-
* SECURITY LOGGER - LOG DETALHADO DE OPERAΓΓES DE CYBERSECURITY
|
| 4 |
-
* βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 5 |
-
* β
Registra todas as operaΓ§Γ΅es com timestamps
|
| 6 |
-
* β
Armazena em database segura
|
| 7 |
-
* β
Fornece relatΓ³rios de auditoria
|
| 8 |
-
* β
Detecta atividade suspeita
|
| 9 |
-
* β
IntegraΓ§Γ£o com alertas
|
| 10 |
-
* βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 11 |
-
*/
|
| 12 |
-
|
| 13 |
const fs = require('fs');
|
| 14 |
const path = require('path');
|
| 15 |
|
| 16 |
class SecurityLogger {
|
| 17 |
-
constructor(config) {
|
| 18 |
this.config = config;
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
this.alertsPath = path.join(this.logsPath, 'alerts.json');
|
| 28 |
this.opsPath = path.join(this.logsPath, 'operations.json');
|
| 29 |
-
|
| 30 |
-
// Cria diretΓ³rios com tratamento de erro
|
| 31 |
try {
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
console.warn(`β οΈ SecurityLogger: NΓ£o foi possΓvel criar diretΓ³rio em ${this.logsPath}:`, error.message);
|
| 38 |
-
|
| 39 |
-
// Fallback para /tmp direto
|
| 40 |
-
const tmpPath = '/tmp/security_logs';
|
| 41 |
-
try {
|
| 42 |
-
fs.mkdirSync(tmpPath, { recursive: true });
|
| 43 |
-
this.logsPath = tmpPath;
|
| 44 |
-
this.alertsPath = path.join(this.logsPath, 'alerts.json');
|
| 45 |
-
this.opsPath = path.join(this.logsPath, 'operations.json');
|
| 46 |
-
console.log(`β
SecurityLogger: Usando fallback: ${this.logsPath}`);
|
| 47 |
-
} catch (fallbackError) {
|
| 48 |
-
console.error('β SecurityLogger: Erro crΓtico ao criar diretΓ³rio:', fallbackError.message);
|
| 49 |
-
this.logsPath = null;
|
| 50 |
-
}
|
| 51 |
}
|
| 52 |
|
| 53 |
-
// Carrega logs
|
| 54 |
this.operations = this.logsPath ? this._loadJSON(this.opsPath, []) : [];
|
| 55 |
this.alerts = this.logsPath ? this._loadJSON(this.alertsPath, []) : [];
|
| 56 |
|
| 57 |
console.log('β
SecurityLogger inicializado');
|
| 58 |
}
|
| 59 |
|
| 60 |
-
/**
|
| 61 |
-
* Registra operaΓ§Γ£o de cybersecurity
|
| 62 |
-
*/
|
| 63 |
logOperation(operacao) {
|
|
|
|
|
|
|
| 64 |
try {
|
| 65 |
const entry = {
|
| 66 |
id: `${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
|
|
@@ -75,199 +50,75 @@ class SecurityLogger {
|
|
| 75 |
duracao: operacao.duracao || 0
|
| 76 |
};
|
| 77 |
|
| 78 |
-
// Adiciona ao log
|
| 79 |
this.operations.push(entry);
|
| 80 |
this._saveJSON(this.opsPath, this.operations);
|
| 81 |
|
| 82 |
-
// Verifica se Γ© atividade suspeita
|
| 83 |
if (this._isSuspicious(entry)) {
|
| 84 |
this._createAlert(entry);
|
| 85 |
}
|
| 86 |
|
| 87 |
-
console.log(`π [SECURITY
|
| 88 |
return entry;
|
| 89 |
} catch (e) {
|
| 90 |
console.error('Erro ao logar operaΓ§Γ£o:', e);
|
| 91 |
}
|
| 92 |
}
|
| 93 |
|
| 94 |
-
/**
|
| 95 |
-
* Cria alerta de atividade suspeita
|
| 96 |
-
*/
|
| 97 |
_createAlert(operacao) {
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
return alert;
|
| 115 |
-
} catch (e) {
|
| 116 |
-
console.error('Erro ao criar alerta:', e);
|
| 117 |
-
}
|
| 118 |
}
|
| 119 |
|
| 120 |
-
/**
|
| 121 |
-
* ObtΓ©m relatΓ³rio de operaΓ§Γ΅es
|
| 122 |
-
*/
|
| 123 |
-
getOperationReport(filtros = {}) {
|
| 124 |
-
try {
|
| 125 |
-
let ops = [...this.operations];
|
| 126 |
-
|
| 127 |
-
// Filtra por usuΓ‘rio
|
| 128 |
-
if (filtros.usuario) {
|
| 129 |
-
ops = ops.filter(o => o.usuario === filtros.usuario);
|
| 130 |
-
}
|
| 131 |
-
|
| 132 |
-
// Filtra por tipo
|
| 133 |
-
if (filtros.tipo) {
|
| 134 |
-
ops = ops.filter(o => o.tipoOperacao === filtros.tipo);
|
| 135 |
-
}
|
| 136 |
-
|
| 137 |
-
// Filtra por perΓodo
|
| 138 |
-
if (filtros.dataInicio && filtros.dataFim) {
|
| 139 |
-
const inicio = new Date(filtros.dataInicio);
|
| 140 |
-
const fim = new Date(filtros.dataFim);
|
| 141 |
-
ops = ops.filter(o => {
|
| 142 |
-
const data = new Date(o.timestamp);
|
| 143 |
-
return data >= inicio && data <= fim;
|
| 144 |
-
});
|
| 145 |
-
}
|
| 146 |
-
|
| 147 |
-
// Agrupa por tipo
|
| 148 |
-
const porTipo = {};
|
| 149 |
-
const porRisco = {};
|
| 150 |
-
|
| 151 |
-
ops.forEach(op => {
|
| 152 |
-
porTipo[op.tipoOperacao] = (porTipo[op.tipoOperacao] || 0) + 1;
|
| 153 |
-
porRisco[op.risco] = (porRisco[op.risco] || 0) + 1;
|
| 154 |
-
});
|
| 155 |
-
|
| 156 |
-
return {
|
| 157 |
-
totalOperacoes: ops.length,
|
| 158 |
-
operacoes: ops.slice(-50), // Γltimas 50
|
| 159 |
-
resumoPorTipo: porTipo,
|
| 160 |
-
resumoPorRisco: porRisco,
|
| 161 |
-
operaΓ§Γ΅esSuspeitas: ops.filter(o => o.risco === 'ALTO' || o.risco === 'CRΓTICO').length
|
| 162 |
-
};
|
| 163 |
-
} catch (e) {
|
| 164 |
-
console.error('Erro ao gerar relatΓ³rio:', e);
|
| 165 |
-
return { erro: e.message };
|
| 166 |
-
}
|
| 167 |
-
}
|
| 168 |
-
|
| 169 |
-
/**
|
| 170 |
-
* ObtΓ©m relatΓ³rio de alertas
|
| 171 |
-
*/
|
| 172 |
-
getAlertReport() {
|
| 173 |
-
try {
|
| 174 |
-
const alertasNovos = this.alerts.filter(a => a.status === 'NOVO');
|
| 175 |
-
const alertasResolvidos = this.alerts.filter(a => a.status === 'RESOLVIDO');
|
| 176 |
-
|
| 177 |
-
return {
|
| 178 |
-
totalAlertas: this.alerts.length,
|
| 179 |
-
alertasNovos: alertasNovos.length,
|
| 180 |
-
alertasResolvidos: alertasResolvidos.length,
|
| 181 |
-
ultimos: this.alerts.slice(-20)
|
| 182 |
-
};
|
| 183 |
-
} catch (e) {
|
| 184 |
-
return { erro: e.message };
|
| 185 |
-
}
|
| 186 |
-
}
|
| 187 |
-
|
| 188 |
-
/**
|
| 189 |
-
* Marca alerta como resolvido
|
| 190 |
-
*/
|
| 191 |
-
resolveAlert(alertId) {
|
| 192 |
-
try {
|
| 193 |
-
const alert = this.alerts.find(a => a.id === alertId);
|
| 194 |
-
if (alert) {
|
| 195 |
-
alert.status = 'RESOLVIDO';
|
| 196 |
-
alert.resolvidoEm = new Date().toISOString();
|
| 197 |
-
this._saveJSON(this.alertsPath, this.alerts);
|
| 198 |
-
return true;
|
| 199 |
-
}
|
| 200 |
-
return false;
|
| 201 |
-
} catch (e) {
|
| 202 |
-
return false;
|
| 203 |
-
}
|
| 204 |
-
}
|
| 205 |
-
|
| 206 |
-
/**
|
| 207 |
-
* DetecΓ§Γ£o de atividade suspeita
|
| 208 |
-
*/
|
| 209 |
_isSuspicious(operacao) {
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
return timeDiff < 60000; // ΓΊltimos 60s
|
| 214 |
-
});
|
| 215 |
|
| 216 |
if (recentOps.length > 5) return true;
|
| 217 |
-
|
| 218 |
-
// Scan agressivo
|
| 219 |
if (operacao.tipoOperacao === 'NMAP_SCAN' && operacao.risco === 'ALTO') return true;
|
| 220 |
-
|
| 221 |
-
// MΓΊltiplas tentativas de SQL injection
|
| 222 |
if (operacao.tipoOperacao === 'SQLMAP_TEST' && operacao.resultado === 'VULNERΓVEL') return true;
|
| 223 |
|
| 224 |
-
// Breach search repetido
|
| 225 |
-
if (operacao.tipoOperacao === 'BREACH_SEARCH') {
|
| 226 |
-
const recent = recentOps.filter(o => o.tipoOperacao === 'BREACH_SEARCH');
|
| 227 |
-
if (recent.length > 3) return true;
|
| 228 |
-
}
|
| 229 |
-
|
| 230 |
return false;
|
| 231 |
}
|
| 232 |
|
| 233 |
_getSuspiciousReason(operacao) {
|
| 234 |
-
const
|
| 235 |
|
| 236 |
-
if (operacao.tipoOperacao === 'NMAP_SCAN')
|
| 237 |
-
|
| 238 |
-
|
| 239 |
|
| 240 |
-
|
| 241 |
-
razoes.push('Teste de SQL Injection');
|
| 242 |
-
}
|
| 243 |
-
|
| 244 |
-
if (operacao.risco === 'CRΓTICO') {
|
| 245 |
-
razoes.push('Risco crΓtico detectado');
|
| 246 |
-
}
|
| 247 |
-
|
| 248 |
-
return razoes.length > 0 ? razoes.join(', ') : 'Atividade incomum';
|
| 249 |
}
|
| 250 |
|
| 251 |
-
|
| 252 |
-
* FUNΓΓES AUXILIARES
|
| 253 |
-
*/
|
| 254 |
-
|
| 255 |
-
_loadJSON(filepath, defaultValue = {}) {
|
| 256 |
try {
|
| 257 |
-
if (fs.existsSync(
|
| 258 |
-
return JSON.parse(fs.readFileSync(
|
| 259 |
}
|
| 260 |
-
} catch
|
| 261 |
-
|
| 262 |
-
}
|
| 263 |
-
return defaultValue;
|
| 264 |
}
|
| 265 |
|
| 266 |
-
_saveJSON(
|
| 267 |
try {
|
| 268 |
-
fs.writeFileSync(
|
| 269 |
} catch (e) {
|
| 270 |
-
console.error(`Erro ao salvar ${
|
| 271 |
}
|
| 272 |
}
|
| 273 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
const fs = require('fs');
|
| 2 |
const path = require('path');
|
| 3 |
|
| 4 |
class SecurityLogger {
|
| 5 |
+
constructor(config = {}) {
|
| 6 |
this.config = config;
|
| 7 |
+
|
| 8 |
+
/**
|
| 9 |
+
* Detecta ambiente
|
| 10 |
+
* HF Spaces β filesystem root read-only
|
| 11 |
+
* Railway / Docker β usar volume persistente
|
| 12 |
+
*/
|
| 13 |
+
const isHF = !!process.env.SPACE_ID;
|
| 14 |
+
const basePath = isHF
|
| 15 |
+
? '/tmp/akira_data'
|
| 16 |
+
: process.env.DATA_PATH || path.resolve('data');
|
| 17 |
+
|
| 18 |
+
this.logsPath = path.join(basePath, 'security_logs');
|
| 19 |
this.alertsPath = path.join(this.logsPath, 'alerts.json');
|
| 20 |
this.opsPath = path.join(this.logsPath, 'operations.json');
|
| 21 |
+
|
|
|
|
| 22 |
try {
|
| 23 |
+
fs.mkdirSync(this.logsPath, { recursive: true });
|
| 24 |
+
console.log(`β
SecurityLogger: DiretΓ³rio ativo: ${this.logsPath}`);
|
| 25 |
+
} catch (err) {
|
| 26 |
+
console.error('β SecurityLogger: Falha ao criar diretΓ³rio:', err.message);
|
| 27 |
+
this.logsPath = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
}
|
| 29 |
|
|
|
|
| 30 |
this.operations = this.logsPath ? this._loadJSON(this.opsPath, []) : [];
|
| 31 |
this.alerts = this.logsPath ? this._loadJSON(this.alertsPath, []) : [];
|
| 32 |
|
| 33 |
console.log('β
SecurityLogger inicializado');
|
| 34 |
}
|
| 35 |
|
|
|
|
|
|
|
|
|
|
| 36 |
logOperation(operacao) {
|
| 37 |
+
if (!this.logsPath) return;
|
| 38 |
+
|
| 39 |
try {
|
| 40 |
const entry = {
|
| 41 |
id: `${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
|
|
|
|
| 50 |
duracao: operacao.duracao || 0
|
| 51 |
};
|
| 52 |
|
|
|
|
| 53 |
this.operations.push(entry);
|
| 54 |
this._saveJSON(this.opsPath, this.operations);
|
| 55 |
|
|
|
|
| 56 |
if (this._isSuspicious(entry)) {
|
| 57 |
this._createAlert(entry);
|
| 58 |
}
|
| 59 |
|
| 60 |
+
console.log(`π [SECURITY] ${entry.tipoOperacao} β ${entry.alvo}`);
|
| 61 |
return entry;
|
| 62 |
} catch (e) {
|
| 63 |
console.error('Erro ao logar operaΓ§Γ£o:', e);
|
| 64 |
}
|
| 65 |
}
|
| 66 |
|
|
|
|
|
|
|
|
|
|
| 67 |
_createAlert(operacao) {
|
| 68 |
+
const alert = {
|
| 69 |
+
id: `alert_${Date.now()}`,
|
| 70 |
+
timestamp: new Date().toISOString(),
|
| 71 |
+
severidade: 'ALTO',
|
| 72 |
+
operacaoId: operacao.id,
|
| 73 |
+
usuario: operacao.usuario,
|
| 74 |
+
descricao: `OperaΓ§Γ£o suspeita: ${operacao.tipoOperacao}`,
|
| 75 |
+
motivo: this._getSuspiciousReason(operacao),
|
| 76 |
+
status: 'NOVO'
|
| 77 |
+
};
|
| 78 |
+
|
| 79 |
+
this.alerts.push(alert);
|
| 80 |
+
this._saveJSON(this.alertsPath, this.alerts);
|
| 81 |
+
|
| 82 |
+
console.log(`π¨ [SECURITY ALERT] ${alert.descricao}`);
|
| 83 |
+
return alert;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
}
|
| 85 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
_isSuspicious(operacao) {
|
| 87 |
+
const recentOps = this.operations.filter(o =>
|
| 88 |
+
new Date(operacao.timestamp) - new Date(o.timestamp) < 60000
|
| 89 |
+
);
|
|
|
|
|
|
|
| 90 |
|
| 91 |
if (recentOps.length > 5) return true;
|
|
|
|
|
|
|
| 92 |
if (operacao.tipoOperacao === 'NMAP_SCAN' && operacao.risco === 'ALTO') return true;
|
|
|
|
|
|
|
| 93 |
if (operacao.tipoOperacao === 'SQLMAP_TEST' && operacao.resultado === 'VULNERΓVEL') return true;
|
| 94 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
return false;
|
| 96 |
}
|
| 97 |
|
| 98 |
_getSuspiciousReason(operacao) {
|
| 99 |
+
const reasons = [];
|
| 100 |
|
| 101 |
+
if (operacao.tipoOperacao === 'NMAP_SCAN') reasons.push('Port scan');
|
| 102 |
+
if (operacao.tipoOperacao === 'SQLMAP_TEST') reasons.push('SQL Injection');
|
| 103 |
+
if (operacao.risco === 'CRΓTICO') reasons.push('Risco crΓtico');
|
| 104 |
|
| 105 |
+
return reasons.join(', ') || 'Atividade incomum';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
}
|
| 107 |
|
| 108 |
+
_loadJSON(file, fallback) {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
try {
|
| 110 |
+
if (fs.existsSync(file)) {
|
| 111 |
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
| 112 |
}
|
| 113 |
+
} catch {}
|
| 114 |
+
return fallback;
|
|
|
|
|
|
|
| 115 |
}
|
| 116 |
|
| 117 |
+
_saveJSON(file, data) {
|
| 118 |
try {
|
| 119 |
+
fs.writeFileSync(file, JSON.stringify(data, null, 2));
|
| 120 |
} catch (e) {
|
| 121 |
+
console.error(`Erro ao salvar ${file}:`, e.message);
|
| 122 |
}
|
| 123 |
}
|
| 124 |
}
|