- index.html: sustituye grid de bloques por 6 tabs horizontales (Cuentas base / Redes sociales / Mensajería / Streaming / Buscadores / Data brokers) con items como tarjetas en grid responsive - egosurfing.html: dorking expandido de 12 dorks a ~95 organizados en 10 categorías OSINT profesionales (nombre, email, teléfono, alias, data brokers, pastes/brechas, registros oficiales, perfil profesional, geolocalización, archivo histórico) con sistema de tabs dinámico - erase.js: límites de longitud en campos opcionales (nickname 100, phone 30, address 300, extra 500) + type-check explícito en email Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
63 lines
2.2 KiB
JavaScript
63 lines
2.2 KiB
JavaScript
'use strict';
|
|
|
|
const crypto = require('crypto');
|
|
const { sendErasureMail, PROVIDER_DATA } = require('../services/mailer');
|
|
|
|
const ALLOWED_PROVIDERS = new Set(Object.keys(PROVIDER_DATA));
|
|
|
|
module.exports = async (req, res) => {
|
|
try {
|
|
const { provider, email,
|
|
nickname: rawNick, phone: rawPhone,
|
|
address: rawAddr, extra: rawExtra } = req.body;
|
|
|
|
// Validación mínima
|
|
if (!provider || !email) {
|
|
return res.status(400).json({ error: 'provider y email son obligatorios' });
|
|
}
|
|
|
|
// Validar proveedor conocido (previene abusos de relay)
|
|
if (!ALLOWED_PROVIDERS.has(provider)) {
|
|
return res.status(400).json({ error: 'Proveedor no soportado. Usa el formulario oficial.' });
|
|
}
|
|
|
|
// Validación básica de email
|
|
if (typeof email !== 'string' || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
return res.status(400).json({ error: 'Email inválido' });
|
|
}
|
|
|
|
// Límites de longitud en campos opcionales (defensa en profundidad)
|
|
const nickname = String(rawNick || '').slice(0, 100).trim();
|
|
const phone = String(rawPhone || '').slice(0, 30).trim();
|
|
const address = String(rawAddr || '').slice(0, 300).trim();
|
|
const extra = String(rawExtra || '').slice(0, 500).trim();
|
|
|
|
// Hash irreversible para referencia (auditoría sin almacenar PII)
|
|
const hash = crypto
|
|
.createHash('sha256')
|
|
.update(email + (process.env.SALT || 'resetea-default-salt'))
|
|
.digest('hex');
|
|
|
|
const result = await sendErasureMail({ provider, email, nickname, phone, address, extra });
|
|
|
|
if (result.skipped) {
|
|
return res.json({
|
|
status: 'use_form',
|
|
message: 'Este proveedor no acepta solicitudes por email. Usa su formulario oficial.',
|
|
formUrl: result.formUrl,
|
|
reference: hash.substring(0, 12),
|
|
});
|
|
}
|
|
|
|
// PII fuera de scope aquí — solo el hash queda
|
|
res.json({
|
|
status: 'ok',
|
|
message: 'Solicitud enviada. Guarda el código de referencia.',
|
|
reference: hash.substring(0, 12),
|
|
});
|
|
|
|
} catch (e) {
|
|
console.error('erase route error:', e.message);
|
|
res.status(500).json({ error: 'Error interno. Inténtalo de nuevo o usa el formulario oficial del proveedor.' });
|
|
}
|
|
};
|