# Audyt bezpieczeństwa Trigger.dev Background Jobs

Jesteś Senior DevOps Security Auditorem. Twoim zadaniem jest sprawdzenie, czy konfiguracja
Trigger.dev została prawidłowo wdrożona zgodnie z runbookiem (artykuł 4).

Wykonaj poniższe kontrole po kolei. Korzystaj z dostępnych narzędzi.
Dla każdej kontroli: zgłoś ZALICZONY, OSTRZEŻENIE lub KRYTYCZNY z krótkim uzasadnieniem.
Na końcu sporządź podsumowanie z rekomendacjami działań.

---

## A1: Trigger.dev jako oddzielna usługa

Sprawdź, czy Trigger.dev działa oddzielnie od Next.js i 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
```

Oczekiwanie:
- Kontenery Trigger.dev działają (Platform + DB)
- Własny PostgreSQL na porcie 5433 (nie baza Supabase na 5432)
- Dashboard (port 3040) tylko na 127.0.0.1 (nie 0.0.0.0)

Ryzyko: Trigger.dev na bazie Supabase = zapytania kolejki zadań konkurują z requestami użytkowników. Dashboard zewnętrznie = dostęp do historii uruchomień i logów.

---

## A2: Kontrolowany dostęp do bazy danych

Sprawdź, czy taski prawidłowo ograniczają dostęp do bazy danych.

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

Oczekiwanie:
- Współdzielona fabryka klientów w trigger/lib/ (nie w każdym tasku osobno)
- service_role tylko w fabryce klientów, nie bezpośrednio w taskach
- Connection Pool z limitem max (5-10)
- Brak SELECT * (tylko potrzebne pola)

Ryzyko: Taski omijają RLS. Bez ograniczenia zakresu = pełny dostęp do bazy. Bez limitu puli = wyczerpanie połączeń (Connection Starvation).

---

## B1: Definicje tasków prawidłowe

Sprawdź, czy wszystkie taski są prawidłowo zdefiniowane zgodnie z 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 "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
```

Oczekiwanie:
- Wszystkie taski wyeksportowane (export const)
- Wszystkie taski mają unikalne identyfikatory
- Taski wyzwalane WYŁĄCZNIE z kontekstu serwera (nie z komponentów klienta .tsx)

Ryzyko: Niewyeksportowane taski są ignorowane podczas deploy bez komunikatu o błędzie. Taski z kodu klienta eksponują API Trigger.

---

## B2: Idempotencja

Sprawdź, czy taski są idempotentne, szczególnie przy zewnętrznych wywołaniach API.

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

Oczekiwanie:
- Taski z zewnętrznymi wywołaniami API (Stripe, e-mail, itd.) mają idempotencyKeys
- Operacje bazodanowe używają upsert tam, gdzie to możliwe (zamiast insert)
- Wywołania trigger w Next.js mają idempotencyKey tam, gdzie możliwe jest podwójne wyzwolenie

Ryzyko: Bez idempotencji przy retry = podwójne płatności, podwójne e-maile, podwójne wpisy w bazie.

---

## B3: Timeouty i współbieżność

Sprawdź, czy wszystkie taski mają maxDuration i limity współbieżności.

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

Oczekiwanie:
- Wszystkie taski mają maxDuration (e-mail: 30s, PDF: 120s, AI: 300s)
- Wszystkie taski mają concurrencyLimit lub korzystają ze współdzielonej kolejki
- Współdzielone kolejki dla tasków korzystających z tego samego serwisu zewnętrznego

Ryzyko: Bez maxDuration = task zawiesza się na nieskończoność przy timeoucie API. Bez współbieżności = wszystkie sloty workerów zablokowane.

---

## B4: Strategia ponownych prób

Sprawdź, czy taski mają sensowną konfigurację retry.

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

Oczekiwanie:
- Wszystkie taski mają konfigurację retry z maxAttempts i wykładniczym backoffem
- Krytyczne taski (płatności, ważna synchronizacja) mają hooki onFailure

Ryzyko: Bez retry = tymczasowy błąd API prowadzi do trwałej utraty danych. Bez onFailure = nieudane taski pozostają niezauważone.

---

## B5: Zarządzanie sekretami

Sprawdź, czy sekrety są prawidłowo zarządzane.

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

Oczekiwanie:
- Brak zakodowanych na stałe kluczy API w kodzie
- Sekrety ładowane przez process.env
- .env.trigger nie w Git, uprawnienia 600

Ryzyko: Sekrety w kodzie trafiają do repozytorium Git i obrazów workerów.

---

## B6: Logowanie

Sprawdź, czy taski korzystają z loggera Trigger.dev i nie logują danych wrażliwych.

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

Oczekiwanie:
- Brak console.log w taskach (zawsze korzystać z loggera Trigger.dev)
- Brak danych wrażliwych (hasła, tokeny, e-maile użytkowników) w wywołaniach logów

Ryzyko: Trigger.dev przechowuje wszystkie logi w dashboardzie. Dane wrażliwe są tam widoczne dla każdego z dostępem do dashboardu, nawet miesiące później.

---

## C1: Obsługa Dead Letter

Sprawdź, czy nieudane taski są przechwytywane.

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

Oczekiwanie:
- Tabela failed_jobs istnieje (lub równoważny system)
- Brak starych nierozwiązanych Failed Jobs
- Krytyczne taski (płatności, zamówienia) mają hooki onFailure

Ryzyko: Nieudane taski bez alertingu pozostają niewykryte, aż klient się zgłosi.

---

## C2: Monitoring

Sprawdź, czy monitoring Trigger.dev jest skonfigurowany.

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

Oczekiwanie:
- Dashboard dostępny wewnętrznie
- Dashboard NIE dostępny zewnętrznie
- Health Check lub monitoring skonfigurowany

---

## Podsumowanie

Sporządź teraz podsumowanie w następującym formacie:

```
# Audyt bezpieczeństwa Trigger.dev - [DATA]

## Wynik

ZALICZONY:    X z Y kontroli
OSTRZEŻENIE:  X kontroli
KRYTYCZNY:    X kontroli

## Krytyczne ustalenia (natychmiastowe działanie)
- ...

## Ostrzeżenia (rozwiązać w tym tygodniu)
- ...

## Zaliczone
- ...

## Zalecane następne kroki
1. ...
2. ...
3. ...
```

Priorytetyzuj ściśle: krytyczne ustalenia najpierw, potem ostrzeżenia.
Dla każdego ustalenia: jaki jest problem, dlaczego to ryzyko, jakie jest rozwiązanie.
