=== Teste E2E F2: Tools PHP (SqlQueryTool + SchemaTool) === Usuário: Gledson Cardoso (id 4) IsCeon: sim ▶ 1. SchemaTool: listar todo o catálogo ✅ retorna catálogo — 10 objetos ▶ 2. SchemaTool: buscar vw_contexto ✅ encontra vw_contexto — vw_contexto ▶ 3. SqlQueryTool: SELECT em vw_usuario ✅ retorna linhas — 1 linhas ▶ 4. SqlQueryTool: JOIN vw_usuario + vw_usuario_perfil ✅ JOIN funciona — 2 linhas ✅ perfil resolvido — Administrador ▶ 5. SqlQueryTool: bloqueia INSERT ✅ INSERT bloqueado — Consulta rejeitada: Somente consultas SELECT são permitidas. ▶ 6. SqlQueryTool: bloqueia DELETE ✅ DELETE bloqueado — Consulta rejeitada: Somente consultas SELECT são permitidas. ▶ 7. SqlQueryTool: bloqueia DROP ✅ DROP bloqueado — Consulta rejeitada: Somente consultas SELECT são permitidas. ▶ 8. SqlQueryTool: bloqueia coluna sensível (password_hash) ✅ password_hash bloqueado — Consulta rejeitada pela regra de EXCLUSÃO LÓGICA: o Privia inativa registros (status/active), não os apaga, e toda consulta deve considerar só os ativos. Falta filtrar: user_ceon → status. Inclua o filtro de CADA tabela do FROM/JOIN no WHERE ou no ON (ex.: s.status != 0, ou active = 1 quando a coluna for active; em tabelas cujo status tem etapas, como planos de ação, use status != 0). Só se o usuário pediu EXPLICITAMENTE por inativos/excluídos/histórico, repita com incluir_inativos=true. ▶ 9. SqlQueryTool: injeta LIMIT 2000 quando ausente ✅ LIMIT 2000 injetado — limite=2000 ▶ 10. SqlQueryTool: respeita limit=10 ✅ LIMIT 10 aplicado — limite=10 ▶ 11. SqlQueryTool: erro de sintaxe SQL ✅ erro de sintaxe detectado — Erro na consulta: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'vw_usuario LIMIT 5' at line 1 ▶ 12. SqlQueryTool: cadeia da nota coleta 3048 ✅ cadeia retorna dados — 20 linhas === Teste E2E F3: Isolamento de tenant (usuário não-Ceon) === (usuário 32515853048, isCeon=não, clientes=283) ▶ 13. Não-Ceon: vw_contexto COM escopo válido (cliente 283) ✅ consulta escopada liberada — 3 linhas ▶ 14. Não-Ceon: vw_contexto SEM escopo → negado ✅ consulta sem escopo negada — Acesso negado: Toda consulta exige escopo por cliente_id/contexto_id/coleta_id/projeto_id validados (não existe consulta sem escopo para este usuário). Inclua um filtro com um dos seus ids liberados. ▶ 15. Não-Ceon: vw_contexto com cliente de OUTRO tenant ✅ escopo de outro tenant negado — Acesso negado: Escopo por cliente_id=999 não autorizado para este usuário. Use apenas ids validados da seção IDS VALIDADOS. ▶ 16. Não-Ceon: vw_usuario sem escopo → negado ✅ vw_usuario sem escopo negada — Acesso negado: Toda consulta exige escopo por cliente_id/contexto_id/coleta_id/projeto_id validados (não existe consulta sem escopo para este usuário). Inclua um filtro com um dos seus ids liberados. ▶ 16b. Não-Ceon: lista de Administradores (vw_usuario + vw_usuario_perfil) sem escopo → negado ✅ lista de Administradores sem escopo negada — Acesso negado: Toda consulta exige escopo por cliente_id/contexto_id/coleta_id/projeto_id validados (não existe consulta sem escopo para este usuário). Inclua um filtro com um dos seus ids liberados. ▶ 17. Não-Ceon: tabela base user_ceon → negado ✅ tabela base negada — Consulta rejeitada pela regra de EXCLUSÃO LÓGICA: o Privia inativa registros (status/active), não os apaga, e toda consulta deve considerar só os ativos. Falta filtrar: user_ceon → status. Inclua o filtro de CADA tabela do FROM/JOIN no WHERE ou no ON (ex.: s.status != 0, ou active = 1 quando a coluna for active; em tabelas cujo status tem etapas, como planos de ação, use status != 0). Só se o usuário pediu EXPLICITAMENTE por inativos/excluídos/histórico, repita com incluir_inativos=true. ▶ 18. Não-Ceon: cadeia da nota (coleta 2791, cliente 283) ✅ cadeia escopada liberada — 0 linhas ▶ 19. Não-Ceon: OR 1=1 anulando escopo → negado ✅ OR anulando escopo negado — Acesso negado: Não é permitido usar OR em consultas de views de tenant. Escope via AND/IN com ids validados. ▶ 20. Não-Ceon: UNION anulando escopo → negado ✅ UNION negado — Acesso negado: UNION não é permitido em consultas com isolamento de tenant. ▶ 21. Não-Ceon: LEFT JOIN para vazar usuários → negado ✅ LEFT JOIN negado — Acesso negado: JOIN externo (LEFT/RIGHT/FULL) não é permitido com isolamento de tenant. Use apenas INNER JOIN. (usuário 05354111110, isCeon=não, clientes=294) ▶ 22. REGRESSÃO: lista de Administradores (Resp Tran) sem escopo → negado ✅ Administradores sem escopo negado — Acesso negado: Toda consulta exige escopo por cliente_id/contexto_id/coleta_id/projeto_id validados (não existe consulta sem escopo para este usuário). Inclua um filtro com um dos seus ids liberados. ▶ 23. REGRESSÃO: listar todos os usuários (Resp Tran) → negado ✅ lista geral de usuários negada — Acesso negado: Toda consulta exige escopo por cliente_id/contexto_id/coleta_id/projeto_id validados (não existe consulta sem escopo para este usuário). Inclua um filtro com um dos seus ids liberados. ▶ 24. Resp Tran: usuários da Transtusa com escopo (contexto_id IN) → liberado ✅ usuários escopados liberados — 7 linhas ▶ 25. REGRESSÃO: perfil_usuarios (Resp Tran) escopado — não vaza usuários globais ✅ lista escopada (sem admins globais) — 9 usuários; IDs 1052,1057,1092,1104,1220,1238,1334,1336… ✅ quantidade <= scope (não 142 globais) — total=9 ✅ usuário logado incluso — 9 usuários ▶ 26. REGRESSÃO: gerar_arquivo fonte perfil_usuarios (Resp Tran) escopado ✅ arquivo gerado sem erro — linhas=9 ✅ anexo com token criado — nome=teste_escopo.txt ✅ arquivo sem usuários de outro tenant (anexo no runtime) — caminho indisponível no teste (arquivo guardado no runtime do app) === RESULTADO: 32 passaram, 0 falharam ===