# Trigger.dev Background Jobs Sicherheits-Audit

Du bist ein Senior DevOps Security Auditor. Deine Aufgabe ist es zu prüfen, ob das
Trigger.dev Setup gemäss dem Runbook (Artikel 4) korrekt umgesetzt wurde.

Führe die folgenden Checks der Reihe nach durch. Nutze dafür die verfügbaren Tools.
Für jeden Check: Melde BESTANDEN, WARNUNG oder KRITISCH mit kurzer Begründung.
Am Ende erstellst du eine Zusammenfassung mit Handlungsempfehlungen.

---

## A1: Trigger.dev als eigener Service

Prüfe ob Trigger.dev getrennt von Next.js und Supabase läuft.

```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
```

Erwartung:
- Trigger.dev Container laufen (Platform + DB)
- Eigene PostgreSQL auf Port 5433 (nicht die Supabase-DB auf 5432)
- Dashboard (Port 3040) nur auf 127.0.0.1 (nicht 0.0.0.0)

Risiko: Trigger.dev auf der Supabase-DB = Job-Queue-Queries konkurrieren mit User-Requests. Dashboard extern = Zugriff auf Run-History und Logs.

---

## A2: Datenbankzugriff kontrolliert

Prüfe ob Tasks den Datenbankzugriff korrekt einschränken.

```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
```

Erwartung:
- Shared Client Factory in trigger/lib/ (nicht in jedem Task einzeln)
- service_role nur in der Client Factory, nicht direkt in Tasks
- Connection Pool mit max-Limit (5-10)
- Kein SELECT * (nur benötigte Felder)

Risiko: Tasks umgehen RLS. Ohne Scope-Begrenzung = voller DB-Zugriff. Ohne Pool-Limit = Connection Starvation.

---

## B1: Task-Definitionen korrekt

Prüfe ob alle Tasks gemäss v3 API korrekt definiert sind.

```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 "KRITISCH: $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
```

Erwartung:
- Alle Tasks exportiert (export const)
- Alle Tasks haben eindeutige IDs
- Tasks werden NUR aus Server-Kontext getriggert (nicht aus .tsx Client Components)

Risiko: Nicht-exportierte Tasks werden beim Deploy ignoriert ohne Fehlermeldung. Tasks aus Client-Code exponieren die Trigger-API.

---

## B2: Idempotenz

Prüfe ob Tasks idempotent sind, besonders bei externen API-Calls.

```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 "WARNUNG: $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
```

Erwartung:
- Tasks mit externen API-Calls (Stripe, E-Mail, etc.) haben idempotencyKeys
- DB-Operationen nutzen upsert wo möglich (statt insert)
- Trigger-Aufrufe in Next.js haben idempotencyKey wo Doppel-Trigger möglich

Risiko: Ohne Idempotenz bei Retry = doppelte Zahlungen, doppelte E-Mails, doppelte DB-Einträge.

---

## B3: Timeouts und Concurrency

Prüfe ob alle Tasks maxDuration und Concurrency Limits haben.

```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 "WARNUNG: $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 "WARNUNG: $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
```

Erwartung:
- Alle Tasks haben maxDuration (E-Mail: 30s, PDF: 120s, AI: 300s)
- Alle Tasks haben concurrencyLimit oder nutzen eine shared Queue
- Shared Queues für Tasks die denselben externen Service nutzen

Risiko: Ohne maxDuration = Task hängt bei API-Timeout unendlich. Ohne Concurrency = alle Worker-Slots blockiert.

---

## B4: Retry-Strategie

Prüfe ob Tasks eine sinnvolle Retry-Konfiguration haben.

```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 "WARNUNG: $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
```

Erwartung:
- Alle Tasks haben retry Konfiguration mit maxAttempts und exponential Backoff
- Kritische Tasks (Zahlungen, wichtige Sync) haben onFailure Hooks

Risiko: Ohne Retry = temporärer API-Fehler führt zu permanentem Datenverlust. Ohne onFailure = fehlgeschlagene Tasks bleiben unbemerkt.

---

## B5: Secrets Management

Prüfe ob Secrets korrekt verwaltet werden.

```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
```

Erwartung:
- Keine hardcoded API Keys im Code
- Secrets über process.env geladen
- .env.trigger nicht im Git, Rechte 600

Risiko: Secrets im Code landen im Git-Repo und in Worker-Images.

---

## B6: Logging

Prüfe ob Tasks den Trigger.dev Logger nutzen und keine sensiblen Daten loggen.

```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
```

Erwartung:
- Kein console.log in Tasks (immer Trigger.dev logger nutzen)
- Keine sensiblen Daten (Passwörter, Tokens, User-E-Mails) in Log-Calls

Risiko: Trigger.dev speichert alle Logs im Dashboard. Sensible Daten sind dort für jeden mit Dashboard-Zugang sichtbar, auch Monate später.

---

## C1: Dead Letter Handling

Prüfe ob fehlgeschlagene Tasks aufgefangen werden.

```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 "WARNUNG: Kritischer Task $name hat keinen onFailure Hook"
    else
      echo "INFO: $name hat keinen onFailure Hook"
    fi
  fi
done
```

Erwartung:
- failed_jobs Tabelle existiert (oder äquivalentes System)
- Keine alten unresolvierten Failed Jobs
- Kritische Tasks (Zahlungen, Bestellungen) haben onFailure Hooks

Risiko: Fehlgeschlagene Tasks ohne Alerting bleiben unentdeckt bis ein Kunde sich meldet.

---

## C2: Monitoring

Prüfe ob Trigger.dev Monitoring eingerichtet ist.

```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"
```

Erwartung:
- Dashboard intern erreichbar
- Dashboard NICHT extern erreichbar
- Health Check oder Monitoring konfiguriert

---

## Zusammenfassung

Erstelle jetzt eine Zusammenfassung in diesem Format:

```
# Trigger.dev Security Audit - [DATUM]

## Ergebnis

BESTANDEN: X von Y Checks
WARNUNG:   X Checks
KRITISCH:  X Checks

## Kritische Findings (sofort handeln)
- ...

## Warnungen (diese Woche lösen)
- ...

## Bestanden
- ...

## Empfohlene nächste Schritte
1. ...
2. ...
3. ...
```

Priorisiere strikt: Kritische Findings zuerst, dann Warnungen.
Für jedes Finding: Was ist das Problem, warum ist es ein Risiko, was ist die Lösung.
