O módulo de roteamento principal do sistema Xunfeng, projetado para auditoria automatizada e gestão de ativos em redes corporativas, é construído sobre o microframework Flask e o MongoDB. A camada de apresentação consolida a lógica de interface, orquestrando consultas paginadas, gerenciamento de tarefas de varredura, sicnronização de extensões e controle de acesso. A arquitetura segue um padrão baseado em decoradores para validar sessões e isolar riscos de requisições cross-site antes da execução das rotas.
Camada de Proteção e Middleware
A validação de estado examina o objeto de sessão global para confirmar credenciais ativas. Caso o marcador de autenticação esteja ausente ou inválido, o fluxo é interrompido e redirecionado para o ponto de entrada. De forma similar, a mitigação de CSRF analisa o cabeçalho de origem, comparando o domínio requisitante com o host da aplicação para descartar chamadas externas maliciosas.
from functools import wraps
from flask import session, redirect, url_for
def exigir_autenticacao(view_func):
@wraps(view_func)
def validador_sessao(*args, **kwargs):
estado = session.get('usuario_validado')
if estado == 'ativo':
return view_func(*args, **kwargs)
return redirect(url_for('endpoint_login'))
return validador_sessao
def proteger_csrf(view_func):
@wraps(view_func)
def validador_origem(*args, **kwargs):
ref = request.referrer
if ref and request.host in ref:
return view_func(*args, **kwargs)
return redirect(url_for('tratamento_erro'))
return validador_origem
Consulta de Ativos e Paginação
A interface de busca processa strings de consulta contendo múltiplos endereços IP, converte a entrada para filtros nativos do MongoDB e aplica ordenação decrescente por timestamp. O mecanismo de paginação calcula dinamicamente os limites e deslocamentos (offsets), retornando os dados processados junto com o contador total para renderização no front-end.
@app.route('/buscar')
@exigir_autenticacao
def listar_ativos():
termo = request.args.get('query', '').strip()
pagina = max(int(request.args.get('pg', 1)), 1)
if termo:
segmentacao = termo.split(';')
filtro_db = converter_para_mongo(segmentacao)
cursor = db.inventario.find(filtro_db).sort('registro_ts', -1)
cursor.limit(itens_por_pagina).skip((pagina - 1) * itens_por_pagina)
return render_template('painel.html',
dados=cursor,
total_registros=cursor.count(),
termo_entrada=termo)
return render_template('painel.html', dados=[], total_registros=0)
Orquestração e Detalhamento de Tarefas
A criação de novos trabalhos de escaneamento valida a lista de alvos, normaliza pares de IP/porta e persiste os metadados na coleção de tarefas. A visualização de resultados agrupa as execuções por lotes temporais. Para evitar latência em conjuntos volumosos, a aplicação pré-carrega os hostnames em um mapa de hash local, unindo-os aos registros de vulnerabilidade durante a iteração.
@app.route('/detalhar_job')
@exigir_autenticacao
def exibir_job():
job_id = request.args.get('id_tarefa')
data_lote = request.args.get('lote_data')
saida_formatada = []
job = db.tarefas.find_one({'_id': ObjectId(job_id)})
if job:
lotes = db.vulnerabilidades.distinct('execucao_em', {'tarefa_ref': ObjectId(job_id)})
if not data_lote and lotes:
lotes.sort(reverse=True)
data_lote = lotes[0]
if data_lote:
filtro = {'tarefa_ref': ObjectId(job_id), 'execucao_em': data_lote}
itens = db.vulnerabilidades.find(filtro).sort('detectado_em', -1)
mapa_dns = {d['ip']: d.get('dns_name', '')
for d in db.inventario.find({'dns_name': {'$ne': None}})}
for vuln in itens:
vuln['host_name'] = mapa_dns.get(vuln['ip'], 'Indisponível')
saida_formatada.append(vuln)
return render_template('detalhes.html', resultados=saida_formatada)
Exportação de Dados e Gestão de Extensões
A geração de relatórios em formato XLS emprega a biblioteca xlwt para montagem em memória. As linhas são transformadas em tuplas, escritas sequencialmente e retornadas via resposta HTTP com cabeçalhos de toça de download. Paralelamente, o sistema permite a remoção de plugins, limpando tanto o documento no banco quanto o arquivo físico no disco.
@app.route('/sincronizar_extensao')
@exigir_autenticacao
@proteger_csrf
def baixar_plugin_remoto():
identificador = request.args.get('cod_ext')
registro = db.atualizacoes_disponiveis.find_one({'hash_unico': identificador})
if not registro:
return 'nao_encontrado', 404
destino = os.path.join(CAMINHO_MODULOS, os.path.basename(registro['url']))
urllib.urlretrieve(registro['url'], destino)
if os.path.exists(destino):
metadados_extra = {}
extensao = destino.rsplit('.', 1)[-1]
if extensao == 'py':
nome_base = os.path.splitext(os.path.basename(destino))[0]
mod = __import__(nome_base)
metadados_extra = mod.exportar_metadados()
metadados_extra['nome_arquivo'] = nome_base
else:
with open(destino, 'r') as f:
metadados_extra = json.load(f)
metadados_extra['nome_arquivo'] = destino
documento_final = {
'criado_em': datetime.utcnow(),
'origem': 'repositorio_externo'
}
documento_final.update(metadados_extra)
db.modulos.insert(documento_final)
db.atualizacoes_disponiveis.update_one(
{'hash_unico': identificador},
{'$set': {'estado': 'instalado'}}
)
return 'operacao_concluida', 200
return 'falha_download', 500
Parâmetros do Sistema e Controle de Sessão
O painel administrativo expõe configurações categorizadas por motor de execução, interpretando listas e escalares antes da persistência atômica. O fluxo de login valida as credenciais contra variáveis de configuração da aplicação e manipula o estado da sessão, enquanto as rotas de fallback mapeiam códigos de status HTTP para templates específicos de erro.
@app.route('/acesso', methods=['GET', 'POST'])
def controlar_acesso():
if request.method == 'POST':
credencial_usuario = request.form.get('login')
credencial_senha = request.form.get('senha')
if credencial_usuario == app.config['ADMIN_ID'] and credencial_senha == app.config['ADMIN_SECRET']:
session['usuario_validado'] = 'ativo'
return redirect(url_for('listar_ativos'))
return redirect(url_for('controlar_acesso'))
return render_template('login_view.html')
@app.route('/sair')
@exigir_autenticacao
def encerrar_acesso():
session.clear()
return redirect(url_for('controlar_acesso'))
@app.route('/erro_navegacao')
def exibir_404():
return render_template('erro_404.html'), 404
@app.route('/erro_servidor')
def exibir_500():
return render_template('erro_500.html'), 500