CRITICAL fixes: - Restrict X-View-Tenant impersonation to global admin only (was any admin) - Add authorization to subscription endpoints (was open to any user) - Make webhook signature verification mandatory (was skippable) - Remove databaseName from JWT payload (resolve server-side with cache) - Reduce body size limit from 1GB to 10MB (50MB for bulk CFDI) - Restrict .env file permissions to 600 HIGH fixes: - Add authorization to SAT cron endpoints (global admin only) - Add Content-Security-Policy and Permissions-Policy headers - Centralize isGlobalAdmin() utility with caching - Add rate limiting on auth endpoints (express-rate-limit) - Require authentication on logout endpoint MEDIUM fixes: - Replace Math.random() with crypto.randomBytes for temp passwords - Remove console.log of temporary passwords in production - Remove DB credentials from admin notification email - Add escapeHtml() to email templates (prevent HTML injection) - Add file size validation on FIEL upload (50KB max) - Require TLS for SMTP connections - Normalize email to lowercase before uniqueness check - Remove hardcoded default for FIEL_ENCRYPTION_KEY Also includes: - Complete production deployment documentation - API reference documentation - Security audit report with remediation details - Updated README with v0.5.0 changelog - New client admin email template - Utility scripts (create-carlos, test-emails) - PM2 ecosystem config updates Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
97 lines
3.0 KiB
TypeScript
97 lines
3.0 KiB
TypeScript
import { emailService } from '../src/services/email/email.service.js';
|
|
|
|
const recipients = ['ivan@horuxfin.com', 'carlos@horuxfin.com'];
|
|
|
|
async function sendAllSamples() {
|
|
for (const to of recipients) {
|
|
console.log(`\n=== Enviando a ${to} ===`);
|
|
|
|
// 1. Welcome
|
|
console.log('1/6 Bienvenida...');
|
|
await emailService.sendWelcome(to, {
|
|
nombre: 'Ivan Alcaraz',
|
|
email: 'ivan@horuxfin.com',
|
|
tempPassword: 'TempPass123!',
|
|
});
|
|
|
|
// 2. FIEL notification (goes to ADMIN_EMAIL, but we override for test)
|
|
console.log('2/6 Notificación FIEL...');
|
|
// Send directly since sendFielNotification goes to admin
|
|
const { fielNotificationEmail } = await import('../src/services/email/templates/fiel-notification.js');
|
|
const { createTransport } = await import('nodemailer');
|
|
const { env } = await import('../src/config/env.js');
|
|
const transport = createTransport({
|
|
host: env.SMTP_HOST,
|
|
port: parseInt(env.SMTP_PORT),
|
|
secure: false,
|
|
auth: { user: env.SMTP_USER, pass: env.SMTP_PASS },
|
|
});
|
|
const fielHtml = fielNotificationEmail({
|
|
clienteNombre: 'Consultoria Alcaraz Salazar',
|
|
clienteRfc: 'CAS200101XXX',
|
|
});
|
|
await transport.sendMail({
|
|
from: env.SMTP_FROM,
|
|
to,
|
|
subject: '[Consultoria Alcaraz Salazar] subió su FIEL (MUESTRA)',
|
|
html: fielHtml,
|
|
});
|
|
|
|
// 3. Payment confirmed
|
|
console.log('3/6 Pago confirmado...');
|
|
await emailService.sendPaymentConfirmed(to, {
|
|
nombre: 'Ivan Alcaraz',
|
|
amount: 1499,
|
|
plan: 'Enterprise',
|
|
date: '16 de marzo de 2026',
|
|
});
|
|
|
|
// 4. Payment failed
|
|
console.log('4/6 Pago fallido...');
|
|
const { paymentFailedEmail } = await import('../src/services/email/templates/payment-failed.js');
|
|
const failedHtml = paymentFailedEmail({
|
|
nombre: 'Ivan Alcaraz',
|
|
amount: 1499,
|
|
plan: 'Enterprise',
|
|
});
|
|
await transport.sendMail({
|
|
from: env.SMTP_FROM,
|
|
to,
|
|
subject: 'Problema con tu pago - Horux360 (MUESTRA)',
|
|
html: failedHtml,
|
|
});
|
|
|
|
// 5. Subscription expiring
|
|
console.log('5/6 Suscripción por vencer...');
|
|
await emailService.sendSubscriptionExpiring(to, {
|
|
nombre: 'Ivan Alcaraz',
|
|
plan: 'Enterprise',
|
|
expiresAt: '21 de marzo de 2026',
|
|
});
|
|
|
|
// 6. Subscription cancelled
|
|
console.log('6/6 Suscripción cancelada...');
|
|
const { subscriptionCancelledEmail } = await import('../src/services/email/templates/subscription-cancelled.js');
|
|
const cancelledHtml = subscriptionCancelledEmail({
|
|
nombre: 'Ivan Alcaraz',
|
|
plan: 'Enterprise',
|
|
});
|
|
await transport.sendMail({
|
|
from: env.SMTP_FROM,
|
|
to,
|
|
subject: 'Suscripción cancelada - Horux360 (MUESTRA)',
|
|
html: cancelledHtml,
|
|
});
|
|
|
|
console.log(`Listo: 6 correos enviados a ${to}`);
|
|
}
|
|
|
|
console.log('\n=== Todos los correos enviados ===');
|
|
process.exit(0);
|
|
}
|
|
|
|
sendAllSamples().catch((err) => {
|
|
console.error('Error:', err);
|
|
process.exit(1);
|
|
});
|