#!/bin/sh
#
# Remediação do Dia 0 — passos 1, 2 e a apuração do 6.
#
#   sh scripts/remediacao-dia0.sh              audita; não move arquivo nenhum
#   sh scripts/remediacao-dia0.sh --aplicar    move os arquivos para quarentena
#
# O modo padrão é auditoria de propósito: rode primeiro sem --aplicar, leia o
# relatório, e só então aplique. Os dois modos gravam docs/incidente-lgpd-dia0.md,
# porque a apuração é leitura de log e interessa registrar de qualquer maneira.
#
# O que este script NÃO faz, porque exige julgamento humano: o deploy (passo 4),
# a rotação de credenciais (passo 5) e a criação do local.php (passo 3).
#
# Nada é apagado. Os arquivos vão para uma quarentena datada, e o relatório de
# apuração é gravado para servir de evidência — inclusive quando dá vazio, que
# é o resultado que interessa registrar.

set -eu

RAIZ="${RAIZ:-$(pwd)}"
LOGS_NGINX="${LOGS_NGINX:-/var/log/nginx}"
DOMINIO="${DOMINIO:-franquiabv.com.br}"
QUARENTENA="${QUARENTENA:-$HOME/quarentena-dia0}"

APLICAR=0
[ "${1:-}" = "--aplicar" ] && APLICAR=1

# Arquivos que o nginx serve diretamente por estarem dentro do webroot.
EXPOSTOS="clientes.txt phpinfofile.php log_scripts.txt logs"

achados=0

titulo() {
    echo ""
    echo "== $1"
}

# ---------------------------------------------------------------------------

if [ ! -d "$RAIZ/public" ]; then
    echo "ERRO: $RAIZ/public não existe — este não parece ser o diretório do sistema." >&2
    echo "Rode a partir da raiz do projeto, ou defina RAIZ=/caminho/do/projeto." >&2
    exit 1
fi

echo "Remediação do Dia 0"
echo "raiz:       $RAIZ"
echo "logs nginx: $LOGS_NGINX"
if [ "$APLICAR" -eq 1 ]; then
    echo "modo:       APLICAR (os arquivos serão movidos)"
else
    echo "modo:       auditoria (nenhum arquivo será movido)"
fi

# --- Passo 1 ---------------------------------------------------------------

titulo "Passo 1 — arquivos com dados pessoais no webroot"

presentes=""

for alvo in $EXPOSTOS; do
    if [ -e "$RAIZ/public/$alvo" ]; then
        tamanho=$(du -sh "$RAIZ/public/$alvo" 2>/dev/null | cut -f1)
        echo "   PRESENTE  public/$alvo  ($tamanho)"
        presentes="$presentes $alvo"
        achados=$((achados + 1))
    else
        echo "   ausente   public/$alvo"
    fi
done

if [ -n "$presentes" ] && [ "$APLICAR" -eq 1 ]; then
    destino="$QUARENTENA/$(date +%Y%m%d-%H%M%S)"
    mkdir -p "$destino"
    chmod 700 "$QUARENTENA" "$destino"

    for alvo in $presentes; do
        mv "$RAIZ/public/$alvo" "$destino/"
        echo "   movido    public/$alvo -> $destino/$alvo"
    done

    echo ""
    echo "   Quarentena em $destino (permissão 700, fora do webroot)."
    echo "   Confira o conteúdo antes de apagar em definitivo."
elif [ -n "$presentes" ]; then
    echo ""
    echo "   Para mover: sh scripts/remediacao-dia0.sh --aplicar"
fi

# --- Passo 6 (verificação) -------------------------------------------------

titulo "Passo 6 — endpoint público de migração de senhas"

# NÃO probe a rota por HTTP. Ela sobrescreve o hash de senha de todos os
# usuários ativos, e um GET — ou um HEAD, que o PHP executa igual, apenas
# descartando o corpo — DISPARA a ação. Verificar pela web seria causar o
# incidente que se quer detectar.
#
# A verificação segura é no código implantado: se a rota não está declarada,
# não existe.
if grep -rqE "migrar-senhas|migrarSenhas" "$RAIZ/module" "$RAIZ/config" 2>/dev/null; then
    echo "   ROTA PRESENTE no código implantado."
    echo ""
    grep -rnE "migrar-senhas|migrarSenhas" "$RAIZ/module" "$RAIZ/config" 2>/dev/null \
        | sed "s|$RAIZ/||" | sed 's/^/      /'
    echo ""
    echo "   Qualquer pessoa na internet pode sobrescrever a senha de todos os"
    echo "   usuários ativos. Se o deploy for demorar, bloqueie no nginx AGORA —"
    echo "   ver docs/dia0-remediacao-producao.md, passo 6."
    achados=$((achados + 1))
else
    echo "   Rota ausente do código implantado."
fi

# --- Passo 2 — apuração ----------------------------------------------------

titulo "Passo 2 — apuração de acesso externo (LGPD)"

relatorio="$RAIZ/docs/incidente-lgpd-dia0.md"

# O DIRETORIO QUE NAO EXISTE NAO E "NADA PENDENTE" (a correcao da fatia 128, o
# D332). Ate entao este caso so imprimia o aviso: nao gravava relatorio (o de
# uma rodada anterior ficava la), nao contava achado e saia 0 -- com o passo 1
# e o 6 limpos, o resumo dizia "Nada pendente nos passos 1, 2 e 6" sem ter
# lido log nenhum. Agora a apuracao segue com zero arquivos: o controle
# positivo da zero, o relatorio diz "nao li o log do site" e o roteiro sai 2.
diretorio_ausente=0
if [ ! -d "$LOGS_NGINX" ]; then
    echo "   $LOGS_NGINX não existe ou não está acessível."
    echo "   Rode como root, ou defina LOGS_NGINX=/caminho/dos/logs."
    diretorio_ausente=1
fi

{
    # Um arquivo temporário para não gravar relatório pela metade se algo falhar;
    # e as duas listas: os arquivos lidos inteiros e os que nao se leem.
    tmp="$(mktemp)"
    legiveis="$(mktemp)"
    ilegiveis="$(mktemp)"
    trap 'rm -f "$tmp" "$legiveis" "$ilegiveis"' EXIT

    # OS LOGS DO SITE, E NAO SO O access.log (D332, desde 08/11/2026). O site
    # tem log proprio por vhost (o D36: a coleta que olhou so `access.log*`
    # achou zero pedidos a /api/versao/app, e a que varreu `*.log` achou
    # 11.032), e esta apuracao procurava so em `access.log*` -- o "nenhum acesso
    # externo" que sustenta nao notificar a ANPD podia ser o arquivo errado.
    # Agora ela le todo `*.log` e `*.log.*` do diretorio (o `.gz` pelo zgrep),
    # como o varrer() do `coletar-producao.sh`, menos os de erro: eles nao
    # registram pedido atendido, e a primeira coluna deles e a data, nao o IP.
    arquivos_de_log() {
        for f in "$LOGS_NGINX"/*.log "$LOGS_NGINX"/*.log.*; do
            [ -f "$f" ] || continue
            case "${f##*/}" in *error*) continue ;; esac
            echo "$f"
        done
    }

    # CADA ARQUIVO LIDO INTEIRO, OU NA LISTA DOS QUE NAO LI (a correcao da fatia
    # 128). A busca abaixo cala o erro do zgrep, e o controle positivo e um so,
    # somado sobre todos os arquivos: o `.gz` da semana do incidente truncado
    # ("unexpected end of file"), ou sem permissao para quem roda, saia no
    # relatorio como "lido" e a conclusao cobria o periodo dele sem te-lo lido.
    # O zgrep nao serve de prova: o do macOS sai 0 no `.gz` truncado, com o que
    # leu ate o corte. Por isso o `gzip -t` (le o arquivo inteiro e confere o
    # CRC), o `[ -r ]` no comum, e a compressao que o zgrep nao abre fica de fora.
    le_inteiro() {
        [ -r "$1" ] || return 1
        case "$1" in
            *.gz) gzip -t "$1" > /dev/null 2>&1 ;;
            *.bz2|*.xz|*.zst|*.lz4|*.Z) return 1 ;;
            *) cat "$1" > /dev/null 2>&1 ;;
        esac
    }

    arquivos_de_log | while IFS= read -r f; do
        if le_inteiro "$f"; then
            echo "$f" >> "$legiveis"
        else
            echo "$f" >> "$ilegiveis"
        fi
    done

    varrer() {
        while IFS= read -r f; do
            case "$f" in
                *.gz) zgrep -hF -- "$1" "$f" 2>/dev/null || true ;;
                *) grep -hF -- "$1" "$f" 2>/dev/null || true ;;
            esac
        done < "$legiveis"
    }

    # O CONTROLE POSITIVO (D332): a mesma busca, sobre a rota que o aplicativo
    # chama a cada abertura, tem de achar mais que zero. Zero quer dizer que o
    # log do site nao foi lido -- e entao "nenhum acesso" nao vale nada: o
    # relatorio diz "nao li o log do site", e o roteiro sai com 2.
    lidos=$(wc -l < "$legiveis" | tr -d ' ')
    nao_lidos=$(wc -l < "$ilegiveis" | tr -d ' ')
    controle=$(varrer '/api/versao/app' | wc -l | tr -d ' ')
    nao_li=0
    [ "$controle" -gt 0 ] || nao_li=1

    {
        echo "# Apuração de incidente — Dia 0"
        echo ""
        echo "Gerado em $(date '+%d/%m/%Y %H:%M:%S') por scripts/remediacao-dia0.sh."
        echo ""
        echo "Busca por acessos externos aos arquivos expostos e ao endpoint de"
        echo "migração de senhas, nos logs retidos em \`$LOGS_NGINX\`."
        echo ""
        echo "**A ausência de acessos também é um registro que interessa** — é ela"
        echo "que sustenta a decisão de não notificar a ANPD."
        echo ""
        if [ "$diretorio_ausente" -eq 1 ]; then
            echo "**O diretório \`$LOGS_NGINX\` não existe ou não está acessível** a quem rodou."
            echo ""
        fi
        echo "Arquivos lidos: $lidos (todo \`*.log\` e \`*.log.*\`, o \`.gz\` incluído; os de erro ficam de fora):"
        echo ""
        while IFS= read -r f; do echo "- \`${f##*/}\`"; done < "$legiveis"
        echo ""
        if [ "$nao_lidos" -gt 0 ]; then
            echo "**Arquivos que NÃO li: $nao_lidos.** O \`.gz\` truncado ou corrompido (o \`gzip -t\` recusa),"
            echo "sem permissão para quem rodou, ou numa compressão que este roteiro não abre. A apuração"
            echo "abaixo **não cobre o período deles**: leia-os como root (\`zcat\`) e rode de novo."
            echo ""
            while IFS= read -r f; do echo "- \`${f##*/}\`"; done < "$ilegiveis"
            echo ""
        fi
        echo "**Controle positivo:** \`/api/versao/app\`, que o aplicativo chama a cada abertura, aparece $controle vez(es)."
        if [ "$nao_li" -eq 1 ]; then
            echo ""
            echo "**NÃO LI O LOG DO SITE.** O controle deu zero: estes arquivos não são os do site,"
            echo "e a ausência de acesso abaixo não vale como registro. Aponte \`LOGS_NGINX=\` para o"
            echo "diretório dos logs do vhost e rode de novo."
        fi
        echo ""
    } > "$tmp"

    if [ "$nao_li" -eq 1 ]; then
        echo "   NAO LI O LOG DO SITE: /api/versao/app aparece 0 vez(es) em $lidos arquivo(s) de $LOGS_NGINX."
        achados=$((achados + 1))
    else
        echo "   controle positivo: /api/versao/app aparece $controle vez(es) em $lidos arquivo(s)"
    fi

    if [ "$nao_lidos" -gt 0 ]; then
        echo "   NAO LI $nao_lidos ARQUIVO(S) DE LOG (truncado, sem permissao ou compressao desconhecida):"
        while IFS= read -r f; do echo "      $f"; done < "$ilegiveis"
        achados=$((achados + 1))
    fi

    total_externos=0

    for termo in clientes.txt phpinfofile log_scripts migrar-senhas; do
        echo "" >> "$tmp"
        echo "## $termo" >> "$tmp"
        echo "" >> "$tmp"

        # 127.0.0.1 e ::1 são o próprio servidor.
        encontrados=$(varrer "$termo" | grep -v "^127\.0\.0\.1 " | grep -v "^::1 " || true)

        if [ -z "$encontrados" ] && [ "$nao_li" -eq 1 ]; then
            echo "Não apurado: o log do site não foi lido (o controle positivo deu zero)." >> "$tmp"
            echo "   $termo: NAO APURADO (o log do site nao foi lido)"
        elif [ -z "$encontrados" ] && [ "$nao_lidos" -gt 0 ]; then
            echo "Nenhum acesso externo nos arquivos lidos; $nao_lidos arquivo(s) não lido(s) (a lista no começo)." >> "$tmp"
            echo "   $termo: nenhum acesso externo nos arquivos lidos ($nao_lidos nao lido(s))"
        elif [ -z "$encontrados" ]; then
            echo "Nenhum acesso externo registrado." >> "$tmp"
            echo "   $termo: nenhum acesso externo"
        else
            n=$(echo "$encontrados" | wc -l | tr -d ' ')
            total_externos=$((total_externos + n))
            achados=$((achados + 1))

            if [ "$n" -eq 1 ]; then
                echo "**1 requisição externa.** IP:" >> "$tmp"
            else
                echo "**$n requisições externas.** IPs por volume:" >> "$tmp"
            fi
            echo "" >> "$tmp"
            echo '```' >> "$tmp"
            echo "$encontrados" | awk '{print $1}' | sort | uniq -c | sort -rn >> "$tmp"
            echo '```' >> "$tmp"
            echo "" >> "$tmp"
            echo "<details><summary>Requisições completas</summary>" >> "$tmp"
            echo "" >> "$tmp"
            echo '```' >> "$tmp"
            echo "$encontrados" >> "$tmp"
            echo '```' >> "$tmp"
            echo "" >> "$tmp"
            echo "</details>" >> "$tmp"

            echo "   $termo: $n ACESSO(S) EXTERNO(S)"
        fi
    done

    {
        echo ""
        echo "---"
        echo ""
        if [ "$total_externos" -eq 0 ] && [ "$nao_li" -eq 1 ]; then
            echo "## Conclusão preliminar"
            echo ""
            echo "**Nenhuma conclusão: o log do site não foi lido.** O controle positivo deu"
            echo "zero, e \"nenhum acesso\" aqui seria a comparação contra o vazio. Isto não"
            echo "sustenta a decisão de não notificar a ANPD."
        elif [ "$total_externos" -eq 0 ] && [ "$nao_lidos" -gt 0 ]; then
            echo "## Conclusão preliminar"
            echo ""
            echo "**Nenhuma conclusão sobre o período inteiro:** nenhum acesso externo nos arquivos"
            echo "lidos, mas $nao_lidos arquivo(s) não foram lidos (a lista no começo), e o período"
            echo "deles fica de fora. Isto não sustenta, sozinho, a decisão de não notificar a ANPD."
        elif [ "$total_externos" -eq 0 ]; then
            echo "## Conclusão preliminar"
            echo ""
            echo "Nenhum acesso externo nos logs retidos. Atenção ao alcance desta"
            echo "afirmação: ela cobre apenas o período de retenção do nginx, não toda"
            echo "a vida dos arquivos. Registre há quanto tempo os logs são mantidos."
        else
            echo "## Conclusão preliminar"
            echo ""
            echo "**$total_externos requisição(ões) externa(s) registrada(s).** Separe os IPs de"
            echo "robôs de busca conhecidos dos demais e **escale para o jurídico**: o"
            echo "prazo de comunicação à ANPD é curto."
            echo ""
            echo "Se houver acessos a \`migrar-senhas\`, há risco de senhas terem sido"
            echo "revertidas — force a redefinição de senha de todos os colaboradores."
            if [ "$nao_lidos" -gt 0 ]; then
                echo ""
                echo "E $nao_lidos arquivo(s) não foram lidos (a lista no começo): a contagem pode ser maior."
            fi
        fi
    } >> "$tmp"

    mkdir -p "$RAIZ/docs"
    mv "$tmp" "$relatorio"
    rm -f "$legiveis" "$ilegiveis"
    trap - EXIT

    echo ""
    echo "   Relatório gravado em docs/incidente-lgpd-dia0.md"
}

# ---------------------------------------------------------------------------

titulo "Resumo"

if [ "$achados" -eq 0 ]; then
    echo "   Nada pendente nos passos 1, 2 e 6."
else
    echo "   $achados item(ns) exigem ação — releia as seções acima."
fi

echo ""
echo "   Ainda manuais: passo 3 (local.php), passo 4 (deploy), passo 5 (rotação)."
echo "   Antes do deploy, rode: php scripts/verificar-local-php.php"
echo ""

# A APURACAO SEM O LOG DO SITE NAO E RESULTADO (D332): sai com 2, "nao
# consegui medir", para nenhum roteiro que chame este ler o relatorio como limpo.
# E A APURACAO COM ARQUIVO QUE NAO SE LEU TAMBEM NAO (a correcao da fatia 128):
# o relatorio tem o que leu, mas nao cobre o periodo do arquivo de fora.
if [ "${nao_li:-0}" -eq 1 ]; then
    echo "   SAIDA 2: a apuração do passo 2 não leu o log do site (o controle positivo deu zero)."
    exit 2
fi
if [ "${nao_lidos:-0}" -gt 0 ]; then
    echo "   SAIDA 2: a apuração do passo 2 não leu $nao_lidos arquivo(s) de log; o relatório não cobre o período deles."
    exit 2
fi
