Skip to content

Q27 — PAN Masking Screenshots (Evidencia Auditable)

Fecha captura: 2026-08-01 Ambiente: Local docker-compose (postgres + kafka + kong + auth + payments-api + merchants + onboarding + frontend Vue en localhost:5173) Herramienta: Playwright 1.62.1 headless chromium (viewport 1440×900 @2x retina) Script: /tmp/pw-screenshot-pan-masking.mjsData: Seed sintético — 1 merchant demo + 3 customers + 3 payment methods (test PANs Visa 4111/MC 4444/Amex 1005) + 7 payment intents. Zero data real de producción.

PCI DSS Req cubiertos:

  • 3.4.1 — Enmascaramiento por defecto (BIN + last4 solo)
  • 3.4.2 — Restricción de acceso a PAN completo
  • 7.2 — Necesidad de conocer (least privilege)

Regla observada en TODAS las pantallas

El PAN completo NUNCA aparece en la UI merchant. Solo se muestra:

  • Visa ····4242 — brand + puntos medios + last 4 digits
  • •••• •••• •••• 1111 — formato completo enmascarado (16 chars)
  • Metadata (JSON): brand=visa, last4=4242, sin campo pan

Inventario de pantallas capturadas (10 archivos PNG)

01-login-page.png

Ruta: /auth/loginContenido: Formulario login. Sin data de tarjetas — screenshot de baseline.

02-dashboard-overview.png

Ruta: /dashboard/overviewContenido: Dashboard principal. Métricas agregadas (Total pagado, Suscripciones activas, Tasa éxito). Ningún PAN — solo agregados numéricos.

03-payments-list.png

Ruta: /dashboard/paymentsContenido: Lista de 7 transacciones seed. Columnas: ID (truncado 8 chars) · Comercio · Monto · Estado · Fecha · Acciones. El PAN NO aparece — el listado no incluye card display porque no es información relevante en modo lista.

04-payment-detail-masked.png ⭐

Ruta: /dashboard/payments/{id}Contenido: Detalle de una transacción. Muestra explícitamente:

  • CARD: Visa ····4242 (formato masking)
  • METADATA: brand=visa · last4=4242 · paymentMethod=visa_4242
  • Ningún campo pan completo en la pantalla

Esta es la evidencia más fuerte de Req 3.4.1 — la pantalla que más data expone del método de pago, y aun así el PAN nunca se completa.

05-payment-methods-list.png ⭐

Ruta: /dashboard/payment-methodsContenido: "Tarjetas guardadas" con header:

"Vault tokenizado — el PAN nunca toca tu servidor"

Footer badge: vault.card-vault-service · pci-dss scoped · token-only storage. Es la evidencia arquitectónica visible al merchant de la separación PCI (Q28 crosslink).

06-customers-list.png

Ruta: /dashboard/subscriptions/customersContenido: Lista de customers. Solo email/nombre, sin tarjetas.

07-customer-detail-payment-methods.png ⭐

Ruta: /dashboard/subscriptions/customers/{id}Contenido: Detalle del customer Juan Pérez. Panel derecho PAYMENT.METHODS — muestra "Sin métodos guardados" (placeholder correcto). Cuando hay métodos guardados, la UI renderiza PaymentMethodCard.vue con •••• •••• •••• <interpolación Vue: method.last4> (ver código fuente frontend/apps/fintrix-dashboard/src/modules/payment-methods/components/PaymentMethodCard.vue:85).

08-subscriptions-list.png

Ruta: /dashboard/subscriptions/listContenido: Suscripciones activas. Sin PAN visible.

09-transactions.png

Ruta: /dashboard/transactionsContenido: Historia transacciones (mismo endpoint que payments). Sin PAN visible.

10-refunds-report.png

Ruta: /dashboard/reports/refundsContenido: Reporte de refunds. Sin PAN visible.


Reproducibilidad

Para regenerar estos screenshots:

bash
# 1. Levantar stack backend
cd backend
docker compose up -d postgres zookeeper kafka minio auth-service payments-api merchants kong onboarding-service

# 2. Marcar onboarding approved para user demo
psql -h localhost -p 5432 -U fintrix -d onboarding_db <<SQL
INSERT INTO onboarding_core.onboarding_merchant_status (merchant_id, status)
VALUES ('<MERCHANT_ID>', 'approved') ON CONFLICT (merchant_id) DO UPDATE SET status='approved';
SQL

# 3. Levantar frontend
cd ../frontend/apps/fintrix-dashboard
npm run dev  # http://localhost:5173

# 4. Playwright
export PLAYWRIGHT_BROWSERS_PATH=/tmp/pw-browsers
npx playwright install chromium
node /tmp/pw-screenshot-pan-masking.mjs

Código relevante (masking implementations)

ComponentePathPatrón
PaymentMethodCard.vuefrontend/.../payment-methods/components/•••• •••• •••• <interpolación Vue: method.last4>
PaymentDetailPage.vuefrontend/.../payments/pages/${brand} ····${last4}
Backend DTObackend/apps/card-vault-service/src/cards/dto/Nunca retorna field pan, solo brand, last4, bin
DB schemabackend/db/merchants_db/.../customer_payment_methodsColumnas last4 (4 chars) + bin (6 chars). No columna pan.

Semgrep rules complementarias (SEC-023 activo en CI)

  • backend/tools/semgrep/rules/no-plain-pan-in-logs.yml
  • backend/tools/semgrep/rules/no-plain-pan-in-kafka.yml
  • backend/tools/semgrep/rules/no-plain-pan-in-response.yml

Cualquier PR que introduzca un campo pan en logs, eventos Kafka, o response DTOs falla el pipeline PCI DSS — Semgrep static analysis.


Firma auditoría

  • Capturado por: Gabriel Ureña Chacón (CTO)
  • Verificado por: (a firmar antes envío QSA)
  • Fecha: 2026-08-01

Documentación Confidencial — Solo para uso interno y auditoría PCI DSS