feat: add templateCodigo field to alert configurations and enhance alert handling with new email/chat templates for cybersecurity incidents
This commit is contained in:
@@ -122,6 +122,7 @@
|
|||||||
severidadeMin: alertSeveridadeMin,
|
severidadeMin: alertSeveridadeMin,
|
||||||
tiposAtaque: alertTiposAtaque as AtaqueCiberneticoTipo[],
|
tiposAtaque: alertTiposAtaque as AtaqueCiberneticoTipo[],
|
||||||
reenvioMin: alertReenvioMin,
|
reenvioMin: alertReenvioMin,
|
||||||
|
templateCodigo: alertTemplate, // Incluir template selecionado
|
||||||
criadoPor: obterUsuarioId()
|
criadoPor: obterUsuarioId()
|
||||||
});
|
});
|
||||||
feedback = {
|
feedback = {
|
||||||
@@ -152,9 +153,10 @@
|
|||||||
severidadeMin: SeveridadeSeguranca;
|
severidadeMin: SeveridadeSeguranca;
|
||||||
tiposAtaque?: AtaqueCiberneticoTipo[];
|
tiposAtaque?: AtaqueCiberneticoTipo[];
|
||||||
reenvioMin: number;
|
reenvioMin: number;
|
||||||
|
templateCodigo?: string;
|
||||||
}) {
|
}) {
|
||||||
alertNomeConfig = config.nome ?? '';
|
alertNomeConfig = config.nome ?? '';
|
||||||
alertTemplate = 'incidente_critico'; // Mantém o template padrão
|
alertTemplate = config.templateCodigo ?? 'incidente_critico'; // Usar template salvo ou padrão
|
||||||
enviarPorEmail = config.canais?.email ?? true;
|
enviarPorEmail = config.canais?.email ?? true;
|
||||||
enviarPorChat = config.canais?.chat ?? false;
|
enviarPorChat = config.canais?.chat ?? false;
|
||||||
alertEmails = (config.emails ?? []).join('\n');
|
alertEmails = (config.emails ?? []).join('\n');
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { v } from 'convex/values';
|
import { v } from 'convex/values';
|
||||||
import { internalMutation, mutation, query } from './_generated/server';
|
import { internalMutation, mutation, query } from './_generated/server';
|
||||||
import { internal } from './_generated/api';
|
import { internal, api } from './_generated/api';
|
||||||
import type { Id } from './_generated/dataModel';
|
import type { Id } from './_generated/dataModel';
|
||||||
import type {
|
import type {
|
||||||
AtaqueCiberneticoTipo,
|
AtaqueCiberneticoTipo,
|
||||||
@@ -1436,6 +1436,7 @@ export const listarAlertConfigs = query({
|
|||||||
severidadeMin: severidadeValidator,
|
severidadeMin: severidadeValidator,
|
||||||
tiposAtaque: v.optional(v.array(ataqueValidator)),
|
tiposAtaque: v.optional(v.array(ataqueValidator)),
|
||||||
reenvioMin: v.number(),
|
reenvioMin: v.number(),
|
||||||
|
templateCodigo: v.optional(v.string()),
|
||||||
criadoEm: v.number(),
|
criadoEm: v.number(),
|
||||||
atualizadoEm: v.number()
|
atualizadoEm: v.number()
|
||||||
})
|
})
|
||||||
@@ -1456,6 +1457,7 @@ export const listarAlertConfigs = query({
|
|||||||
severidadeMin: r.severidadeMin,
|
severidadeMin: r.severidadeMin,
|
||||||
tiposAtaque: r.tiposAtaque,
|
tiposAtaque: r.tiposAtaque,
|
||||||
reenvioMin: r.reenvioMin,
|
reenvioMin: r.reenvioMin,
|
||||||
|
templateCodigo: r.templateCodigo,
|
||||||
criadoEm: r.criadoEm,
|
criadoEm: r.criadoEm,
|
||||||
atualizadoEm: r.atualizadoEm
|
atualizadoEm: r.atualizadoEm
|
||||||
}));
|
}));
|
||||||
@@ -1472,6 +1474,7 @@ export const salvarAlertConfig = mutation({
|
|||||||
severidadeMin: severidadeValidator,
|
severidadeMin: severidadeValidator,
|
||||||
tiposAtaque: v.optional(v.array(ataqueValidator)),
|
tiposAtaque: v.optional(v.array(ataqueValidator)),
|
||||||
reenvioMin: v.number(),
|
reenvioMin: v.number(),
|
||||||
|
templateCodigo: v.optional(v.string()), // Template a ser usado
|
||||||
criadoPor: v.id('usuarios')
|
criadoPor: v.id('usuarios')
|
||||||
},
|
},
|
||||||
returns: v.object({ _id: v.id('alertConfigs') }),
|
returns: v.object({ _id: v.id('alertConfigs') }),
|
||||||
@@ -1486,6 +1489,7 @@ export const salvarAlertConfig = mutation({
|
|||||||
severidadeMin: args.severidadeMin,
|
severidadeMin: args.severidadeMin,
|
||||||
tiposAtaque: args.tiposAtaque,
|
tiposAtaque: args.tiposAtaque,
|
||||||
reenvioMin: args.reenvioMin,
|
reenvioMin: args.reenvioMin,
|
||||||
|
templateCodigo: args.templateCodigo,
|
||||||
atualizadoEm: agora
|
atualizadoEm: agora
|
||||||
});
|
});
|
||||||
return { _id: args.configId };
|
return { _id: args.configId };
|
||||||
@@ -1498,6 +1502,7 @@ export const salvarAlertConfig = mutation({
|
|||||||
severidadeMin: args.severidadeMin,
|
severidadeMin: args.severidadeMin,
|
||||||
tiposAtaque: args.tiposAtaque,
|
tiposAtaque: args.tiposAtaque,
|
||||||
reenvioMin: args.reenvioMin,
|
reenvioMin: args.reenvioMin,
|
||||||
|
templateCodigo: args.templateCodigo ?? 'incidente_critico', // Padrão
|
||||||
criadoPor: args.criadoPor,
|
criadoPor: args.criadoPor,
|
||||||
criadoEm: agora,
|
criadoEm: agora,
|
||||||
atualizadoEm: agora
|
atualizadoEm: agora
|
||||||
@@ -1523,6 +1528,200 @@ export const dispararAlertasInternos = internalMutation({
|
|||||||
const evento = await ctx.db.get(args.eventoId);
|
const evento = await ctx.db.get(args.eventoId);
|
||||||
if (!evento) return null;
|
if (!evento) return null;
|
||||||
|
|
||||||
|
// Buscar todas as configurações de alerta ativas
|
||||||
|
const alertConfigs = await ctx.db.query('alertConfigs').collect();
|
||||||
|
|
||||||
|
// Obter URL do sistema
|
||||||
|
let urlSistema = process.env.FRONTEND_URL || 'http://localhost:5173';
|
||||||
|
if (!urlSistema.match(/^https?:\/\//i)) {
|
||||||
|
urlSistema = `http://${urlSistema}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formatar data/hora
|
||||||
|
const dataHora = new Date(evento.timestamp).toLocaleString('pt-BR', {
|
||||||
|
day: '2-digit',
|
||||||
|
month: '2-digit',
|
||||||
|
year: 'numeric',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mapear severidade para texto legível
|
||||||
|
const severityLabels: Record<SeveridadeSeguranca, string> = {
|
||||||
|
informativo: 'Informativo',
|
||||||
|
baixo: 'Baixo',
|
||||||
|
moderado: 'Moderado',
|
||||||
|
alto: 'Alto',
|
||||||
|
critico: 'Crítico'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mapear tipo de ataque para texto legível
|
||||||
|
const attackLabels: Record<string, string> = {
|
||||||
|
phishing: 'Phishing',
|
||||||
|
malware: 'Malware',
|
||||||
|
ransomware: 'Ransomware',
|
||||||
|
brute_force: 'Brute Force',
|
||||||
|
credential_stuffing: 'Credential Stuffing',
|
||||||
|
sql_injection: 'SQL Injection',
|
||||||
|
xss: 'XSS',
|
||||||
|
path_traversal: 'Path Traversal',
|
||||||
|
command_injection: 'Command Injection',
|
||||||
|
nosql_injection: 'NoSQL Injection',
|
||||||
|
xxe: 'XXE',
|
||||||
|
man_in_the_middle: 'MITM',
|
||||||
|
ddos: 'DDoS',
|
||||||
|
engenharia_social: 'Engenharia Social',
|
||||||
|
cve_exploit: 'Exploração de CVE',
|
||||||
|
apt: 'APT',
|
||||||
|
zero_day: 'Zero-Day',
|
||||||
|
supply_chain: 'Supply Chain',
|
||||||
|
fileless_malware: 'Fileless Malware',
|
||||||
|
polymorphic_malware: 'Polymorphic',
|
||||||
|
ransomware_lateral: 'Ransomware Lateral',
|
||||||
|
deepfake_phishing: 'Deepfake Phishing',
|
||||||
|
adversarial_ai: 'Ataque IA',
|
||||||
|
side_channel: 'Side-Channel',
|
||||||
|
firmware_bootloader: 'Firmware/Bootloader',
|
||||||
|
bec: 'BEC',
|
||||||
|
botnet: 'Botnet',
|
||||||
|
ot_ics: 'OT/ICS',
|
||||||
|
quantum_attack: 'Quantum'
|
||||||
|
};
|
||||||
|
|
||||||
|
const tipoAtaqueLabel = attackLabels[evento.tipoAtaque] || evento.tipoAtaque.replace(/_/g, ' ');
|
||||||
|
const severidadeLabel = severityLabels[evento.severidade] || evento.severidade;
|
||||||
|
|
||||||
|
// Função auxiliar para verificar se a severidade atende ao mínimo
|
||||||
|
const severidadeAtende = (severidade: SeveridadeSeguranca, min: SeveridadeSeguranca): boolean => {
|
||||||
|
const ordem: SeveridadeSeguranca[] = ['informativo', 'baixo', 'moderado', 'alto', 'critico'];
|
||||||
|
return ordem.indexOf(severidade) >= ordem.indexOf(min);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Processar cada configuração de alerta
|
||||||
|
for (const config of alertConfigs) {
|
||||||
|
// Verificar se a severidade atende ao mínimo
|
||||||
|
if (!severidadeAtende(evento.severidade, config.severidadeMin)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar se o tipo de ataque está na lista (se especificado)
|
||||||
|
if (config.tiposAtaque && config.tiposAtaque.length > 0) {
|
||||||
|
if (!config.tiposAtaque.includes(evento.tipoAtaque)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buscar usuário sistema para enviar emails (ou usar o primeiro usuário TI)
|
||||||
|
const rolesTi = await ctx.db
|
||||||
|
.query('roles')
|
||||||
|
.withIndex('by_nivel', (q) => q.lte('nivel', 1))
|
||||||
|
.first();
|
||||||
|
let usuarioSistema: Id<'usuarios'> | undefined;
|
||||||
|
if (rolesTi) {
|
||||||
|
const usuarioTi = await ctx.db
|
||||||
|
.query('usuarios')
|
||||||
|
.withIndex('by_role', (q) => q.eq('roleId', rolesTi._id))
|
||||||
|
.first();
|
||||||
|
if (usuarioTi) {
|
||||||
|
usuarioSistema = usuarioTi._id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!usuarioSistema) {
|
||||||
|
console.error('❌ Não foi possível encontrar usuário sistema para enviar alertas');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preparar variáveis do template
|
||||||
|
const variaveisTemplate = {
|
||||||
|
destinatarioNome: '', // Será preenchido por destinatário
|
||||||
|
tipoAtaque: tipoAtaqueLabel,
|
||||||
|
severidade: severidadeLabel,
|
||||||
|
descricao: evento.descricao,
|
||||||
|
origemIp: evento.origemIp || 'N/A',
|
||||||
|
dataHora,
|
||||||
|
urlSistema
|
||||||
|
};
|
||||||
|
|
||||||
|
// ENVIAR EMAILS
|
||||||
|
if (config.canais.email && config.emails.length > 0) {
|
||||||
|
const templateCodigo = config.templateCodigo || 'incidente_critico';
|
||||||
|
|
||||||
|
for (const emailDestinatario of config.emails) {
|
||||||
|
// Buscar usuário pelo email
|
||||||
|
const usuarioDestinatario = await ctx.db
|
||||||
|
.query('usuarios')
|
||||||
|
.filter((q) => q.eq(q.field('email'), emailDestinatario))
|
||||||
|
.first();
|
||||||
|
|
||||||
|
if (usuarioDestinatario) {
|
||||||
|
variaveisTemplate.destinatarioNome = usuarioDestinatario.nome;
|
||||||
|
|
||||||
|
// Enviar email usando template
|
||||||
|
ctx.scheduler
|
||||||
|
.runAfter(0, api.email.enviarEmailComTemplate, {
|
||||||
|
destinatario: emailDestinatario,
|
||||||
|
destinatarioId: usuarioDestinatario._id,
|
||||||
|
templateCodigo,
|
||||||
|
variaveis: variaveisTemplate,
|
||||||
|
enviadoPor: usuarioSistema
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(
|
||||||
|
`Erro ao agendar email de alerta para ${emailDestinatario}:`,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ENVIAR CHAT
|
||||||
|
if (config.canais.chat && config.chatUsers.length > 0) {
|
||||||
|
const templateCodigo = config.templateCodigo || 'incidente_critico';
|
||||||
|
|
||||||
|
// Buscar template para chat
|
||||||
|
const template = await ctx.runQuery(api.templatesMensagens.obterTemplatePorCodigo, {
|
||||||
|
codigo: templateCodigo
|
||||||
|
});
|
||||||
|
|
||||||
|
if (template) {
|
||||||
|
// Importar função de renderização
|
||||||
|
const { renderizarTemplateChatFromDoc } = await import('./templatesMensagens');
|
||||||
|
|
||||||
|
for (const chatUserEmail of config.chatUsers) {
|
||||||
|
// Buscar usuário pelo email
|
||||||
|
const usuarioDestinatario = await ctx.db
|
||||||
|
.query('usuarios')
|
||||||
|
.filter((q) => q.eq(q.field('email'), chatUserEmail))
|
||||||
|
.first();
|
||||||
|
|
||||||
|
if (usuarioDestinatario && usuarioSistema) {
|
||||||
|
variaveisTemplate.destinatarioNome = usuarioDestinatario.nome;
|
||||||
|
|
||||||
|
// Renderizar mensagem do template
|
||||||
|
const mensagemChat = renderizarTemplateChatFromDoc(template, variaveisTemplate);
|
||||||
|
|
||||||
|
// Usar função interna para criar conversa e enviar mensagem
|
||||||
|
ctx.scheduler
|
||||||
|
.runAfter(0, internal.security.enviarMensagemChatSistema, {
|
||||||
|
usuarioSistemaId: usuarioSistema,
|
||||||
|
usuarioDestinatarioId: usuarioDestinatario._id,
|
||||||
|
mensagem: mensagemChat
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(
|
||||||
|
`Erro ao agendar mensagem de chat para ${chatUserEmail}:`,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manter notificação padrão para usuários TI (compatibilidade)
|
||||||
const rolesTi = await ctx.db
|
const rolesTi = await ctx.db
|
||||||
.query('roles')
|
.query('roles')
|
||||||
.withIndex('by_nivel', (q) => q.lte('nivel', 1))
|
.withIndex('by_nivel', (q) => q.lte('nivel', 1))
|
||||||
@@ -1547,7 +1746,7 @@ export const dispararAlertasInternos = internalMutation({
|
|||||||
conversaId: undefined,
|
conversaId: undefined,
|
||||||
mensagemId: undefined,
|
mensagemId: undefined,
|
||||||
remetenteId: undefined,
|
remetenteId: undefined,
|
||||||
titulo: `🚨 ${evento.severidade.toUpperCase()} - ${evento.tipoAtaque.replace(/_/g, ' ')}`,
|
titulo: `🚨 ${evento.severidade.toUpperCase()} - ${tipoAtaqueLabel}`,
|
||||||
descricao: evento.descricao,
|
descricao: evento.descricao,
|
||||||
lida: false,
|
lida: false,
|
||||||
criadaEm: Date.now()
|
criadaEm: Date.now()
|
||||||
@@ -1558,6 +1757,80 @@ export const dispararAlertasInternos = internalMutation({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Função interna para enviar mensagem de chat do sistema
|
||||||
|
*/
|
||||||
|
export const enviarMensagemChatSistema = internalMutation({
|
||||||
|
args: {
|
||||||
|
usuarioSistemaId: v.id('usuarios'),
|
||||||
|
usuarioDestinatarioId: v.id('usuarios'),
|
||||||
|
mensagem: v.string()
|
||||||
|
},
|
||||||
|
returns: v.null(),
|
||||||
|
handler: async (ctx, args) => {
|
||||||
|
// Buscar ou criar conversa individual entre sistema e destinatário
|
||||||
|
const conversasExistentes = await ctx.db
|
||||||
|
.query('conversas')
|
||||||
|
.filter((q) => q.eq(q.field('tipo'), 'individual'))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let conversaId: Id<'conversas'> | null = null;
|
||||||
|
|
||||||
|
for (const conversa of conversasExistentes) {
|
||||||
|
if (
|
||||||
|
conversa.participantes.length === 2 &&
|
||||||
|
conversa.participantes.includes(args.usuarioSistemaId) &&
|
||||||
|
conversa.participantes.includes(args.usuarioDestinatarioId)
|
||||||
|
) {
|
||||||
|
conversaId = conversa._id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conversaId) {
|
||||||
|
// Criar nova conversa
|
||||||
|
conversaId = await ctx.db.insert('conversas', {
|
||||||
|
tipo: 'individual',
|
||||||
|
participantes: [args.usuarioSistemaId, args.usuarioDestinatarioId],
|
||||||
|
criadoPor: args.usuarioSistemaId,
|
||||||
|
criadoEm: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Criar mensagem
|
||||||
|
const mensagemId = await ctx.db.insert('mensagens', {
|
||||||
|
conversaId,
|
||||||
|
remetenteId: args.usuarioSistemaId,
|
||||||
|
conteudo: args.mensagem,
|
||||||
|
conteudoBusca: args.mensagem.toLowerCase(),
|
||||||
|
tipo: 'texto',
|
||||||
|
criadaEm: Date.now()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Atualizar última mensagem da conversa
|
||||||
|
await ctx.db.patch(conversaId, {
|
||||||
|
ultimaMensagem: args.mensagem.substring(0, 100),
|
||||||
|
ultimaMensagemTimestamp: Date.now(),
|
||||||
|
ultimaMensagemRemetenteId: args.usuarioSistemaId
|
||||||
|
});
|
||||||
|
|
||||||
|
// Criar notificação para destinatário
|
||||||
|
await ctx.db.insert('notificacoes', {
|
||||||
|
usuarioId: args.usuarioDestinatarioId,
|
||||||
|
tipo: 'nova_mensagem',
|
||||||
|
conversaId,
|
||||||
|
mensagemId,
|
||||||
|
remetenteId: args.usuarioSistemaId,
|
||||||
|
titulo: '🚨 Alerta de Segurança',
|
||||||
|
descricao: args.mensagem.substring(0, 100),
|
||||||
|
lida: false,
|
||||||
|
criadaEm: Date.now()
|
||||||
|
});
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export const expirarBloqueiosIpAutomaticos = internalMutation({
|
export const expirarBloqueiosIpAutomaticos = internalMutation({
|
||||||
args: {},
|
args: {},
|
||||||
returns: v.null(),
|
returns: v.null(),
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ export const systemTables = {
|
|||||||
severidadeMin: severidadeSeguranca,
|
severidadeMin: severidadeSeguranca,
|
||||||
tiposAtaque: v.optional(v.array(ataqueCiberneticoTipo)),
|
tiposAtaque: v.optional(v.array(ataqueCiberneticoTipo)),
|
||||||
reenvioMin: v.number(),
|
reenvioMin: v.number(),
|
||||||
|
templateCodigo: v.optional(v.string()), // Template a ser usado para email/chat
|
||||||
criadoPor: v.id('usuarios'),
|
criadoPor: v.id('usuarios'),
|
||||||
criadoEm: v.number(),
|
criadoEm: v.number(),
|
||||||
atualizadoEm: v.number()
|
atualizadoEm: v.number()
|
||||||
|
|||||||
@@ -701,6 +701,164 @@ export const criarTemplatesPadrao = mutation({
|
|||||||
],
|
],
|
||||||
categoria: 'email' as const,
|
categoria: 'email' as const,
|
||||||
tags: ['ausencia', 'reprovacao', 'gestao']
|
tags: ['ausencia', 'reprovacao', 'gestao']
|
||||||
|
},
|
||||||
|
// ===================== ALERTAS DE SEGURANÇA CIBERNÉTICA =====================
|
||||||
|
{
|
||||||
|
codigo: 'incidente_critico',
|
||||||
|
nome: 'Incidente Crítico - Ação Imediata',
|
||||||
|
titulo: '🚨 ALERTA CRÍTICO: {{tipoAtaque}}',
|
||||||
|
corpo:
|
||||||
|
"<html><body style='font-family: Arial, sans-serif; line-height: 1.6; color: #333;'>" +
|
||||||
|
"<div style='max-width: 600px; margin: 0 auto; padding: 20px;'>" +
|
||||||
|
"<h2 style='color: #DC2626;'>🚨 ALERTA CRÍTICO DE SEGURANÇA</h2>" +
|
||||||
|
'<p>Olá <strong>{{destinatarioNome}}</strong>,</p>' +
|
||||||
|
'<p>Um <strong>incidente crítico de segurança</strong> foi detectado no sistema:</p>' +
|
||||||
|
"<div style='background-color: #FEF2F2; border-left: 4px solid #DC2626; padding: 15px; border-radius: 8px; margin: 20px 0;'>" +
|
||||||
|
"<p style='margin: 0;'><strong>Tipo de Ataque:</strong> {{tipoAtaque}}</p>" +
|
||||||
|
"<p style='margin: 5px 0 0 0;'><strong>Severidade:</strong> <span style='color: #DC2626; font-weight: bold;'>{{severidade}}</span></p>" +
|
||||||
|
"<p style='margin: 5px 0 0 0;'><strong>Descrição:</strong> {{descricao}}</p>" +
|
||||||
|
"<p style='margin: 5px 0 0 0;'><strong>IP de Origem:</strong> {{origemIp}}</p>" +
|
||||||
|
"<p style='margin: 5px 0 0 0;'><strong>Data/Hora:</strong> {{dataHora}}</p>" +
|
||||||
|
'</div>' +
|
||||||
|
"<p style='color: #DC2626; font-weight: bold;'>⚠️ AÇÃO IMEDIATA NECESSÁRIA</p>" +
|
||||||
|
"<p style='margin-top: 30px;'>" +
|
||||||
|
"<a href='{{urlSistema}}/ti/cybersecurity' " +
|
||||||
|
"style='background-color: #DC2626; color: white; padding: 12px 24px; " +
|
||||||
|
"text-decoration: none; border-radius: 6px; display: inline-block;'>" +
|
||||||
|
'Ver Detalhes do Incidente' +
|
||||||
|
'</a>' +
|
||||||
|
'</p>' +
|
||||||
|
"<p style='color: #6B7280; font-size: 12px; margin-top: 30px;'>" +
|
||||||
|
'SGSE - Sistema de Gerenciamento de Secretaria - Equipe de Segurança' +
|
||||||
|
'</p>' +
|
||||||
|
'</div></body></html>',
|
||||||
|
variaveis: [
|
||||||
|
'destinatarioNome',
|
||||||
|
'tipoAtaque',
|
||||||
|
'severidade',
|
||||||
|
'descricao',
|
||||||
|
'origemIp',
|
||||||
|
'dataHora',
|
||||||
|
'urlSistema'
|
||||||
|
],
|
||||||
|
categoria: 'email' as const,
|
||||||
|
tags: ['seguranca', 'alerta', 'critico', 'cybersecurity']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
codigo: 'bloqueio_automatico',
|
||||||
|
nome: 'Bloqueio Automático',
|
||||||
|
titulo: '🔒 Bloqueio Automático: {{tipoAtaque}}',
|
||||||
|
corpo:
|
||||||
|
"<html><body style='font-family: Arial, sans-serif; line-height: 1.6; color: #333;'>" +
|
||||||
|
"<div style='max-width: 600px; margin: 0 auto; padding: 20px;'>" +
|
||||||
|
"<h2 style='color: #F59E0B;'>🔒 Bloqueio Automático Aplicado</h2>" +
|
||||||
|
'<p>Olá <strong>{{destinatarioNome}}</strong>,</p>' +
|
||||||
|
'<p>O sistema aplicou um <strong>bloqueio automático</strong> devido a uma tentativa de ataque detectada:</p>' +
|
||||||
|
"<div style='background-color: #FFFBEB; border-left: 4px solid #F59E0B; padding: 15px; border-radius: 8px; margin: 20px 0;'>" +
|
||||||
|
"<p style='margin: 0;'><strong>Tipo de Ataque:</strong> {{tipoAtaque}}</p>" +
|
||||||
|
"<p style='margin: 5px 0 0 0;'><strong>IP Bloqueado:</strong> {{origemIp}}</p>" +
|
||||||
|
"<p style='margin: 5px 0 0 0;'><strong>Descrição:</strong> {{descricao}}</p>" +
|
||||||
|
"<p style='margin: 5px 0 0 0;'><strong>Data/Hora:</strong> {{dataHora}}</p>" +
|
||||||
|
'</div>' +
|
||||||
|
"<p style='margin-top: 30px;'>" +
|
||||||
|
"<a href='{{urlSistema}}/ti/cybersecurity' " +
|
||||||
|
"style='background-color: #F59E0B; color: white; padding: 12px 24px; " +
|
||||||
|
"text-decoration: none; border-radius: 6px; display: inline-block;'>" +
|
||||||
|
'Ver Detalhes do Bloqueio' +
|
||||||
|
'</a>' +
|
||||||
|
'</p>' +
|
||||||
|
"<p style='color: #6B7280; font-size: 12px; margin-top: 30px;'>" +
|
||||||
|
'SGSE - Sistema de Gerenciamento de Secretaria - Equipe de Segurança' +
|
||||||
|
'</p>' +
|
||||||
|
'</div></body></html>',
|
||||||
|
variaveis: [
|
||||||
|
'destinatarioNome',
|
||||||
|
'tipoAtaque',
|
||||||
|
'origemIp',
|
||||||
|
'descricao',
|
||||||
|
'dataHora',
|
||||||
|
'urlSistema'
|
||||||
|
],
|
||||||
|
categoria: 'email' as const,
|
||||||
|
tags: ['seguranca', 'bloqueio', 'automatico', 'cybersecurity']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
codigo: 'sumario_30min',
|
||||||
|
nome: 'Sumário 30 Min',
|
||||||
|
titulo: '📊 Sumário de Segurança - Últimos 30 minutos',
|
||||||
|
corpo:
|
||||||
|
"<html><body style='font-family: Arial, sans-serif; line-height: 1.6; color: #333;'>" +
|
||||||
|
"<div style='max-width: 600px; margin: 0 auto; padding: 20px;'>" +
|
||||||
|
"<h2 style='color: #2563EB;'>📊 Sumário de Segurança</h2>" +
|
||||||
|
'<p>Olá <strong>{{destinatarioNome}}</strong>,</p>' +
|
||||||
|
'<p>Resumo dos eventos de segurança dos últimos 30 minutos:</p>' +
|
||||||
|
"<div style='background-color: #EFF6FF; border-left: 4px solid #2563EB; padding: 15px; border-radius: 8px; margin: 20px 0;'>" +
|
||||||
|
"<p style='margin: 0;'><strong>Total de Eventos:</strong> {{totalEventos}}</p>" +
|
||||||
|
"<p style='margin: 5px 0 0 0;'><strong>Eventos Críticos:</strong> {{eventosCriticos}}</p>" +
|
||||||
|
"<p style='margin: 5px 0 0 0;'><strong>Eventos Altos:</strong> {{eventosAltos}}</p>" +
|
||||||
|
"<p style='margin: 5px 0 0 0;'><strong>IPs Bloqueados:</strong> {{ipsBloqueados}}</p>" +
|
||||||
|
'</div>' +
|
||||||
|
"<p style='margin-top: 30px;'>" +
|
||||||
|
"<a href='{{urlSistema}}/ti/cybersecurity' " +
|
||||||
|
"style='background-color: #2563EB; color: white; padding: 12px 24px; " +
|
||||||
|
"text-decoration: none; border-radius: 6px; display: inline-block;'>" +
|
||||||
|
'Ver Relatório Completo' +
|
||||||
|
'</a>' +
|
||||||
|
'</p>' +
|
||||||
|
"<p style='color: #6B7280; font-size: 12px; margin-top: 30px;'>" +
|
||||||
|
'SGSE - Sistema de Gerenciamento de Secretaria - Equipe de Segurança' +
|
||||||
|
'</p>' +
|
||||||
|
'</div></body></html>',
|
||||||
|
variaveis: [
|
||||||
|
'destinatarioNome',
|
||||||
|
'totalEventos',
|
||||||
|
'eventosCriticos',
|
||||||
|
'eventosAltos',
|
||||||
|
'ipsBloqueados',
|
||||||
|
'urlSistema'
|
||||||
|
],
|
||||||
|
categoria: 'email' as const,
|
||||||
|
tags: ['seguranca', 'sumario', 'relatorio', 'cybersecurity']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
codigo: 'anormalidade',
|
||||||
|
nome: 'Anomalia Detectada',
|
||||||
|
titulo: '⚠️ Anomalia Detectada: {{tipoAtaque}}',
|
||||||
|
corpo:
|
||||||
|
"<html><body style='font-family: Arial, sans-serif; line-height: 1.6; color: #333;'>" +
|
||||||
|
"<div style='max-width: 600px; margin: 0 auto; padding: 20px;'>" +
|
||||||
|
"<h2 style='color: #F59E0B;'>⚠️ Anomalia Detectada</h2>" +
|
||||||
|
'<p>Olá <strong>{{destinatarioNome}}</strong>,</p>' +
|
||||||
|
'<p>O sistema detectou uma <strong>anomalia de segurança</strong> que requer atenção:</p>' +
|
||||||
|
"<div style='background-color: #FFFBEB; border-left: 4px solid #F59E0B; padding: 15px; border-radius: 8px; margin: 20px 0;'>" +
|
||||||
|
"<p style='margin: 0;'><strong>Tipo de Ataque:</strong> {{tipoAtaque}}</p>" +
|
||||||
|
"<p style='margin: 5px 0 0 0;'><strong>Severidade:</strong> {{severidade}}</p>" +
|
||||||
|
"<p style='margin: 5px 0 0 0;'><strong>Descrição:</strong> {{descricao}}</p>" +
|
||||||
|
"<p style='margin: 5px 0 0 0;'><strong>IP de Origem:</strong> {{origemIp}}</p>" +
|
||||||
|
"<p style='margin: 5px 0 0 0;'><strong>Data/Hora:</strong> {{dataHora}}</p>" +
|
||||||
|
'</div>' +
|
||||||
|
"<p style='margin-top: 30px;'>" +
|
||||||
|
"<a href='{{urlSistema}}/ti/cybersecurity' " +
|
||||||
|
"style='background-color: #F59E0B; color: white; padding: 12px 24px; " +
|
||||||
|
"text-decoration: none; border-radius: 6px; display: inline-block;'>" +
|
||||||
|
'Ver Detalhes da Anomalia' +
|
||||||
|
'</a>' +
|
||||||
|
'</p>' +
|
||||||
|
"<p style='color: #6B7280; font-size: 12px; margin-top: 30px;'>" +
|
||||||
|
'SGSE - Sistema de Gerenciamento de Secretaria - Equipe de Segurança' +
|
||||||
|
'</p>' +
|
||||||
|
'</div></body></html>',
|
||||||
|
variaveis: [
|
||||||
|
'destinatarioNome',
|
||||||
|
'tipoAtaque',
|
||||||
|
'severidade',
|
||||||
|
'descricao',
|
||||||
|
'origemIp',
|
||||||
|
'dataHora',
|
||||||
|
'urlSistema'
|
||||||
|
],
|
||||||
|
categoria: 'email' as const,
|
||||||
|
tags: ['seguranca', 'anomalia', 'alerta', 'cybersecurity']
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user