Skip to content

[SEC-A03] npm audit en CI (backend) - #189

Merged
CrisNAC merged 7 commits into
devfrom
OM-563
Jun 4, 2026
Merged

CrisNAC merged 7 commits into
devfrom
OM-563

Conversation

@DavidVillarM

@DavidVillarM DavidVillarM commented Jun 2, 2026 •

Copy link
Copy Markdown
Collaborator

Objetivo

Implementar control de seguridad en CI para que el pipeline falle cuando se detecten vulnerabilidades high o critical.

Cambios

  • Se agrega job security-audit al workflow de backend.
  • Se ejecuta audit-ci con política desde security/audit-ci.json.
  • Se documenta el manejo de excepciones en security/AUDIT_EXCEPTIONS.md.
  • Se actualiza package-lock.json para reducir hallazgos de seguridad.

Política de excepciones

  • Estado actual: sin excepciones aprobadas (allowlist vacío).
  • Cualquier excepción futura debe:
    1. agregarse en security/audit-ci.json,
    2. documentarse en security/AUDIT_EXCEPTIONS.md con justificación, responsable y vencimiento.

Criterio de aceptación

  • CI falla en vulnerabilidades critical/high sin excepción documentada.
  • Job de seguridad activo en el pipeline de backend.

Validación

  • npx --yes audit-ci@7 --config security/audit-ci.json

Summary by CodeRabbit

Notas de Lanzamiento

  • Seguridad

    • Se implementó auditoría automática de vulnerabilidades en el pipeline de integración continua.
    • Se agregó registro detallado de eventos de seguridad con sanitización de datos sensibles (contraseñas, tokens, etc.).
    • Se registran eventos de auditoría en cambios de estado de asignaciones de entregas y órdenes.
    • Se agregó monitoreo de intentos de acceso no autorizado (403).
  • Tests

    • Se añadieron pruebas unitarias completas para validar la protección de datos sensibles en logs.

Registra login fallido, respuestas 403 y cambios de estado en pedidos y asignaciones, sanitizando tokens y contraseñas en los logs.
Normaliza claves sensibles camelCase, protege metadatos reservados, evita log injection, audita asignaciones fuera de la transacción y evita que fallos de logging afecten respuestas.
Usa replaceAll, omite claves reservadas sin variables sin usar, valida la línea JSON y escribe por stdout para mitigar log injection.
Add a dedicated security-audit job that fails on high/critical vulnerabilities and document how to manage approved exceptions.
@coderabbitai

coderabbitai Bot commented Jun 2, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@DavidVillarM, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 33 minutes and 25 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4ca4c37a-0c39-42a3-8e09-f6b343e60283

📥 Commits

Reviewing files that changed from the base of the PR and between e778b64 and c9440d2.

📒 Files selected for processing (20)
  • .github/workflows/vitest-supertest.yml
  • src/app.js
  • src/lib/security-logger.js
  • src/middlewares/validate.middleware.js
  • src/modules/delivery/delivery-assignments/delivery-assignments.service.js
  • src/modules/session/controllers/session.controllers.js
  • tests/e2e/assignments-complete.test.js
  • tests/e2e/delivery-profile.test.js
  • tests/e2e/delivery-register.test.js
  • tests/e2e/delivery-status.test.js
  • tests/e2e/delivery.test.js
  • tests/e2e/notifications.test.js
  • tests/e2e/orders-delivery-reviews.test.js
  • tests/e2e/product/products.test.js
  • tests/e2e/stock.test.js
  • tests/e2e/stores.test.js
  • tests/e2e/users.test.js
  • tests/unit/delivery/delivery-complete.test.js
  • tests/unit/delivery/delivery-profile.test.js
  • tests/unit/dtos/misc.dto.test.js
📝 Walkthrough

Walkthrough

Se implementa un sistema integral de auditoría de seguridad que sanitiza automáticamente eventos sensibles, captura fallos de autenticación y cambios de estado en órdenes/asignaciones, y valida dependencias de seguridad en CI con soporte para excepciones documentadas.

Changes

Sistema Integral de Auditoría de Seguridad

Layer / File(s) Summary
Infraestructura de Auditoría en CI y Configuración
.github/workflows/vitest-supertest.yml, security/audit-ci.json, security/AUDIT_EXCEPTIONS.md
Se agrega un job security-audit en CI que ejecuta audit-ci@7 configurado en audit-ci.json para fallar en vulnerabilidades high/critical, con documentación de política de excepciones en AUDIT_EXCEPTIONS.md.
Librería de Sanitización y Logging de Seguridad
src/lib/security-logger.js, tests/unit/lib/security-logger.test.js
Se implementa normalizeKey, sanitizeForLog, buildSecurityLogLine y logSecurityEvent que sanitizan recursivamente eventos redaccionando tokens/passwords/JWT, validan tamaño/control-chars/JSON, y escriben a stdout con prefijo [SECURITY]; incluye cobertura unitaria completa.
Middleware de Logging y Respuestas 403
src/middlewares/security-log.middleware.js, src/app.js, tests/unit/middlewares/security-log.middleware.test.js
Se crea securityResponseLogger que captura respuestas HTTP 403 y emite eventos FORBIDDEN con método, ruta y datos de usuario; se integra en el pipeline de Express tras cookieParser() con tests de validación de invocación.
Auditoría de Fallos de Autenticación en Login
src/modules/session/controllers/session.controllers.js
Se instrumenta login para registrar eventos LOGIN_FAILED con razones: credenciales faltantes, usuario inactivo, o contraseña inválida; se reordena import de verifyPassword desde utilidades.
Auditoría de Cambios de Estado en Asignaciones
src/modules/delivery/delivery-assignments/delivery-assignments.service.js
Se agrega auditoría en createAssignmentService, completeAssignmentService y respondToAssignmentService registrando eventos ASSIGNMENT_STATUS_CHANGED en creación (null→PENDING), completitud (ACCEPTED→DELIVERED) y respuesta (aceptación/rechazo con metadata de reasignación); en respuesta se diferida la ejecución de logs hasta tras la transacción.
Auditoría de Cambios de Estado en Órdenes
src/modules/users/orders/order.service.js
Se agrega auditoría en updateOrderStatusService registrando eventos ORDER_STATUS_CHANGED con ID de orden, estados anterior/nuevo y metadatos del actor (ID y rol).

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant AuthCtrl as session.controllers
    participant Logger as logSecurityEvent
    participant Sanitize as sanitizeForLog
    participant Stdout as [SECURITY] log

    Client->>AuthCtrl: login(email, password)
    
    alt missing credentials
        AuthCtrl->>Logger: LOGIN_FAILED<br/>reason: missing_credentials
    else invalid user
        AuthCtrl->>Logger: LOGIN_FAILED<br/>reason: invalid_credentials
    else password mismatch
        AuthCtrl->>Logger: LOGIN_FAILED<br/>reason: invalid_credentials
    end

    Logger->>Sanitize: sanitizeForLog(details)
    Sanitize->>Sanitize: redact password/token fields
    Sanitize->>Sanitize: remove control chars
    Sanitize-->>Logger: sanitized
    Logger->>Stdout: [SECURITY] {event, details, timestamp}
    Logger-->>AuthCtrl: logged
    AuthCtrl-->>Client: UnauthorizedError
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • CrisNAC/BackendMarketplace#185: Ambos PRs modifican src/modules/session/controllers/session.controllers.js en el flujo de login para instrumentar comportamiento alrededor de fallos de verificación de contraseña.
  • CrisNAC/BackendMarketplace#128: Ambos PRs tocan la lógica de src/modules/delivery/delivery-assignments/delivery-assignments.service.js en cambios de estado de asignaciones, con este PR agregando llamadas a logSecurityEvent.
  • CrisNAC/BackendMarketplace#68: Ambos PRs modifican src/modules/users/orders/order.service.js en updateOrderStatusService para agregar auditoría de cambios de estado.

Suggested reviewers

  • CrisNAC

Poem

🐰 ¡Un conejo auditor, vigilante y certero,
Redacta secretos con celo verdadero!
Tokens y contraseñas, sin dejar ni un rastro,
Cada evento registra cual mago de astro. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed El título es específico y refleja el cambio principal: agregar auditoría de seguridad npm en CI para el backend, siguiendo la convención de nomenclatura del proyecto [SEC-A03].
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch OM-563

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/modules/delivery/delivery-assignments/delivery-assignments.service.js (1)

290-305: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Falta el evento ORDER_STATUS_CHANGED al completar la entrega.

Dentro de la transacción la orden pasa a DELIVERED (líneas 290-293), pero solo se registra ASSIGNMENT_STATUS_CHANGED. Esto es inconsistente con la rama ACCEPT de respondToAssignmentService, donde sí se emiten ambos eventos (asignación y orden). Para mantener la cobertura de auditoría uniforme, conviene registrar también el cambio de estado de la orden.

♻️ Posible ajuste para auditar el cambio de orden
   logSecurityEvent("ASSIGNMENT_STATUS_CHANGED", {
     assignmentId: id_delivery_assignment,
     orderId: assignment.fk_order,
     deliveryId: assignment.fk_delivery,
     previousStatus: "ACCEPTED",
     newStatus: "DELIVERED",
     actorUserId: id_user,
   });
+
+  logSecurityEvent("ORDER_STATUS_CHANGED", {
+    orderId: assignment.fk_order,
+    newStatus: "DELIVERED",
+    actorUserId: id_user,
+    actorRole: "DELIVERY",
+    source: "assignment_complete",
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/delivery/delivery-assignments/delivery-assignments.service.js`
around lines 290 - 305, La transacción actual actualiza la orden a DELIVERED
pero no emite el evento ORDER_STATUS_CHANGED; antes de llamar a
tx.orders.update() lee el estado actual de la orden (por ejemplo con
tx.orders.findUnique({ where: { id_order: assignment.fk_order } })) para
capturar previousStatus, luego después de la transacción invoca
logSecurityEvent("ORDER_STATUS_CHANGED", { orderId: assignment.fk_order,
previousStatus: <valor_obtenido>, newStatus: "DELIVERED", actorUserId: id_user
}) — mantén además el evento ASSIGNMENT_STATUS_CHANGED existente
(identificadores relevantes: tx.orders.update, assignment.fk_order,
logSecurityEvent, id_delivery_assignment, id_user).
src/modules/users/orders/order.service.js (1)

16-17: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Arreglar los mocks de Prisma: agregá orders.count en e2e (para generateOrderNumber)

El CI falla porque generateOrderNumber() llama prismaOrTx.orders.count(...) (líneas ~16-17) y en los mocks de Prisma usados por los tests e2e prisma.orders no define count, causando TypeError: prismaOrTx.orders.count is not a function propagado por mapOrderResponse y createOrderService. En los unit tests (tests/unit/order/order.test.js) orders.count sí existe; el faltante está en e2e (por ejemplo tests/e2e/stock.test.js y otros como tests/e2e/orders-available-deliveries.test.js, tests/e2e/notifications.test.js, tests/e2e/delivery-assignments-*.test.js, etc.).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/users/orders/order.service.js` around lines 16 - 17, La falla
viene de que generateOrderNumber usa prismaOrTx.orders.count(...) pero los mocks
de Prisma en los tests e2e no exponen orders.count; actualizar los mocks e2e
para definir orders.count (método que acepta el mismo filtro que en producción)
y devolver un número apropiado (p. ej. 0 o el valor esperado por los tests).
Localizá los mocks compartidos usados por los e2e (donde se crea el objeto
prisma/mockPrisma) y añadí una implementación para orders.count; esto garantiza
que generateOrderNumber, mapOrderResponse y createOrderService no lancen
TypeError durante los tests.
🧹 Nitpick comments (2)
.github/workflows/vitest-supertest.yml (1)

8-21: ⚡ Quick win

Considerar hardening de seguridad del job según análisis estático.

El análisis estático (zizmor) identificó varias oportunidades de hardening de seguridad en el job security-audit:

  1. Acciones sin pinning a commit hash: Las acciones actions/checkout@v4 y actions/setup-node@v4 usan tags en lugar de hashes de commit específicos, lo que podría permitir ataques de supply chain si el tag se modifica.

  2. Permisos por defecto amplios: El job no especifica un bloque permissions:, heredando permisos por defecto que podrían ser más amplios de lo necesario.

  3. Credenciales persistentes: No se establece persist-credentials: false en el checkout, lo que podría exponer credenciales en artifacts.

🔒 Hardening propuesto
  security-audit:
    name: Security audit (high/critical)
    timeout-minutes: 10
    runs-on: ubuntu-latest
+   permissions:
+     contents: read
    steps:
      - uses: actions/checkout@v4
+       with:
+         persist-credentials: false
      - uses: actions/setup-node@v4
        with:
          node-version: lts/*
          cache: 'npm'

Para pinning a commit hash (opcional según política organizacional):

-     - uses: actions/checkout@v4
+     - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
-     - uses: actions/setup-node@v4
+     - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/vitest-supertest.yml around lines 8 - 21, The
security-audit job should be hardened: replace tag-based action references
(actions/checkout@v4, actions/setup-node@v4) with pinned commit SHAs or
organization-approved digests; add a minimal permissions: block for the job to
grant only the least privileges required (e.g., restrict to read-only where
possible) instead of inheriting defaults; and set persist-credentials: false on
the actions/checkout step to avoid leaking runner credentials to subsequent
steps or artifacts; update the job named security-audit and the
checkout/setup-node steps accordingly.
src/modules/delivery/delivery-assignments/delivery-assignments.service.js (1)

94-100: 💤 Low value

Transiciones intermedias dentro de la transacción no quedan auditadas.

El evento nuevo cubre la creación de la asignación pendiente, pero dentro de la transacción también se cambian estados que no se registran: el pendingAssignment pasado a REJECTED (líneas 44-47) y el cierre/reasignación vía closePendingAndReassign(..., "EXPIRED") (línea 51). Si el objetivo de la auditoría es trazar todas las transiciones de estado, considerá emitir eventos también para esos casos.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/delivery/delivery-assignments/delivery-assignments.service.js`
around lines 94 - 100, La creación actual solo emite
logSecurityEvent("ASSIGNMENT_STATUS_CHANGED") para newDeliveryAssignment; además
debe emitirse un evento cuando el pendingAssignment se marca como REJECTED y
cuando se invoca closePendingAndReassign(..., "EXPIRED"). Añadí llamadas a
logSecurityEvent("ASSIGNMENT_STATUS_CHANGED") justo después de la actualización
que pone pendingAssignment a "REJECTED" (usando
pendingAssignment.id_delivery_assignment / fk_order / fk_delivery y
previousStatus como su estado previo), y otra llamada dentro o inmediatamente
después de closePendingAndReassign(...) para registrar la transición a "EXPIRED"
(usando el id retornado o el assignment afectado y newStatus "EXPIRED"),
asegurando consistencia en los campos previousStatus/newStatus y que los eventos
se emitan dentro de la misma transacción.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/security-logger.js`:
- Around line 20-42: Reemplaza el uso de String.prototype.replaceAll por
alternativas compatibles con Node versiones LTS en normalizeKey y
sanitizeLogString: en normalizeKey intercambia replaceAll(/([a-z0-9])([A-Z])/g,
...) por String.prototype.replace con la misma expresión global, y cambia
replaceAll("-", "_") por split("-").join("_") (o replace con /-/g); en
sanitizeLogString usa replace con patrones globales (o split/join) en lugar de
replaceAll para CONTROL_CHARS_PATTERN, JWT_PATTERN y BEARER_PATTERN; además, si
prefieres garantizar la API, añade engines.node: ">=15" en package.json como
alternativa documentada. Asegúrate de que SENSITIVE_KEYS, isSensitiveKey y el
flujo de normalización sigan usando normalizeKey tras los cambios.

---

Outside diff comments:
In `@src/modules/delivery/delivery-assignments/delivery-assignments.service.js`:
- Around line 290-305: La transacción actual actualiza la orden a DELIVERED pero
no emite el evento ORDER_STATUS_CHANGED; antes de llamar a tx.orders.update()
lee el estado actual de la orden (por ejemplo con tx.orders.findUnique({ where:
{ id_order: assignment.fk_order } })) para capturar previousStatus, luego
después de la transacción invoca logSecurityEvent("ORDER_STATUS_CHANGED", {
orderId: assignment.fk_order, previousStatus: <valor_obtenido>, newStatus:
"DELIVERED", actorUserId: id_user }) — mantén además el evento
ASSIGNMENT_STATUS_CHANGED existente (identificadores relevantes:
tx.orders.update, assignment.fk_order, logSecurityEvent, id_delivery_assignment,
id_user).

In `@src/modules/users/orders/order.service.js`:
- Around line 16-17: La falla viene de que generateOrderNumber usa
prismaOrTx.orders.count(...) pero los mocks de Prisma en los tests e2e no
exponen orders.count; actualizar los mocks e2e para definir orders.count (método
que acepta el mismo filtro que en producción) y devolver un número apropiado (p.
ej. 0 o el valor esperado por los tests). Localizá los mocks compartidos usados
por los e2e (donde se crea el objeto prisma/mockPrisma) y añadí una
implementación para orders.count; esto garantiza que generateOrderNumber,
mapOrderResponse y createOrderService no lancen TypeError durante los tests.

---

Nitpick comments:
In @.github/workflows/vitest-supertest.yml:
- Around line 8-21: The security-audit job should be hardened: replace tag-based
action references (actions/checkout@v4, actions/setup-node@v4) with pinned
commit SHAs or organization-approved digests; add a minimal permissions: block
for the job to grant only the least privileges required (e.g., restrict to
read-only where possible) instead of inheriting defaults; and set
persist-credentials: false on the actions/checkout step to avoid leaking runner
credentials to subsequent steps or artifacts; update the job named
security-audit and the checkout/setup-node steps accordingly.

In `@src/modules/delivery/delivery-assignments/delivery-assignments.service.js`:
- Around line 94-100: La creación actual solo emite
logSecurityEvent("ASSIGNMENT_STATUS_CHANGED") para newDeliveryAssignment; además
debe emitirse un evento cuando el pendingAssignment se marca como REJECTED y
cuando se invoca closePendingAndReassign(..., "EXPIRED"). Añadí llamadas a
logSecurityEvent("ASSIGNMENT_STATUS_CHANGED") justo después de la actualización
que pone pendingAssignment a "REJECTED" (usando
pendingAssignment.id_delivery_assignment / fk_order / fk_delivery y
previousStatus como su estado previo), y otra llamada dentro o inmediatamente
después de closePendingAndReassign(...) para registrar la transición a "EXPIRED"
(usando el id retornado o el assignment afectado y newStatus "EXPIRED"),
asegurando consistencia en los campos previousStatus/newStatus y que los eventos
se emitan dentro de la misma transacción.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 420207fd-80e2-4ba1-8316-544b11e5fabd

📥 Commits

Reviewing files that changed from the base of the PR and between fbc7b7d and e778b64.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (11)
  • .github/workflows/vitest-supertest.yml
  • security/AUDIT_EXCEPTIONS.md
  • security/audit-ci.json
  • src/app.js
  • src/lib/security-logger.js
  • src/middlewares/security-log.middleware.js
  • src/modules/delivery/delivery-assignments/delivery-assignments.service.js
  • src/modules/session/controllers/session.controllers.js
  • src/modules/users/orders/order.service.js
  • tests/unit/lib/security-logger.test.js
  • tests/unit/middlewares/security-log.middleware.test.js

Comment thread src/lib/security-logger.js
Address valid review findings with minimal changes: replace replaceAll for LTS compatibility, add missing order status audit logging in assignment completion flow, harden security-audit job permissions, and update e2e Prisma mocks for orders.count.
Evita sobrescribir req.params al validar varios DTOs y actualiza aserciones al nuevo formato de errores de validacion.
@sonarqubecloud

sonarqubecloud Bot commented Jun 4, 2026

Copy link
Copy Markdown

@CrisNAC
CrisNAC merged commit b4a10e7 into dev Jun 4, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants