Files
mercadodevida/work/artifacts/F-152/security.json
2026-08-22 07:09:03 +02:00

30 lines
2.9 KiB
JSON

{
"feature_id": "F-152",
"agent": "security",
"stage": "security_gate",
"verdict": "APPROVED",
"summary": "F-152 no amplía superficie de autorización ni expone nuevos endpoints públicos. No se hardcodean credenciales SMTP (se leen de store_settings vía pool); el email de confirmación reusa el patrón existente de order-status-mailer (read-only pool lookup de identity_users.email). Los emails son best-effort y no bloquean el registro/webhook. Personalización HTML se escapa (XSS-safe). El gate outcome.kind==='processed' previene envíos duplicados ante replay de webhooks.",
"evidence": [
"No se añaden endpoints públicos nuevos: POST /auth/register y POST /payments/webhook ya existían",
"No se modifica RBAC ni requireRole en ningún route; register sigue siendo público (comportamiento previsto)",
"SMTP credentials no aparecen en código: SettingsWelcomeMailer.readSmtpOptions lee store_settings (smtp_host/port/secure/user/pass/from) vía pg pool",
"Order confirmation email lookup es read-only pool (orders_orders.user_id -> identity_users.email), mismo patrón que sendOrderStatusEmail/order-status-mailer",
"Best-effort: welcome .catch(request.log.warn); order confirmation en try/catch con warn -> no excepción al cliente",
"Idempotencia webhook: outcome.kind==='processed' garantiza un solo email por PaymentSucceeded (PaymentsService devuelve kind:'duplicate' antes de transición)",
"XSS-safe: buildWelcomeEmail HTML-escapea name (test unitario)",
"No new data at rest; no new tokens/secrets"
],
"checks": [
{"item": "Autorización / RBAC intacta", "ok": true, "evidence": "No se modifican requireRole ni middlewares de auth; register sigue público, webhook sigue sin auth (preexistente)"},
{"item": "No credenciales SMTP en código", "ok": true, "evidence": "SettingsWelcomeMailer readSmtpOptions(pool) consulta store_settings; prettier/eslint no detectan secrets; git diff --check limpio"},
{"item": "Lookup de email cliente read-only", "ok": true, "evidence": "payments.routes usa pool.query SELECT identity_users.email FROM orders_orders o JOIN identity_users; no writes"},
{"item": "Best-effort no expone errores internos", "ok": true, "evidence": "welcome .catch(warn); order-conf try/catch warn; registro/webhook nunca propagan error SMTP al cliente"},
{"item": "Idempotencia frente a replay", "ok": true, "evidence": "gate outcome.kind==='processed' + PaymentsService kind:'duplicate'"},
{"item": "XSS en personalización", "ok": true, "evidence": "buildWelcomeEmail escapea name (test)"},
{"item": "Data at rest / secrets", "ok": true, "evidence": "No se crean tablas ni columnas nuevas; no se añaden env vars/secretos nuevos"}
],
"issues": [],
"notes": "Hardening futuro opcional: validar/descartar emails con header CRLF injection en readSmtpOptions antes de pasar a nodemailer; fuera del scope de F-152.",
"timestamp": "2026-08-22T05:05:10Z"
}