# Auditoría de seguridad de Trigger.dev Background Jobs

Eres un Senior DevOps Security Auditor. Tu tarea es verificar si la configuración
de Trigger.dev se ha implementado correctamente según el runbook (artículo 4).

Ejecuta las siguientes comprobaciones en orden. Utiliza las herramientas disponibles.
Para cada comprobación: Reporta APROBADO, ADVERTENCIA o CRÍTICO con una breve justificación.
Al final, elabora un resumen con recomendaciones de actuación.

---

## A1: Trigger.dev como servicio independiente

Verifica si Trigger.dev se ejecuta separado de Next.js y Supabase.

```bash
# Trigger.dev Container/Prozesse
echo "=== Trigger.dev Container ==="
docker ps --format '{{.Names}}\t{{.Image}}\t{{.Status}}' 2>/dev/null | grep -i "trigger"

# Eigene Datenbank?
echo "=== Trigger.dev Datenbank ==="
ss -tlnp | grep 5433 2>/dev/null || echo "Port 5433 nicht aktiv"

# Supabase DB auf anderem Port?
echo "=== Supabase Datenbank ==="
ss -tlnp | grep 5432 2>/dev/null || echo "Port 5432 nicht aktiv"

# Trigger.dev docker-compose vorhanden?
echo "=== Trigger.dev Compose ==="
find /opt -name "docker-compose*" -path "*trigger*" 2>/dev/null | head -5

# Dashboard nur intern?
echo "=== Dashboard Port ==="
ss -tlnp | grep 3040 2>/dev/null
```

Expectativa:
- Contenedores de Trigger.dev en ejecución (Platform + DB)
- PostgreSQL propia en el puerto 5433 (no la BD de Supabase en el 5432)
- Dashboard (puerto 3040) solo en 127.0.0.1 (no 0.0.0.0)

Riesgo: Trigger.dev en la BD de Supabase = las consultas de la cola de trabajos compiten con las peticiones de usuario. Dashboard externo = acceso al historial de ejecuciones y logs.

---

## A2: Acceso a base de datos controlado

Verifica si los tasks restringen correctamente el acceso a la base de datos.

```bash
# Trigger Tasks Verzeichnis finden
TASKS_DIR=$(find . -path "*/trigger/tasks" -type d 2>/dev/null | grep -v node_modules | head -1)
TASKS_DIR=${TASKS_DIR:-"trigger/tasks"}

# Shared Supabase Client vorhanden?
echo "=== Shared Client ==="
find . -path "*/trigger/lib/*" -name "*.ts" 2>/dev/null | grep -v node_modules

# Wo wird service_role / DATABASE_URL verwendet?
echo "=== service_role in Tasks ==="
grep -rn "SERVICE_ROLE\|service_role\|createTaskClient\|createAdminClient" \
  ${TASKS_DIR}/ --include="*.ts" 2>/dev/null | head -10

# Direkte DB-Connection?
echo "=== Direkte DB-Connection ==="
grep -rn "DATABASE_URL\|postgres(\|pg\.connect\|new Pool" \
  trigger/ --include="*.ts" 2>/dev/null | grep -v node_modules | head -10

# Connection Pool Limits?
echo "=== Connection Pool Config ==="
grep -rn "max:\|pool\|idle_timeout\|connect_timeout" \
  trigger/ --include="*.ts" 2>/dev/null | grep -v node_modules | head -10

# SELECT * Patterns (zu breiter Zugriff)?
echo "=== SELECT * Patterns ==="
grep -rn "\.select('\\*')\|\.select(\`\\*\`)\|SELECT \\*" \
  ${TASKS_DIR}/ --include="*.ts" 2>/dev/null | head -10
```

Expectativa:
- Client Factory compartida en trigger/lib/ (no en cada task individualmente)
- service_role solo en la Client Factory, no directamente en los tasks
- Connection Pool con límite max (5-10)
- Sin SELECT * (solo los campos necesarios)

Riesgo: Los tasks eluden RLS. Sin limitación de alcance = acceso completo a la BD. Sin límite de pool = agotamiento de conexiones.

---

## B1: Definiciones de tasks correctas

Verifica si todos los tasks están correctamente definidos según la API v3.

```bash
TASKS_DIR=$(find . -path "*/trigger/tasks" -type d 2>/dev/null | grep -v node_modules | head -1)
TASKS_DIR=${TASKS_DIR:-"trigger/tasks"}

# Alle Task-Dateien
echo "=== Task-Dateien ==="
ls ${TASKS_DIR}/*.ts 2>/dev/null

# Exportierte Tasks?
echo "=== Nicht-exportierte Tasks ==="
for file in ${TASKS_DIR}/*.ts 2>/dev/null; do
  [ -f "$file" ] || continue
  name=$(basename "$file" .ts)
  if ! grep -q "export const" "$file"; then
    echo "CRÍTICO: $name ist nicht exportiert"
  fi
done

# Task IDs vorhanden und eindeutig?
echo "=== Task IDs ==="
grep -rn "id:" ${TASKS_DIR}/ --include="*.ts" 2>/dev/null | grep "task({" -A1 | grep "id:" | \
  awk -F'"' '{print $2}' | sort | uniq -c | sort -rn | head -10

# Werden Tasks aus Client-Code getriggert?
echo "=== Tasks aus Client Code ==="
grep -rn "tasks\.trigger\|\.trigger(" app/ --include="*.tsx" 2>/dev/null | \
  grep -v "use server" | grep -v node_modules | head -5
```

Expectativa:
- Todos los tasks exportados (export const)
- Todos los tasks con IDs únicos
- Los tasks se lanzan SOLO desde contexto de servidor (no desde .tsx Client Components)

Riesgo: Los tasks no exportados se ignoran silenciosamente durante el despliegue. Tasks desde código de cliente exponen la API de Trigger.

---

## B2: Idempotencia

Verifica si los tasks son idempotentes, especialmente en llamadas a APIs externas.

```bash
TASKS_DIR=$(find . -path "*/trigger/tasks" -type d 2>/dev/null | grep -v node_modules | head -1)
TASKS_DIR=${TASKS_DIR:-"trigger/tasks"}

# Tasks mit externen API-Calls
echo "=== Tasks mit externen Calls ==="
for file in ${TASKS_DIR}/*.ts 2>/dev/null; do
  [ -f "$file" ] || continue
  name=$(basename "$file" .ts)
  if grep -qE "fetch\(|stripe\.|resend\.|sendgrid\.|openai\.|anthropic\." "$file"; then
    HAS_IDEM=$(grep -cE "idempotencyKey|idempotencyKeys" "$file" || echo 0)
    if [ "$HAS_IDEM" -gt 0 ]; then
      echo "OK: $name hat externe Calls + Idempotency Keys"
    else
      echo "ADVERTENCIA: $name hat externe Calls OHNE Idempotency Keys"
    fi
  fi
done

# DB-Operationen: upsert statt insert?
echo "=== Insert vs Upsert ==="
grep -rn "\.insert(" ${TASKS_DIR}/ --include="*.ts" 2>/dev/null | head -5
grep -rn "\.upsert(" ${TASKS_DIR}/ --include="*.ts" 2>/dev/null | head -5

# Idempotency beim Triggern (aus Next.js)?
echo "=== Idempotency beim Triggern ==="
grep -rn "idempotencyKey" app/ --include="*.ts" 2>/dev/null | grep -v node_modules | head -10
```

Expectativa:
- Los tasks con llamadas a APIs externas (Stripe, correo electrónico, etc.) tienen idempotencyKeys
- Las operaciones de BD utilizan upsert donde sea posible (en lugar de insert)
- Las llamadas trigger en Next.js tienen idempotencyKey donde es posible el doble lanzamiento

Riesgo: Sin idempotencia en el reintento = pagos dobles, correos electrónicos dobles, entradas de BD duplicadas.

---

## B3: Timeouts y concurrencia

Verifica si todos los tasks tienen maxDuration y límites de concurrencia.

```bash
TASKS_DIR=$(find . -path "*/trigger/tasks" -type d 2>/dev/null | grep -v node_modules | head -1)
TASKS_DIR=${TASKS_DIR:-"trigger/tasks"}

# Tasks ohne maxDuration
echo "=== maxDuration ==="
for file in ${TASKS_DIR}/*.ts 2>/dev/null; do
  [ -f "$file" ] || continue
  name=$(basename "$file" .ts)
  if grep -q "maxDuration" "$file"; then
    DURATION=$(grep "maxDuration" "$file" | grep -oP '\d+' | head -1)
    echo "OK: $name (maxDuration: ${DURATION}s)"
  else
    echo "ADVERTENCIA: $name hat KEIN maxDuration"
  fi
done

# Tasks ohne Concurrency Limit
echo "=== Concurrency ==="
for file in ${TASKS_DIR}/*.ts 2>/dev/null; do
  [ -f "$file" ] || continue
  name=$(basename "$file" .ts)
  if grep -qE "concurrencyLimit|queue:" "$file"; then
    LIMIT=$(grep "concurrencyLimit" "$file" | grep -oP '\d+' | head -1)
    echo "OK: $name (concurrency: ${LIMIT:-shared queue})"
  else
    echo "ADVERTENCIA: $name hat KEIN Concurrency Limit"
  fi
done

# Shared Queues?
echo "=== Shared Queues ==="
grep -rn "queue({" trigger/ --include="*.ts" 2>/dev/null | grep -v node_modules | head -5
```

Expectativa:
- Todos los tasks tienen maxDuration (correo electrónico: 30s, PDF: 120s, IA: 300s)
- Todos los tasks tienen concurrencyLimit o utilizan una cola compartida
- Colas compartidas para tasks que utilizan el mismo servicio externo

Riesgo: Sin maxDuration = el task se bloquea indefinidamente en caso de timeout de API. Sin concurrencia = todos los slots de worker bloqueados.

---

## B4: Estrategia de reintentos

Verifica si los tasks tienen una configuración de reintentos coherente.

```bash
TASKS_DIR=$(find . -path "*/trigger/tasks" -type d 2>/dev/null | grep -v node_modules | head -1)
TASKS_DIR=${TASKS_DIR:-"trigger/tasks"}

echo "=== Retry Konfiguration ==="
for file in ${TASKS_DIR}/*.ts 2>/dev/null; do
  [ -f "$file" ] || continue
  name=$(basename "$file" .ts)
  if grep -q "retry:" "$file"; then
    ATTEMPTS=$(grep -A5 "retry:" "$file" | grep "maxAttempts" | grep -oP '\d+' | head -1)
    echo "OK: $name (maxAttempts: ${ATTEMPTS:-?})"
  else
    echo "ADVERTENCIA: $name hat KEINE Retry-Konfiguration (nutzt Default: kein Retry)"
  fi
done

# onFailure Hooks für kritische Tasks?
echo "=== onFailure Hooks ==="
for file in ${TASKS_DIR}/*.ts 2>/dev/null; do
  [ -f "$file" ] || continue
  name=$(basename "$file" .ts)
  if grep -q "onFailure" "$file"; then
    echo "OK: $name hat onFailure Hook"
  else
    echo "INFO: $name hat keinen onFailure Hook"
  fi
done
```

Expectativa:
- Todos los tasks tienen configuración de retry con maxAttempts y backoff exponencial
- Los tasks críticos (pagos, sincronización importante) tienen hooks onFailure

Riesgo: Sin reintentos = un error temporal de API provoca pérdida permanente de datos. Sin onFailure = los tasks fallidos pasan desapercibidos.

---

## B5: Gestión de secretos

Verifica si los secretos se gestionan correctamente.

```bash
# Hardcoded Secrets in Tasks?
echo "=== Hardcoded Secrets ==="
grep -rn "sk_live\|sk_test\|SG\.\|sk-proj\|sk-ant\|Bearer ey\|whsec_" \
  trigger/ --include="*.ts" 2>/dev/null | grep -v node_modules || \
  echo "Keine hardcoded Secrets gefunden (gut)"

# Secrets über process.env geladen?
echo "=== process.env Nutzung ==="
grep -rn "process\.env\." trigger/ --include="*.ts" 2>/dev/null | \
  grep -v node_modules | head -10

# .env.trigger nicht im Git?
echo "=== .env.trigger im Git? ==="
git ls-files .env.trigger 2>/dev/null || echo "Nicht im Git (gut)"

# .env.trigger Rechte
echo "=== .env.trigger Rechte ==="
ENV_TRIGGER=$(find . /opt -name ".env.trigger" 2>/dev/null | head -1)
if [ -n "$ENV_TRIGGER" ]; then
  stat -c "%a %U" "$ENV_TRIGGER"
fi
```

Expectativa:
- Sin claves API hardcodeadas en el código
- Secretos cargados mediante process.env
- .env.trigger no en Git, permisos 600

Riesgo: Los secretos en el código acaban en el repositorio Git y en las imágenes de worker.

---

## B6: Logging

Verifica si los tasks utilizan el logger de Trigger.dev y no registran datos sensibles.

```bash
TASKS_DIR=$(find . -path "*/trigger/tasks" -type d 2>/dev/null | grep -v node_modules | head -1)
TASKS_DIR=${TASKS_DIR:-"trigger/tasks"}

# console.log statt logger?
echo "=== console.log in Tasks ==="
grep -rn "console\.log\|console\.error\|console\.warn" \
  ${TASKS_DIR}/ --include="*.ts" 2>/dev/null || echo "Keine console.log gefunden (gut)"

# Trigger.dev logger importiert?
echo "=== logger Import ==="
grep -rn "from.*@trigger.dev.*logger\|import.*logger" \
  trigger/ --include="*.ts" 2>/dev/null | grep -v node_modules | head -5

# Sensible Daten in Logs?
echo "=== Sensible Daten in logger Calls ==="
grep -rn "logger\.\(info\|error\|warn\)" trigger/ --include="*.ts" 2>/dev/null | \
  grep -iE "password|secret|key|token|email.*:" | grep -v node_modules | head -5
```

Expectativa:
- Sin console.log en los tasks (usar siempre el logger de Trigger.dev)
- Sin datos sensibles (contraseñas, tokens, correos electrónicos de usuario) en las llamadas de log

Riesgo: Trigger.dev almacena todos los logs en el dashboard. Los datos sensibles son visibles allí para cualquier persona con acceso al dashboard, incluso meses después.

---

## C1: Gestión de Dead Letters

Verifica si los tasks fallidos se capturan adecuadamente.

```bash
# failed_jobs Tabelle vorhanden?
echo "=== failed_jobs Tabelle ==="
docker compose exec -T postgres psql -U postgres -c \
  "SELECT tablename FROM pg_tables WHERE tablename = 'failed_jobs';" 2>/dev/null || \
  echo "Konnte DB nicht prüfen"

# Unresolvierte Failed Jobs?
echo "=== Offene Failed Jobs ==="
docker compose exec -T postgres psql -U postgres -c \
  "SELECT count(*) as offen FROM failed_jobs WHERE resolved = false;" 2>/dev/null || \
  echo "Konnte nicht prüfen (Tabelle fehlt oder DB nicht erreichbar)"

# onFailure Hooks in kritischen Tasks?
TASKS_DIR=$(find . -path "*/trigger/tasks" -type d 2>/dev/null | grep -v node_modules | head -1)
TASKS_DIR=${TASKS_DIR:-"trigger/tasks"}

echo "=== Tasks OHNE onFailure ==="
for file in ${TASKS_DIR}/*.ts 2>/dev/null; do
  [ -f "$file" ] || continue
  name=$(basename "$file" .ts)
  if ! grep -q "onFailure" "$file"; then
    # Ist es ein kritischer Task?
    if grep -qE "payment\|invoice\|order\|billing\|charge\|subscription" "$file"; then
      echo "ADVERTENCIA: Kritischer Task $name hat keinen onFailure Hook"
    else
      echo "INFO: $name hat keinen onFailure Hook"
    fi
  fi
done
```

Expectativa:
- La tabla failed_jobs existe (o un sistema equivalente)
- Sin failed jobs antiguos sin resolver
- Los tasks críticos (pagos, pedidos) tienen hooks onFailure

Riesgo: Los tasks fallidos sin alertas pasan desapercibidos hasta que un cliente se queja.

---

## C2: Monitorización

Verifica si la monitorización de Trigger.dev está configurada.

```bash
# Dashboard erreichbar (intern)?
echo "=== Dashboard ==="
curl -s -o /dev/null -w "%{http_code}" http://localhost:3040 2>/dev/null || echo "Nicht erreichbar"

# Dashboard NICHT extern erreichbar?
echo "=== Dashboard extern ==="
EXTERNAL_HOST=$(hostname -I | awk '{print $1}')
curl -s -o /dev/null -w "%{http_code}" --connect-timeout 3 "http://${EXTERNAL_HOST}:3040" 2>/dev/null || echo "Nicht extern erreichbar (gut)"

# Health Check Script vorhanden?
echo "=== Health Check ==="
find /opt -name "*trigger*health*" -o -name "*check*trigger*" 2>/dev/null | head -5

# Cron Job für Monitoring?
echo "=== Cron ==="
crontab -l 2>/dev/null | grep -i "trigger"
```

Expectativa:
- Dashboard accesible internamente
- Dashboard NO accesible desde el exterior
- Health Check o monitorización configurados

---

## Resumen

Elabora ahora un resumen con el siguiente formato:

```
# Auditoría de seguridad Trigger.dev - [FECHA]

## Resultado

APROBADO:     X de Y comprobaciones
ADVERTENCIA:  X comprobaciones
CRÍTICO:      X comprobaciones

## Hallazgos críticos (actuar inmediatamente)
- ...

## Advertencias (resolver esta semana)
- ...

## Aprobados
- ...

## Próximos pasos recomendados
1. ...
2. ...
3. ...
```

Prioriza estrictamente: hallazgos críticos primero, luego advertencias.
Para cada hallazgo: cuál es el problema, por qué es un riesgo, cuál es la solución.
