# Auditoria do Runbook de Self-Hosting Supabase

Voce e um Senior DevOps Security Auditor. Sua tarefa e verificar se o setup
de Self-Hosting do Supabase foi implementado corretamente conforme o runbook (artigo 1).

Execute os seguintes checks na ordem. Utilize as ferramentas disponiveis
(Read, Grep, Glob e os comandos Bash permitidos).

Para cada check: reporte APROVADO, AVISO ou CRITICO com uma justificativa breve.
Ao final, elabore um resumo com recomendacoes de acao.

---

## A1: Separacao de Infraestrutura

Verifique se producao e auditoria rodam em servidores separados.

```bash
# Hostname des aktuellen Servers
hostname

# Gibt es SSH-Zugang zu einem separaten Audit-Server?
grep -r "audit" ~/.ssh/config 2>/dev/null || echo "Kein SSH Config für audit-runner"

# Oder: Wird im Infra-Repo ein zweiter Server referenziert?
grep -ri "audit-runner\|10.0.1.11" . --include="*.yml" --include="*.sh" --include="*.md" 2>/dev/null | head -10
```

Expectativa: Dois hosts separados (supabase-prod + audit-runner).
Risco se nao atendido: Um servidor comprometido pode manipular seus proprios resultados de auditoria.

---

## A2: Rede Privada

Verifique se uma rede privada esta configurada e se o PostgreSQL escuta apenas internamente.

```bash
# Internes Interface vorhanden?
ip addr show | grep -E "10\.0\.[0-9]+\.[0-9]+"

# Auf welchem Interface lauscht PostgreSQL?
ss -tlnp | grep 5432

# Oder in der docker-compose.yml prüfen:
grep -A2 "5432" docker-compose.yml 2>/dev/null || grep -A2 "5432" /opt/supabase/docker-compose.yml 2>/dev/null
```

Expectativa: PostgreSQL escuta em 10.0.1.10:5432 (interface interna), NAO em 0.0.0.0:5432.
Risco se nao atendido: Banco de dados acessivel diretamente pela internet em caso de falha no firewall.

---

## A3: Reverse Proxy e TLS

Verifique se um Reverse Proxy com TLS esta posicionado na frente do stack Supabase.

```bash
# Caddy oder Nginx vorhanden?
which caddy 2>/dev/null || which nginx 2>/dev/null || echo "Kein Reverse Proxy gefunden"

# Caddy Config vorhanden?
find / -name "Caddyfile" -type f 2>/dev/null | head -5

# Oder Nginx Config?
find /etc/nginx -name "*.conf" -type f 2>/dev/null | head -5

# TLS Zertifikat vorhanden und gültig?
echo | openssl s_client -connect localhost:443 2>/dev/null | openssl x509 -noout -enddate 2>/dev/null

# Security Headers konfiguriert? Suche in Proxy-Config nach:
grep -ri "X-Frame-Options\|Strict-Transport-Security\|Content-Security-Policy" \
  /etc/caddy/ /etc/nginx/ 2>/dev/null | head -10
```

Expectativa:
- Reverse Proxy (Caddy ou Nginx) instalado e configurado
- Certificado TLS valido (minimo 14 dias)
- Security Headers: Strict-Transport-Security, X-Frame-Options, X-Content-Type-Options, Content-Security-Policy

Risco se nao atendido: Tokens de autenticacao trafegam em texto puro. Clickjacking e XSS possiveis.

---

## A4: Firewall (duas camadas)

Verifique se tanto um Cloud Firewall quanto um Host Firewall estao ativos.

```bash
# Host Firewall (iptables) aktiv?
iptables -L -n 2>/dev/null | head -20

# Default Policy ist DROP?
iptables -L INPUT -n 2>/dev/null | head -1

# Welche Ports sind explizit erlaubt?
iptables -L INPUT -n 2>/dev/null | grep "ACCEPT"

# Firewall-Baseline vorhanden?
ls -la /root/firewall-baseline.txt 2>/dev/null || ls -la /opt/baselines/firewall-baseline.txt 2>/dev/null

# Drift gegen Baseline?
if [ -f /root/firewall-baseline.txt ]; then
  iptables-save | diff /root/firewall-baseline.txt - 2>/dev/null
elif [ -f /opt/baselines/firewall-baseline.txt ]; then
  iptables-save | diff /opt/baselines/firewall-baseline.txt - 2>/dev/null
fi
```

Expectativa:
- iptables Default Policy: DROP
- Apenas portas 443 (HTTPS) e 22 (do IP de admin) permitidas
- Rede interna (10.0.1.0/24) permitida
- Arquivo de baseline existente, sem drift

Risco se nao atendido: Um iptables -F abre todas as portas se nao existir Cloud Firewall.

---

## A5: Acesso SSH

Verifique se o SSH esta corretamente protegido.

```bash
# SSH Konfiguration prüfen
sshd -T 2>/dev/null | grep -E "passwordauthentication|permitrootlogin|pubkeyauthentication|allowusers"

# Alternativ direkt die Config lesen
grep -E "^PasswordAuthentication|^PermitRootLogin|^PubkeyAuthentication|^AllowUsers|^MaxAuthTries" \
  /etc/ssh/sshd_config 2>/dev/null
```

Expectativa:
- PasswordAuthentication no
- PermitRootLogin no
- PubkeyAuthentication yes
- AllowUsers contem apenas o usuario de deploy

Risco se nao atendido: Brute-force em senhas SSH, com login root acesso total imediato.

---

## B1: Deployment versionado

Verifique se o setup do Supabase esta versionado no Git e se nao ha alteracoes manuais.

```bash
# Git Repository vorhanden?
cd /opt/supabase 2>/dev/null && git status --porcelain

# Oder wo liegt das Infra-Repo?
find /opt -name "docker-compose.yml" -path "*/supabase/*" 2>/dev/null | head -5

# Uncommitted Changes?
cd /opt/supabase 2>/dev/null && git diff --stat HEAD 2>/dev/null

# Differenz zum Remote?
cd /opt/supabase 2>/dev/null && git fetch origin 2>/dev/null && git diff HEAD origin/main --stat 2>/dev/null
```

Expectativa:
- Repositorio Git em /opt/supabase (ou similar)
- Sem uncommitted changes (git status --porcelain vazio)
- Sem diferenca com o remote (servidor atualizado)

Risco se nao atendido: Alteracoes manuais se perdem no git pull. Apos perda do servidor, nao e reproduzivel.

---

## B2: Configuracao de seguranca do Docker-Compose

Verifique o docker-compose.yml quanto aos pontos criticos.

```bash
# docker-compose.yml finden und lesen
COMPOSE=$(find /opt -name "docker-compose.yml" -path "*/supabase/*" 2>/dev/null | head -1)
if [ -n "$COMPOSE" ]; then
  # Images gepinnt (kein :latest)?
  echo "=== Images mit :latest ==="
  grep "image:" "$COMPOSE" | grep "latest" || echo "Keine :latest gefunden (gut)"

  # Postgres Port-Binding
  echo "=== Postgres Port ==="
  grep -A3 "postgres:" "$COMPOSE" | grep "ports" -A1

  # Kong Port-Binding
  echo "=== Kong Port ==="
  grep -A5 "kong:" "$COMPOSE" | grep "ports" -A1

  # GoTrue JWT Expiry
  echo "=== JWT Expiry ==="
  grep "GOTRUE_JWT_EXP" "$COMPOSE" || grep "JWT_EXP" "$COMPOSE" || echo "Nicht in compose (prüfe .env)"

  # GoTrue Autoconfirm
  echo "=== Autoconfirm ==="
  grep "MAILER_AUTOCONFIRM" "$COMPOSE" || echo "Nicht in compose (prüfe .env)"

  # Refresh Token Rotation
  echo "=== Refresh Token Rotation ==="
  grep "REFRESH_TOKEN_ROTATION" "$COMPOSE" || echo "Nicht in compose (prüfe .env)"
fi
```

Verifique tambem o arquivo .env (sem exibir os valores):

```bash
ENV_FILE=$(find /opt -name ".env" -path "*/supabase/*" -not -path "*example*" 2>/dev/null | head -1)
if [ -n "$ENV_FILE" ]; then
  echo "=== JWT Expiry in .env ==="
  grep "JWT_EXP" "$ENV_FILE" | sed 's/=.*/=***/'
  EXPIRY=$(grep "GOTRUE_JWT_EXP" "$ENV_FILE" 2>/dev/null | cut -d= -f2)
  if [ -n "$EXPIRY" ] && [ "$EXPIRY" -gt 3600 ] 2>/dev/null; then
    echo "CRÍTICO: JWT_EXP ist $EXPIRY (über 3600)"
  fi
fi
```

Expectativa:
- Todas as images versionadas (sem :latest)
- Porta Postgres: 10.0.1.10:5432 (interna) ou 127.0.0.1:5432
- Porta Kong: 127.0.0.1:8000 (localhost)
- GOTRUE_JWT_EXP: maximo 3600
- GOTRUE_MAILER_AUTOCONFIRM: false
- REFRESH_TOKEN_ROTATION: true

Risco se nao atendido: Images sem versao fixa podem introduzir breaking changes ou vulnerabilidades.
JWT com 24h de validade = token roubado utilizavel por um dia inteiro.

---

## B3: Gerenciamento de Secrets

Verifique se os secrets estao sendo gerenciados corretamente.

```bash
# .env nicht im Git?
cd /opt/supabase 2>/dev/null && git ls-files .env 2>/dev/null
# Muss leer sein

# .env in .gitignore?
cd /opt/supabase 2>/dev/null && grep "^\.env$" .gitignore 2>/dev/null

# Dateirechte der .env
ENV_FILE=$(find /opt -name ".env" -path "*/supabase/*" -not -path "*example*" 2>/dev/null | head -1)
if [ -n "$ENV_FILE" ]; then
  stat -c "%a %U" "$ENV_FILE"
fi

# Secrets-Länge prüfen (ohne Werte zu zeigen)
if [ -n "$ENV_FILE" ]; then
  echo "=== Secrets kürzer als 16 Zeichen ==="
  awk -F= '{
    if (length($2) > 0 && length($2) < 16 && $1 !~ /PORT|HOST|NAME|SENDER|USER|ENABLED|AUTOCONFIRM|DISABLE|HEADER|URL|ROLE|INTERVAL/)
      print "ZU KURZ: " $1 " (" length($2) " Zeichen)"
  }' "$ENV_FILE"
fi

# Default-Passwörter?
if [ -n "$ENV_FILE" ]; then
  echo "=== Default-Passwörter ==="
  grep -iE "password|secret|key" "$ENV_FILE" | \
    grep -iE "change.me|default|example|your.*here|super-secret|please-change" || \
    echo "Keine Default-Passwörter gefunden (gut)"
fi

# .env.example vorhanden?
ls /opt/supabase/.env.example 2>/dev/null || echo ".env.example fehlt"
```

Expectativa:
- .env NAO esta no Git (git ls-files vazio)
- .env no .gitignore
- Permissoes do arquivo: 600, Owner: deploy
- Todos os secrets com no minimo 16 caracteres
- Sem senhas padrao
- .env.example como template no repositorio

Risco se nao atendido: Secrets vazados sao o problema de seguranca mais comum em self-hosting.

---

## B4: Politicas RLS do Banco de Dados

Verifique se o Row Level Security esta ativo em todas as tabelas public.

```bash
# Tabellen ohne RLS
docker compose exec -T postgres psql -U postgres -c \
  "SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public' AND rowsecurity = false;" \
  2>/dev/null

# Tabellen mit RLS aber ohne Policies (gesperrt, aber möglicherweise versehentlich)
docker compose exec -T postgres psql -U postgres -c \
  "SELECT t.tablename FROM pg_tables t LEFT JOIN pg_policies p ON t.tablename = p.tablename WHERE t.schemaname = 'public' AND t.rowsecurity = true AND p.policyname IS NULL;" \
  2>/dev/null

# Zu offene Policies (qual = 'true' = jeder hat Zugriff)
docker compose exec -T postgres psql -U postgres -c \
  "SELECT tablename, policyname, cmd, qual FROM pg_policies WHERE schemaname = 'public' AND qual = 'true';" \
  2>/dev/null
```

Expectativa:
- Nenhuma tabela public sem RLS (ou excecoes conscientemente documentadas)
- Nenhuma tabela com RLS mas sem nenhuma policy (exceto bloqueio intencional)
- Nenhuma policy com qual = 'true' (= acesso irrestrito)

Risco se nao atendido: Tabelas sem RLS sao legiveis por qualquer pessoa via anon key. Uma policy aberta na tabela users expoe todos os dados de usuarios.

---

## B5: Secrets completos e gerados de forma segura

Verifique se todos os secrets necessarios estao definidos e nao contem valores padrao.

```bash
ENV_FILE=$(find /opt -name ".env" -path "*/supabase/*" -not -path "*example*" 2>/dev/null | head -1)
if [ -n "$ENV_FILE" ]; then
  echo "=== Erforderliche Secrets ==="
  for var in JWT_SECRET POSTGRES_PASSWORD ANON_KEY SERVICE_ROLE_KEY \
    DASHBOARD_PASSWORD LOGFLARE_PUBLIC_ACCESS_TOKEN LOGFLARE_PRIVATE_ACCESS_TOKEN \
    SECRET_KEY_BASE VAULT_ENC_KEY PG_META_CRYPTO_KEY; do
    VAL=$(grep "^${var}=" "$ENV_FILE" 2>/dev/null | cut -d= -f2)
    if [ -z "$VAL" ]; then
      echo "CRÍTICO: $var ist nicht gesetzt"
    elif echo "$VAL" | grep -qiE "your-|change|example|super-secret|default"; then
      echo "CRÍTICO: $var hat Default-Wert"
    else
      LEN=${#VAL}
      echo "OK: $var ($LEN Zeichen)"
    fi
  done

  # JWT_SECRET mindestens 32 Zeichen?
  JWT_LEN=$(grep "^JWT_SECRET=" "$ENV_FILE" | cut -d= -f2 | wc -c)
  [ "$JWT_LEN" -lt 32 ] && echo "CRÍTICO: JWT_SECRET zu kurz ($JWT_LEN Zeichen, min. 32)"

  # SECRET_KEY_BASE mindestens 64 Zeichen?
  SKB_LEN=$(grep "^SECRET_KEY_BASE=" "$ENV_FILE" | cut -d= -f2 | wc -c)
  [ "$SKB_LEN" -lt 64 ] && echo "CRÍTICO: SECRET_KEY_BASE zu kurz ($SKB_LEN Zeichen, min. 64)"

  # Doppelte Werte (gleicher Wert für verschiedene Secrets)?
  echo "=== Doppelte Secret-Werte ==="
  grep -E "SECRET|PASSWORD|KEY" "$ENV_FILE" | cut -d= -f2 | sort | uniq -d | \
    while read dup; do
      [ -n "$dup" ] && echo "AVISO: Mehrere Secrets haben den gleichen Wert"
    done
fi
```

Expectativa:
- Todos os secrets definidos, sem valores padrao
- JWT_SECRET com no minimo 32 caracteres
- SECRET_KEY_BASE com no minimo 64 caracteres
- Sem valores identicos entre diferentes secrets

Risco se nao atendido: JWT_SECRET padrao e publicamente conhecido. Atacantes podem gerar tokens validos.

---

## B6: Configuracao do Kong API Gateway

Verifique se o Kong esta configurado corretamente e escuta apenas em localhost.

```bash
# Kong Port-Binding
echo "=== Kong Ports ==="
ss -tlnp | grep -E "8000|8443"
# Erwartung: 127.0.0.1:8000 und 127.0.0.1:8443

# Kong Config vorhanden?
echo "=== Kong Config ==="
ls -la /opt/supabase/volumes/api/kong.yml 2>/dev/null || echo "kong.yml nicht gefunden"

# JWT Validation aktiv auf API-Routes?
echo "=== JWT Plugins in kong.yml ==="
grep -A3 "key-auth\|jwt" /opt/supabase/volumes/api/kong.yml 2>/dev/null | head -20

# CORS Plugin aktiv?
echo "=== CORS Plugin ==="
grep -A3 "cors" /opt/supabase/volumes/api/kong.yml 2>/dev/null | head -10

# Dashboard Basic Auth: Passwort-Stärke
echo "=== Dashboard-Passwort ==="
DASH_PW=$(grep "^DASHBOARD_PASSWORD=" "$ENV_FILE" 2>/dev/null | cut -d= -f2)
if [ -n "$DASH_PW" ]; then
  DASH_LEN=${#DASH_PW}
  [ "$DASH_LEN" -lt 16 ] && echo "AVISO: Dashboard-Passwort zu kurz ($DASH_LEN Zeichen)"
  [ "$DASH_LEN" -ge 16 ] && echo "OK: Dashboard-Passwort ($DASH_LEN Zeichen)"
else
  echo "CRÍTICO: DASHBOARD_PASSWORD nicht gesetzt"
fi
```

Expectativa:
- Kong escuta em 127.0.0.1:8000 (NAO em 0.0.0.0)
- kong.yml presente com validacao JWT nas rotas de API
- Senha do dashboard com no minimo 16 caracteres

Risco se nao atendido: Kong em 0.0.0.0 = API acessivel diretamente sem TLS. Senha fraca do dashboard = acesso ao Studio para atacantes.

---

## B7: Configuracao do GoTrue (Auth)

Verifique se a autenticacao esta configurada de forma segura.

```bash
ENV_FILE=$(find /opt -name ".env" -path "*/supabase/*" -not -path "*example*" 2>/dev/null | head -1)
COMPOSE=$(find /opt -name "docker-compose.yml" -path "*/supabase/*" 2>/dev/null | head -1)

# GoTrue Health
echo "=== GoTrue Health ==="
docker compose exec -T auth wget --no-verbose --tries=1 --spider http://localhost:9999/health 2>&1

# JWT Expiry
echo "=== JWT Expiry ==="
EXPIRY=$(grep -E "^JWT_EXPIRY=|^JWT_EXP=" "$ENV_FILE" 2>/dev/null | head -1 | cut -d= -f2)
if [ -n "$EXPIRY" ] && [ "$EXPIRY" -gt 3600 ] 2>/dev/null; then
  echo "CRÍTICO: JWT Expiry ist $EXPIRY Sekunden (max. 3600)"
else
  echo "OK: JWT Expiry ist ${EXPIRY:-3600} Sekunden"
fi

# Autoconfirm
echo "=== E-Mail Autoconfirm ==="
AUTOCONFIRM=$(grep -iE "AUTOCONFIRM|ENABLE_EMAIL_AUTOCONFIRM" "$ENV_FILE" 2>/dev/null | head -1 | cut -d= -f2)
if [ "$AUTOCONFIRM" = "true" ]; then
  echo "CRÍTICO: E-Mail Autoconfirm ist aktiviert"
else
  echo "OK: E-Mail Autoconfirm deaktiviert"
fi

# SMTP konfiguriert?
echo "=== SMTP Konfiguration ==="
for var in SMTP_HOST SMTP_PORT SMTP_USER SMTP_PASS; do
  VAL=$(grep "^${var}=" "$ENV_FILE" 2>/dev/null | cut -d= -f2)
  if [ -z "$VAL" ]; then
    echo "AVISO: $var ist leer (Magic Links und E-Mail-Bestätigung funktionieren nicht)"
  else
    echo "OK: $var ist gesetzt"
  fi
done

# Refresh Token Rotation
echo "=== Refresh Token Rotation ==="
grep -i "REFRESH_TOKEN_ROTATION" "$ENV_FILE" "$COMPOSE" 2>/dev/null || \
  echo "AVISO: Refresh Token Rotation nicht explizit konfiguriert"

# Signup offen oder geschlossen?
echo "=== Signup Status ==="
SIGNUP=$(grep "DISABLE_SIGNUP" "$ENV_FILE" 2>/dev/null | cut -d= -f2)
echo "DISABLE_SIGNUP=${SIGNUP:-false} (false = offen, true = geschlossen)"

# Site URL und API External URL
echo "=== URLs ==="
grep -E "^SITE_URL=|^API_EXTERNAL_URL=" "$ENV_FILE" 2>/dev/null
```

Expectativa:
- JWT Expiry maximo 3600 segundos
- Autoconfirm desativado (false)
- SMTP completamente configurado (Host, Port, User, Pass)
- Refresh Token Rotation ativado
- SITE_URL e API_EXTERNAL_URL corretamente definidos (HTTPS)

Risco se nao atendido: Autoconfirm = true sem SMTP = qualquer pessoa pode criar contas sem verificacao de e-mail. JWT com 24h de validade = token roubado utilizavel por um dia inteiro.

---

## B8: Configuracao do PostgREST

Verifique se o PostgREST esta configurado de forma segura.

```bash
COMPOSE=$(find /opt -name "docker-compose.yml" -path "*/supabase/*" 2>/dev/null | head -1)
ENV_FILE=$(find /opt -name ".env" -path "*/supabase/*" -not -path "*example*" 2>/dev/null | head -1)

# PostgREST nutzt authenticator-Rolle (nicht postgres)?
echo "=== PostgREST DB User ==="
grep "PGRST_DB_URI" "$COMPOSE" "$ENV_FILE" 2>/dev/null | head -1
# Muss "authenticator" enthalten, NICHT "postgres"

if grep "PGRST_DB_URI" "$COMPOSE" "$ENV_FILE" 2>/dev/null | grep -q "postgres://postgres:"; then
  echo "CRÍTICO: PostgREST nutzt den postgres Superuser statt authenticator"
fi

# Schemas eingeschränkt?
echo "=== PostgREST Schemas ==="
SCHEMAS=$(grep "PGRST_DB_SCHEMAS" "$ENV_FILE" 2>/dev/null | cut -d= -f2)
if [ -z "$SCHEMAS" ]; then
  echo "AVISO: PGRST_DB_SCHEMAS ist leer (alle Schemas exponiert)"
else
  echo "OK: Schemas eingeschränkt auf: $SCHEMAS"
fi
```

Expectativa:
- PGRST_DB_URI utiliza a role authenticator (NAO o superuser postgres)
- PGRST_DB_SCHEMAS explicitamente definido (public,storage,graphql_public)

Risco se nao atendido: PostgREST com superuser postgres = RLS ineficaz, cada request tem acesso total ao banco. Schemas vazio = schemas internos do Supabase (auth, _realtime) expostos via API.

---

## B9: Configuracao do Realtime

Verifique se o Realtime esta configurado de forma segura.

```bash
COMPOSE=$(find /opt -name "docker-compose.yml" -path "*/supabase/*" 2>/dev/null | head -1)
ENV_FILE=$(find /opt -name ".env" -path "*/supabase/*" -not -path "*example*" 2>/dev/null | head -1)

# DB_ENC_KEY nicht auf Default?
echo "=== Realtime DB_ENC_KEY ==="
ENC_KEY=$(grep "DB_ENC_KEY" "$COMPOSE" 2>/dev/null | grep -v "^#" | head -1)
if echo "$ENC_KEY" | grep -q "supabaserealtime"; then
  echo "CRÍTICO: DB_ENC_KEY ist auf dem Default-Wert 'supabaserealtime'"
else
  echo "OK: DB_ENC_KEY ist geändert"
fi

# SECRET_KEY_BASE Länge
echo "=== SECRET_KEY_BASE ==="
SKB=$(grep "^SECRET_KEY_BASE=" "$ENV_FILE" 2>/dev/null | cut -d= -f2)
if [ -n "$SKB" ]; then
  SKB_LEN=${#SKB}
  [ "$SKB_LEN" -lt 64 ] && echo "CRÍTICO: SECRET_KEY_BASE nur $SKB_LEN Zeichen (min. 64)"
  [ "$SKB_LEN" -ge 64 ] && echo "OK: SECRET_KEY_BASE hat $SKB_LEN Zeichen"
else
  echo "CRÍTICO: SECRET_KEY_BASE nicht gesetzt"
fi

# Realtime Health
echo "=== Realtime Health ==="
docker compose exec -T realtime-dev.supabase-realtime \
  curl -sSf -o /dev/null -w "%{http_code}" \
  -H "Authorization: Bearer $(grep '^ANON_KEY=' $ENV_FILE | cut -d= -f2)" \
  http://localhost:4000/api/tenants/realtime-dev/health 2>/dev/null || echo "Health Check fehlgeschlagen"
```

Expectativa:
- DB_ENC_KEY NAO e "supabaserealtime" (valor padrao)
- SECRET_KEY_BASE com no minimo 64 caracteres
- Health Check do Realtime bem-sucedido

Risco se nao atendido: DB_ENC_KEY padrao = criptografia previsivel.

---

## B10: Storage e MinIO

Verifique se o Storage e, se aplicavel, o MinIO estao configurados de forma segura.

```bash
# Storage Health
echo "=== Storage Health ==="
docker compose exec -T storage wget --no-verbose --tries=1 --spider http://localhost:5000/status 2>&1

# Storage Backend (file oder s3)?
echo "=== Storage Backend ==="
COMPOSE=$(find /opt -name "docker-compose*.yml" -path "*/supabase/*" 2>/dev/null)
grep "STORAGE_BACKEND" $COMPOSE 2>/dev/null || echo "Default: file (lokal)"

# MinIO vorhanden?
echo "=== MinIO ==="
docker ps --format '{{.Names}}\t{{.Image}}' 2>/dev/null | grep -i "minio"

# MinIO Ports nur localhost?
if docker ps 2>/dev/null | grep -q minio; then
  echo "=== MinIO Ports ==="
  ss -tlnp | grep -E "9000|9001"
  # Erwartung: 127.0.0.1, NICHT 0.0.0.0

  # MinIO Default-Credentials?
  ENV_FILE=$(find /opt -name ".env" -path "*/supabase/*" -not -path "*example*" 2>/dev/null | head -1)
  MINIO_USER=$(grep "MINIO_ROOT_USER" "$ENV_FILE" 2>/dev/null | cut -d= -f2)
  MINIO_PASS=$(grep "MINIO_ROOT_PASSWORD" "$ENV_FILE" 2>/dev/null | cut -d= -f2)
  [ "$MINIO_USER" = "minioadmin" ] && echo "CRÍTICO: MinIO Root User ist 'minioadmin' (Default)"
  [ "$MINIO_PASS" = "minioadmin" ] && echo "CRÍTICO: MinIO Root Password ist 'minioadmin' (Default)"
  [ -n "$MINIO_PASS" ] && [ ${#MINIO_PASS} -lt 8 ] && echo "AVISO: MinIO Passwort zu kurz (min. 8)"
fi

# Storage Volume Rechte
echo "=== Storage Volume ==="
ls -la /opt/supabase/volumes/storage/ 2>/dev/null | head -5
```

Expectativa:
- Health Check do Storage bem-sucedido
- Se MinIO: portas em 127.0.0.1 (nao em 0.0.0.0)
- Se MinIO: sem credenciais padrao (minioadmin/minioadmin)
- Volume do Storage existente e gravavel

Risco se nao atendido: MinIO com credenciais padrao em 0.0.0.0 = todos os arquivos publicamente legiveis e gravaveis.

---

## B11: Servicos Internos (Analytics, Vector, Supavisor)

Verifique se os servicos internos estao corretamente protegidos.

```bash
# Analytics (Logflare) nur intern?
echo "=== Analytics Port ==="
ss -tlnp | grep 4000
# Erwartung: 127.0.0.1:4000

# Logflare Tokens nicht auf Default?
ENV_FILE=$(find /opt -name ".env" -path "*/supabase/*" -not -path "*example*" 2>/dev/null | head -1)
echo "=== Logflare Tokens ==="
for var in LOGFLARE_PUBLIC_ACCESS_TOKEN LOGFLARE_PRIVATE_ACCESS_TOKEN; do
  VAL=$(grep "^${var}=" "$ENV_FILE" 2>/dev/null | cut -d= -f2)
  if [ -z "$VAL" ] || echo "$VAL" | grep -qiE "your-|change|example"; then
    echo "AVISO: $var ist nicht gesetzt oder Default"
  else
    echo "OK: $var ist gesetzt (${#VAL} Zeichen)"
  fi
done

# Vector Docker Socket Read-Only?
echo "=== Vector Docker Socket ==="
COMPOSE=$(find /opt -name "docker-compose.yml" -path "*/supabase/*" 2>/dev/null | head -1)
if grep -A5 "vector:" "$COMPOSE" 2>/dev/null | grep "docker.sock" | grep -q ":ro"; then
  echo "OK: Docker Socket ist Read-Only gemountet"
else
  echo "AVISO: Docker Socket möglicherweise nicht Read-Only"
fi

# Supavisor Port nur intern?
echo "=== Supavisor Port ==="
ss -tlnp | grep 6543
# Erwartung: 127.0.0.1:6543

# Pooler Config
echo "=== Pooler Config ==="
grep -E "POOLER_DEFAULT_POOL_SIZE|POOLER_MAX_CLIENT_CONN" "$ENV_FILE" 2>/dev/null
```

Expectativa:
- Analytics em 127.0.0.1:4000 (nao em 0.0.0.0)
- Tokens do Logflare definidos (nao padrao)
- Docker Socket do Vector com :ro (Read-Only)
- Porta Transaction do Supavisor em localhost

Risco se nao atendido: Analytics externo = logs de todos os servicos publicos. Vector sem Read-Only = container pode executar comandos Docker. Supavisor externo = Connection Pooler (e portanto o banco) acessivel de fora.

---

## B12: Roles e Grants do PostgreSQL

Verifique se as roles do banco de dados estao configuradas corretamente.

```bash
# Alle relevanten Rollen und ihre Rechte
echo "=== Datenbankrollen ==="
docker compose exec -T db psql -U postgres -c \
  "SELECT rolname, rolsuper, rolcreaterole, rolcreatedb, rolcanlogin
   FROM pg_roles
   WHERE rolname IN ('anon','authenticated','service_role','authenticator',
     'supabase_admin','supabase_auth_admin','supabase_storage_admin')
   ORDER BY rolname;" 2>/dev/null

# anon darf NICHT superuser sein
echo "=== anon Superuser Check ==="
ANON_SUPER=$(docker compose exec -T db psql -U postgres -t -c \
  "SELECT rolsuper FROM pg_roles WHERE rolname = 'anon';" 2>/dev/null | tr -d ' ')
if [ "$ANON_SUPER" = "t" ]; then
  echo "CRÍTICO: anon-Rolle ist Superuser!"
else
  echo "OK: anon-Rolle ist kein Superuser"
fi

# service_role hat die erwarteten Rechte?
echo "=== service_role Rechte ==="
docker compose exec -T db psql -U postgres -t -c \
  "SELECT rolsuper, rolbypassrls FROM pg_roles WHERE rolname = 'service_role';" 2>/dev/null

# Gibt es unbekannte Rollen mit Login-Berechtigung?
echo "=== Rollen mit Login ==="
docker compose exec -T db psql -U postgres -c \
  "SELECT rolname FROM pg_roles WHERE rolcanlogin = true
   AND rolname NOT IN ('postgres','authenticator','supabase_admin',
     'supabase_auth_admin','supabase_storage_admin','supabase_replication_admin',
     'supabase_read_only_user')
   ORDER BY rolname;" 2>/dev/null
```

Expectativa:
- anon: NAO e superuser, NAO e createrole, NAO e createdb
- authenticated: NAO e superuser
- service_role: bypassrls = true (isso e intencional), NAO e superuser
- Nenhuma role desconhecida com permissao de login

Risco se nao atendido: anon como superuser = cada request nao autenticado tem acesso total ao banco. Roles desconhecidas com login podem ser acessos backdoor.

---

## C1: Backups

Verifique se os backups estao configurados corretamente.

```bash
# Backup-Verzeichnis vorhanden?
ls -lh /opt/backups/*.gpg 2>/dev/null | tail -5

# Letztes Backup: Alter in Stunden
LAST=$(ls -t /opt/backups/*.gpg 2>/dev/null | head -1)
if [ -n "$LAST" ]; then
  AGE=$(( ($(date +%s) - $(stat -c %Y "$LAST")) / 3600 ))
  echo "Letztes Backup: $LAST ($AGE Stunden alt)"
  [ "$AGE" -gt 26 ] && echo "AVISO: Älter als 26 Stunden"
else
  echo "CRÍTICO: Kein Backup gefunden"
fi

# Backup-Dateien nicht 0 Bytes?
find /opt/backups -name "*.gpg" -size 0 -print 2>/dev/null

# Backup verschlüsselt? (GPG Check)
LAST=$(ls -t /opt/backups/*.gpg 2>/dev/null | head -1)
if [ -n "$LAST" ]; then
  file "$LAST" | grep -i "pgp\|gpg\|encrypted" || echo "AVISO: Möglicherweise nicht verschlüsselt"
fi

# Cron Job aktiv?
crontab -l 2>/dev/null | grep "backup"

# Backups extern gespeichert? (auf audit-runner)
ssh deploy@10.0.1.11 "ls -lh /opt/backup-archive/*.gpg 2>/dev/null | tail -3" 2>/dev/null || \
  echo "AVISO: Externe Backups nicht prüfbar (kein SSH zum audit-runner)"

# Restore-Script vorhanden?
ls /opt/supabase/scripts/restore*.sh 2>/dev/null || echo "AVISO: Kein Restore-Script gefunden"
```

Expectativa:
- Backup diario presente (menos de 26 horas)
- Backups criptografados (GPG)
- Sem arquivos de 0 bytes
- Cron Job para backup diario ativo
- Backups armazenados externamente (audit-runner)
- Script de restore presente

Risco se nao atendido: Em caso de perda do servidor (hardware, ransomware) sem caminho de volta.

---

## C2: Restore testado

Verifique se um teste de restore foi realizado.

```bash
# Restore-Test Log vorhanden?
ls -la /var/log/restore-test.log 2>/dev/null

# Wann war der letzte Restore-Test?
if [ -f /var/log/restore-test.log ]; then
  stat -c "%y" /var/log/restore-test.log
  tail -5 /var/log/restore-test.log
fi

# Restore-Script vorhanden und ausführbar?
ls -la /opt/supabase/scripts/restore-test.sh 2>/dev/null
```

Expectativa:
- Teste de restore realizado pelo menos uma vez (log existente)
- Ultimo teste com menos de 30 dias
- Script de restore presente e executavel

Risco se nao atendido: Backups que nunca foram testados frequentemente sao inutilizaveis.

---

## C3: Checks de Seguranca Diarios

Verifique se checks automatizados estao configurados.

```bash
# Security Check Script vorhanden?
ls -la /opt/supabase/scripts/security-check.sh 2>/dev/null || \
ls -la /opt/audit/scripts/security-check.sh 2>/dev/null || \
  echo "AVISO: Kein Security Check Script gefunden"

# Cron Job für tägliche Checks?
crontab -l 2>/dev/null | grep "security-check"

# Letzter Check-Log
ls -la /var/log/security-check.log 2>/dev/null
if [ -f /var/log/security-check.log ]; then
  echo "=== Letzte Einträge ==="
  tail -10 /var/log/security-check.log
fi
```

Expectativa:
- Script de security-check presente
- Cron Job diario ativo
- Logs de hoje presentes

Risco se nao atendido: Desvio de configuracao permanece sem deteccao ate o incidente.

---

## C4: Acesso ao Supabase Studio

Verifique se o Supabase Studio nao esta acessivel externamente.

```bash
# Studio Container läuft?
docker compose ps 2>/dev/null | grep -i "studio"

# Auf welchem Port/Interface lauscht Studio?
ss -tlnp | grep -E "3000|9000" 2>/dev/null

# Von localhost erreichbar?
curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 2>/dev/null || echo "Nicht erreichbar"
```

Expectativa:
- Studio ausente do stack de producao OU escutando apenas em localhost/interface interna
- NAO escutando em 0.0.0.0

Risco se nao atendido: Studio com senha padrao fornece acesso total ao banco de dados.

---

---

## M1: Unattended Upgrades (patches automaticos de seguranca do SO)

```bash
echo "=== Unattended Upgrades installiert? ==="
dpkg -l | grep unattended-upgrades || echo "NICHT installiert"

echo "=== Service aktiv? ==="
systemctl is-active unattended-upgrades

echo "=== Konfiguration ==="
cat /etc/apt/apt.conf.d/50unattended-upgrades 2>/dev/null | grep -E "Allowed-Origins|Automatic-Reboot|Mail" | head -10

echo "=== Auto-Update Konfiguration ==="
cat /etc/apt/apt.conf.d/20auto-upgrades 2>/dev/null

echo "=== Letzte automatische Updates ==="
ls -la /var/log/unattended-upgrades/ 2>/dev/null
tail -20 /var/log/unattended-upgrades/unattended-upgrades.log 2>/dev/null || echo "Kein Log vorhanden"
```

Expectativa:
- unattended-upgrades instalado e ativo
- Origens de seguranca configuradas
- Intervalo de atualizacao automatica configurado para diariamente
- Logs presentes

Risco: Sem patches automaticos de seguranca, vulnerabilidades conhecidas do SO se acumulam com o tempo.

---

## M2: Atualizacoes pendentes

```bash
echo "=== Ausstehende Packages ==="
apt list --upgradable 2>/dev/null | head -20

echo "=== Davon Security Updates ==="
apt list --upgradable 2>/dev/null | grep -i security | head -10

echo "=== Letztes apt update ==="
stat -c "%y" /var/cache/apt/pkgcache.bin 2>/dev/null
AGE=$(( ($(date +%s) - $(stat -c %Y /var/cache/apt/pkgcache.bin 2>/dev/null || echo 0)) / 86400 ))
echo "Alter: ${AGE} Tage"
[ "$AGE" -gt 7 ] && echo "WARNUNG: apt update ist überfällig"

echo "=== Reboot erforderlich? ==="
test -f /var/run/reboot-required && cat /var/run/reboot-required || echo "Kein Reboot nötig"
```

Expectativa:
- Sem atualizacoes de seguranca pendentes
- apt update com menos de 7 dias
- Sem reboot pendente (ou conscientemente agendado)

---

## M3: Versoes e idade das imagens Supabase

```bash
cd /opt/supabase 2>/dev/null || cd /opt

echo "=== Aktuelle Image Versionen ==="
docker compose images --format '{{.Repository}}:{{.Tag}}' 2>/dev/null

echo "=== Image Alter ==="
for image in $(docker compose images --format '{{.Repository}}:{{.Tag}}' 2>/dev/null); do
  CREATED=$(docker inspect --format='{{.Created}}' "$image" 2>/dev/null | cut -dT -f1)
  if [ -n "$CREATED" ]; then
    AGE=$(( ($(date +%s) - $(date -d "$CREATED" +%s 2>/dev/null || echo 0)) / 86400 ))
    echo "$image: erstellt $CREATED ($AGE Tage)"
    [ "$AGE" -gt 90 ] && echo "  → WARNUNG: Älter als 90 Tage"
  fi
done

echo "=== Images gepinnt (kein :latest)? ==="
grep "image:" docker-compose.yml 2>/dev/null | grep "latest" && \
  echo "WARNUNG: :latest Images gefunden" || echo "OK: Alle Images versioniert"

echo "=== Letzter Update-Commit ==="
git log --oneline --grep="Update\|update\|upgrade" -5 2>/dev/null || echo "Keine Update-Commits gefunden"

LAST_UPDATE=$(git log --format="%ai" --grep="Update\|update" -1 2>/dev/null | cut -d' ' -f1)
if [ -n "$LAST_UPDATE" ]; then
  UPDATE_AGE=$(( ($(date +%s) - $(date -d "$LAST_UPDATE" +%s)) / 86400 ))
  echo "Letztes Update: $LAST_UPDATE ($UPDATE_AGE Tage her)"
  [ "$UPDATE_AGE" -gt 45 ] && echo "WARNUNG: Über 45 Tage seit letztem Update"
fi
```

Expectativa:
- Todas as imagens com versao fixada (sem :latest)
- Nenhuma imagem com mais de 90 dias
- Commit de atualizacao nos ultimos 45 dias

Risco: Imagens desatualizadas contem CVEs conhecidos. Supabase GoTrue, PostgREST e Kong recebem patches de seguranca regularmente.

---

## M4: Integridade do backup

```bash
echo "=== Letztes Backup ==="
LAST=$(ls -t /opt/backups/*.gpg 2>/dev/null | head -1)
if [ -n "$LAST" ]; then
  AGE=$(( ($(date +%s) - $(stat -c %Y "$LAST")) / 3600 ))
  SIZE=$(stat -c %s "$LAST")
  echo "Datei: $LAST"
  echo "Alter: ${AGE} Stunden"
  echo "Grösse: $(numfmt --to=iec $SIZE)"
  [ "$AGE" -gt 26 ] && echo "WARNUNG: Backup älter als 26 Stunden"
  [ "$SIZE" -lt 1024 ] && echo "KRITISCH: Backup verdächtig klein (< 1KB)"
else
  echo "KRITISCH: Kein Backup gefunden"
fi

echo "=== Backup Cron aktiv? ==="
crontab -l 2>/dev/null | grep "backup" || echo "WARNUNG: Kein Backup-Cron"

echo "=== Externe Backups (audit-runner)? ==="
ssh deploy@10.0.1.11 "ls -lh /opt/backup-archive/*.gpg 2>/dev/null | tail -3" 2>/dev/null || \
  echo "INFO: Externe Backups nicht prüfbar"

echo "=== Letzter Restore-Test ==="
if [ -f /var/log/restore-test.log ]; then
  RESTORE_DATE=$(stat -c "%y" /var/log/restore-test.log | cut -d' ' -f1)
  RESTORE_AGE=$(( ($(date +%s) - $(date -d "$RESTORE_DATE" +%s)) / 86400 ))
  echo "Letzter Test: $RESTORE_DATE ($RESTORE_AGE Tage)"
  [ "$RESTORE_AGE" -gt 35 ] && echo "WARNUNG: Restore-Test über 35 Tage her"
else
  echo "WARNUNG: Kein Restore-Test Log gefunden"
fi

echo "=== Restore-Test Cron? ==="
crontab -l 2>/dev/null | grep "restore" || echo "WARNUNG: Kein Restore-Test Cron"
```

Expectativa:
- Backup diario presente e atual (< 26h)
- Backup nao suspeitamente pequeno
- Backup armazenado externamente
- Teste de restore nos ultimos 35 dias
- Cron Jobs ativos para ambas as operacoes

---

## M5: Docker Engine e ferramentas

```bash
echo "=== Docker Version ==="
docker version --format 'Client: {{.Client.Version}}, Server: {{.Server.Version}}' 2>/dev/null

echo "=== Docker Compose Version ==="
docker compose version

echo "=== Caddy Version ==="
caddy version 2>/dev/null || echo "Caddy nicht installiert"

echo "=== Node.js Version ==="
node --version 2>/dev/null || echo "Node.js nicht installiert"

echo "=== Git Version ==="
git --version

echo "=== OpenSSL Version ==="
openssl version
```

Expectativa:
- Docker: versao estavel atual
- Caddy: versao atual
- OpenSSL: sem CVEs conhecidos na versao instalada

---

## M6: Validade do certificado TLS

```bash
echo "=== TLS Zertifikat ==="
CERT_END=$(echo | openssl s_client -connect localhost:443 2>/dev/null | \
  openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
if [ -n "$CERT_END" ]; then
  DAYS=$(( ($(date -d "$CERT_END" +%s) - $(date +%s)) / 86400 ))
  echo "Ablauf: $CERT_END ($DAYS Tage)"
  [ "$DAYS" -lt 14 ] && echo "KRITISCH: Zertifikat läuft in $DAYS Tagen ab"
  [ "$DAYS" -lt 30 ] && echo "WARNUNG: Zertifikat läuft in $DAYS Tagen ab"
else
  echo "WARNUNG: Zertifikat nicht prüfbar"
fi

echo "=== Caddy Auto-Renewal aktiv? ==="
systemctl is-active caddy
# Caddy erneuert automatisch, aber nur wenn der Service läuft
```

Expectativa:
- Certificado valido por pelo menos 14 dias
- Servico Caddy ativo (para renovacao automatica)

---

## M7: Espaco em disco e limpeza

```bash
echo "=== Disk Usage ==="
df -h / | tail -1

echo "=== Docker Disk Usage ==="
docker system df

echo "=== Alte Docker Images ==="
docker images --filter "dangling=true" -q | wc -l
echo "dangling Images (können gelöscht werden)"

echo "=== Alte Backups ==="
find /opt/backups -name "*.gpg" -mtime +30 -print | wc -l
echo "Backups älter als 30 Tage"

echo "=== Docker Logs Grösse ==="
du -sh /var/lib/docker/containers/*/  2>/dev/null | sort -rh | head -5
```

Expectativa:
- Uso de disco abaixo de 85%
- Sem grandes quantidades de imagens Docker orfas
- Backups antigos sao limpos (retencao)

---

## M8: Monitoramento de manutencao no Audit-Runner

```bash
echo "=== Maintenance Check Cron auf audit-runner? ==="
ssh deploy@10.0.1.11 "crontab -l 2>/dev/null | grep maintenance" 2>/dev/null || \
  echo "WARNUNG: Kein Maintenance Check Cron auf audit-runner"

echo "=== Security Release Monitor Cron auf audit-runner? ==="
ssh deploy@10.0.1.11 "crontab -l 2>/dev/null | grep security-release" 2>/dev/null || \
  echo "WARNUNG: Kein Security Release Monitor Cron auf audit-runner"

echo "=== Trivy installiert auf audit-runner? ==="
ssh deploy@10.0.1.11 "trivy --version 2>/dev/null" || \
  echo "WARNUNG: Trivy nicht installiert auf audit-runner"

echo "=== Letzter Maintenance Report ==="
ssh deploy@10.0.1.11 "tail -20 /var/log/maintenance-check.log 2>/dev/null" 2>/dev/null || \
  echo "INFO: Maintenance Log nicht verfügbar"

echo "=== Letzter Security Release Report ==="
ssh deploy@10.0.1.11 "tail -20 /var/log/security-releases.log 2>/dev/null" 2>/dev/null || \
  echo "INFO: Security Release Log nicht verfügbar"

echo "=== E-Mail Alerting konfiguriert? ==="
ssh deploy@10.0.1.11 "which mail 2>/dev/null && echo 'mail Befehl verfügbar' || echo 'WARNUNG: mail nicht installiert'" 2>/dev/null
```

Expectativa:
- Cron de verificacao de manutencao no audit-runner ativo (semanal segunda-feira)
- Cron de monitor de releases de seguranca ativo (diario)
- Trivy instalado
- Relatorios recentes presentes
- Alerta por e-mail configurado

---

## M9: Configuracao de Auto-Patch

```bash
echo "=== Auto-Patch Script vorhanden? ==="
ls -la /opt/supabase/scripts/auto-patch.sh 2>/dev/null || echo "WARNUNG: auto-patch.sh fehlt"

echo "=== Auto-Patch Cron aktiv? ==="
crontab -l 2>/dev/null | grep auto-patch || echo "WARNUNG: Kein Auto-Patch Cron"

echo "=== Letzter Auto-Patch Lauf ==="
tail -20 /var/log/auto-patch.log 2>/dev/null || echo "INFO: Noch kein Auto-Patch gelaufen"

echo "=== Auto-Patch Ergebnisse ==="
grep -E "Patches eingespielt|Keine Patches|ABBRUCH|FEHLER" /var/log/auto-patch.log 2>/dev/null | tail -10

echo "=== Cron Zeitplan korrekt? ==="
echo "Erwartet:"
echo "  02:00 Backup"
echo "  03:00 Auto-Patch"
echo "  04:00 Restore-Test (monatlich)"
echo ""
echo "Aktuell:"
crontab -l 2>/dev/null | grep -E "backup|auto-patch|restore"
```

Expectativa:
- auto-patch.sh presente e executavel
- Cron: 03:00 diariamente (APOS o backup as 02:00)
- Logs recentes mostram execucoes bem-sucedidas
- Cron do backup DEVE executar antes do cron de auto-patch

---

## Resumo

Elabore agora um resumo neste formato:

```
# Auditoria Self-Hosting Supabase - [DATA]

## Resultado

APROVADO:  X de Y Checks (Y = aprox. 34 Checks: A1-A5, B1-B12, C1-C4, M1-M9)
AVISO:     X Checks
CRITICO:   X Checks

## Findings Criticos (agir imediatamente)
- ...

## Avisos (resolver nesta semana)
- ...

## Aprovados
- ...

## Visao Geral do Status dos Servicos
| Servico       | Status  | Finding                    |
|---------------|---------|----------------------------|
| PostgreSQL    | OK/AVISO | ...                       |
| Kong          | OK/AVISO | ...                       |
| GoTrue (Auth) | OK/AVISO | ...                       |
| PostgREST     | OK/AVISO | ...                       |
| Realtime      | OK/AVISO | ...                       |
| Storage/MinIO | OK/AVISO | ...                       |
| Analytics     | OK/AVISO | ...                       |
| Vector        | OK/AVISO | ...                       |
| Supavisor     | OK/AVISO | ...                       |
| Studio        | OK/AVISO | ...                       |

## Status de Atualizacoes
| Area                          | Ultima atualizacao | Idade  | Status   |
|-------------------------------|--------------------|--------|----------|
| Patches de seguranca do SO    | ...                | X dias | OK/AVISO |
| Imagens Supabase              | ...                | X dias | OK/AVISO |
| Docker Engine                 | v...               |        | OK/AVISO |
| Caddy                         | v...               |        | OK/AVISO |
| Certificado TLS               | ...                | X dias | OK/AVISO |

## Proximos Passos Recomendados
1. ...
2. ...
3. ...
```

Priorize estritamente: Findings criticos primeiro, depois avisos.
Para cada finding: Qual e o problema, por que e um risco, qual e a solucao.
