services/api/src/scripts/lib/seedTargetGuard.js

Overview

Este módulo protege el arranque de seed.js con una validación estricta del destino MongoDB. Su propósito es permitir solo bases locales de desarrollo y rechazar cualquier URI remota, ambigua o con señales de staging o producción antes de conectar. La lógica es pura y no abre conexiones ni expone credenciales.

En el flujo del seed, esta guardia evita escribir usuarios de prueba, planes y anuncios sobre Atlas, staging o destinos mal interpretados. La documentación del seed confirma que evaluateSeedTarget se ejecuta antes de conectar y que el script falla cerrado cuando el destino no es local.

[!IMPORTANT] El criterio principal no es NODE_ENV. El módulo decide primero por el host y la base de datos de la URI, y usa NODE_ENV y APP_ENV como capa adicional.

How it works

flowchart TD
    U[MongoDB URI] --> D[describeMongoTarget]
    D --> T[target summary]
    T --> E[evaluateSeedTarget]
    E --> L[LOCAL_HOSTS]
    E --> B[FORBIDDEN_DATABASE_RE]
    E --> N[FORBIDDEN_ENVIRONMENTS]
    L --> R[reasons]
    B --> R
    N --> R
    R --> A[allowed result]

Primero, describeMongoTarget extrae esquema, hosts y base desde la URI. Luego, evaluateSeedTarget revisa si el destino usa mongodb+srv, si contiene hosts no locales, si la base falta, si la base parece de staging o producción, y si NODE_ENV o APP_ENV indican un entorno bloqueado. El resultado final resume la decisión con allowed, reasons y un target sanitizado.

Key components

Funciones exportadas

{
    "title": "describeMongoTarget",
    "content": "Analiza una URI MongoDB y devuelve un resumen sin credenciales: esquema, hosts sin puerto y nombre de base. Devuelve null si la URI no se puede interpretar.",
    "type": "function",
    "filePath": "services/api/src/scripts/lib/seedTargetGuard.js",
    "badges": [
        "public"
    ]
}
{
    "title": "evaluateSeedTarget",
    "content": "Eval\u00faa si un destino MongoDB es aceptable para el seed local. Devuelve una decisi\u00f3n booleana, las razones de rechazo y un resumen sanitizado del destino.",
    "type": "function",
    "filePath": "services/api/src/scripts/lib/seedTargetGuard.js",
    "badges": [
        "public"
    ]
}

Elementos internos

  • LOCAL_HOSTS — Set con localhost, 127.0.0.1 y ::1. Solo esos hosts se aceptan como locales.
  • FORBIDDEN_DATABASE_RE — expresión regular que detecta nombres de base con señales de staging, stage, stg, prod, production o live.
  • FORBIDDEN_ENVIRONMENTS — Set con production y staging. Si cualquiera aparece en NODE_ENV o APP_ENV, la evaluación falla.
  • stripPort(host) — helper que elimina el puerto del host y maneja hosts IPv6 entre corchetes.

Data & fields

describeMongoTarget(uri) devuelve un resumen sanitizado

[
    {
        "name": "scheme",
        "type": "string",
        "required": true,
        "description": "Esquema detectado. El módulo acepta mongodb y mongodb+srv."
    },
    {
        "name": "hosts",
        "type": "string[]",
        "required": true,
        "description": "Lista de hosts en minúsculas y sin puerto."
    },
    {
        "name": "database",
        "type": "string",
        "required": true,
        "description": "Nombre de la base extraído de la ruta de la URI y decodificado."
    }
]

El resumen nunca incluye usuario, contraseña ni parámetros de query. Los tests verifican que una URI con credenciales y authSource sigue devolviendo solo scheme, hosts y database.

evaluateSeedTarget({ uri, nodeEnv, appEnv }) usa estas entradas

[
    {
        "name": "uri",
        "type": "string",
        "required": true,
        "description": "URI MongoDB que se quiere evaluar."
    },
    {
        "name": "nodeEnv",
        "type": "string",
        "description": "Valor de NODE_ENV usado como capa adicional de bloqueo."
    },
    {
        "name": "appEnv",
        "type": "string",
        "description": "Valor de APP_ENV usado como capa adicional de bloqueo."
    }
]

evaluateSeedTarget(...) devuelve este objeto

[
    {
        "name": "allowed",
        "type": "boolean",
        "required": true,
        "description": "Indica si la URI pasa todas las reglas."
    },
    {
        "name": "reasons",
        "type": "string[]",
        "required": true,
        "description": "Lista de códigos de rechazo. Cada razón identifica una causa concreta."
    },
    {
        "name": "target",
        "type": "object | null",
        "required": true,
        "description": "Resumen sanitizado del destino, o null si la URI no se puede interpretar.",
        "children": [
            {
                "name": "hosts",
                "type": "string[]",
                "required": true,
                "description": "Hosts locales o remotos detectados en la URI, ya normalizados."
            },
            {
                "name": "database",
                "type": "string",
                "required": true,
                "description": "Nombre de la base detectado en la ruta."
            }
        ]
    }
]

Códigos de razón que emite el módulo

  • URI_UNPARSEABLE — la URI no se pudo interpretar como MongoDB.
  • SRV_NOT_ALLOWED — mongodb+srv no se permite.
  • REMOTE_HOST — existe al menos un host fuera de LOCAL_HOSTS.
  • DATABASE_MISSING — la URI no declara una base explícita.
  • DATABASE_FORBIDDEN — el nombre de base parece de staging o producción.
  • ENVIRONMENT_FORBIDDEN — NODE_ENV o APP_ENV apuntan a staging o producción.

Behaviors

  • describeMongoTarget(uri) — analiza una URI MongoDB, normaliza hosts, decodifica la base y devuelve null si no hay coincidencia válida.
  • evaluateSeedTarget({ uri, nodeEnv, appEnv }) — ejecuta la política de bloqueo y devuelve el veredicto final con razones y resumen del destino.
  • stripPort(host) — elimina el puerto de un host normal y maneja direcciones IPv6 con corchetes.
  • LOCAL_HOSTS — define los únicos hosts aceptados como locales.
  • FORBIDDEN_DATABASE_RE — detecta bases con nombres asociados a entornos no locales.
  • FORBIDDEN_ENVIRONMENTS — bloquea entornos declarados como production o staging.

Gotchas & edge cases

[!WARNING] Si la URI usa mongodb+srv, el módulo la rechaza aunque apunte a un nombre de base local. La razón es que ese esquema siempre apunta a un cluster remoto. fileciteturn0file2turn0file11

[!WARNING] Un host mixto también falla. Si la URI combina un host local con uno remoto, REMOTE_HOST entra en reasons.

[!IMPORTANT] Una URI sin base explícita no pasa. mongodb://localhost:27017 se considera inválida por DATABASE_MISSING.

[!NOTE] El módulo falla cerrado ante entradas vacías, texto que no parece URI y esquemas distintos de MongoDB. Los tests cubren esos casos ambiguos o ilegibles.

[!NOTE] describeMongoTarget no conserva credenciales ni query params. La salida está pensada para mensajes de error seguros y para evaluación interna. fileciteturn0file2turn0file7

[!CAUTION] Un NODE_ENV=development no basta para permitir el destino. Si la URI apunta a remoto, evaluateSeedTarget sigue rechazando.

services/api/src/scripts/seed.js usa evaluateSeedTarget como puerta de entrada antes de conectar a MongoDB. La documentación del seed indica que el proceso sale con código 1 si el destino no pasa esta validación.

services/api/src/scripts/lib/localMongoTarget.js reutiliza describeMongoTarget y evaluateSeedTarget para una política más estricta en herramientas administrativas. Allí se añaden reglas sobre opciones de URI y validación posterior de hello.

services/api/test/seed-target-guard.test.js confirma el comportamiento esperado. Cubre destinos locales válidos, rechazo de mongodb+srv, bloqueo por nombres de base, bloqueo por entorno y protección contra filtrado de credenciales. fileciteturn0file4turn0file7turn0file11

[!TIP] Este módulo funciona como una barrera de seguridad previa a cualquier escritura del seed. Su valor está en decidir antes de conectar, no después.