JustCyber

CondoShield

Condomínio com dezenas de câmeras, DVR e controle de acesso ligados na internet, e ninguém sabe quais estão expostos.

O CondoShield organiza o parque de IoT e CFTV do condomínio em três camadas: o site (o condomínio), os dispositivos daquele site e as varreduras que produzem achados. Tudo é tenant-scoped sob RLS FORCE, e o alvo da varredura vem sempre dos dispositivos cadastrados pelo próprio tenant — nunca de um endereço digitado livremente na requisição.

A varredura é passiva e conservadora por desenho. Antes de qualquer pacote, o guard de SSRF resolve o host e recusa qualquer IP privado, loopback, link-local ou de metadata de nuvem, incluindo o 169.254.169.254. Dispositivo marcado como não exposto à internet nem chega a ser tentado. Um dispositivo interno bloqueado é contado nas estatísticas como bloqueado — isso é o comportamento correto, não uma falha: o que está atrás do roteador só é alcançável por um appliance de borda, que não faz parte deste código. O motor é resiliente por construção: falha em um dispositivo vira uma entrada de erro e não derruba os demais nem a varredura.

O achado tem impressão digital estável derivada de dispositivo, categoria e local. Re-observar atualiza a linha existente em vez de criar duplicata, e só achado realmente novo acima do piso de severidade gera alerta. O score de risco é determinístico: peso da categoria multiplicado pelo fator de severidade, limitado entre 0 e 100. Trechos de evidência já saem redigidos pelo motor do Scan, que mascara credencial, chave PEM e JWT. O crédito segue hold-then-capture: reservado ao pedir, e o pipeline do worker captura no sucesso e libera em qualquer falha, com um beat cross-tenant que ceifa varredura travada em execução e devolve a reserva. Na prática, enquanto faltar o ramo do relay (ver limites), a varredura não sai de 'queued': a reserva fica aberta — nem capturada, nem devolvida.

O que faz hoje

Capacidades em operação

Cada item corresponde a funcionalidade que existe no código e é alcançável por um usuário com o escopo correto.

  • Inventário por site

    CRUD de sites (nome, endereço, administradora) e cadastro, leitura e edição de dispositivos (tipo, IP, MAC, fabricante, modelo, firmware, exposto à internet, status) — não há rota para remover um dispositivo: ele só sai em cascata, quando o site é apagado. Paginação por cursor. Quinze rotas ao todo, todas sob escopo próprio. Quinze rotas ao todo, todas sob escopo próprio.

  • Guard de SSRF obrigatório e fail-closed

    Reusa resolve_public_ips e run_passive_checks do módulo Scan. Host que resolve para IP interno é recusado antes de qualquer pacote e contabilizado como bloqueado. Concorrência limitada a seis dispositivos por vez.

  • Achados deduplicados por impressão digital

    Upsert com ON CONFLICT em (tenant, fingerprint): re-observação atualiza last_seen, severidade, título e evidência em vez de duplicar a linha.

  • Score de risco determinístico

    Valor de 0 a 100 calculado por peso de categoria (credencial padrão 90, acesso anômalo 80, firmware vulnerável 75, exposição 60, porta insegura 50, sem criptografia 40) vezes o multiplicador de severidade.

  • Triagem com transições validadas

    new pode ir para reviewed, confirmed ou dismissed; reviewed para confirmed ou dismissed; confirmed e dismissed são terminais. Transição inválida é recusada.

  • Crédito com hold-then-capture

    Reserva no pedido (preço padrão de 6 créditos, configurável), com Idempotency-Key e rate-limit por IP e tenant na rota de varredura. A captura no sucesso e a liberação na falha estão escritas no pipeline do worker, mas hoje não chegam a acontecer: sem o ramo do relay, a varredura não sai de 'queued' e a reserva permanece aberta (ver limites).

  • Reaper de varredura travada

    Beat cross-tenant na fila default marca como falha a varredura presa em execução além do teto e libera o hold pendente, com guarda idempotente e linha de auditoria.

O que fica com você

Entregáveis

  • Inventário de dispositivos por condomínio, com marcação do que está exposto à internet
  • "Lista de varreduras com o pedido registrado — hoje sempre em 'queued' e sem estatística, porque o motor não chega a rodar (ver limites)" e "Fila de achados, vazia enquanto a varredura não executar: o contrato já entrega severidade, categoria, score de risco e trecho de evidência redigido quando houver achado".
  • Fila de achados com severidade, categoria, score de risco e trecho de evidência já redigido

O que este módulo não faz

A varredura não chega a executar no estado atual do código. O POST /sites/{id}/scans cria o registro em 'queued', reserva o crédito e emite condoshield.scan.requested no outbox — mas o relay em worker/tasks/outbox.py::_enqueue não tem ramo para esse tópico: o evento é marcado como publicado e cai no caso default, sem consumidor. A task condoshield.scan existe, está registrada na fila scanning e delega ao pipeline correto, porém ninguém a enfileira. Resultado: a varredura fica em 'queued' e o reaper só ceifa as que estão em 'running', então o hold de crédito permanece aberto. Falta um ramo no relay. Não existe descoberta de dispositivos: o inventário é cadastrado à mão por POST /devices, não há varredura de rede que encontre câmeras sozinha. O motor produz apenas duas das seis categorias do vocabulário — o mapeamento traduz para exposicao_internet e sem_criptografia; credencial_padrao, firmware_vulneravel, acesso_anomalo e porta_insegura existem no schema e no peso do score, mas nenhum código as gera. Dispositivo de LAN não é avaliado: sem appliance de borda, o que está atrás do roteador fica fora, e o appliance é gancho externo que não existe no repositório. O campo modules do corpo é aceito e gravado, mas o motor não o lê: não há como escolher o que rodar. Varredura agendada não existe — o vocabulário aceita 'scheduled', toda criação grava 'manual' e nenhum beat cria varredura. O resumo executivo por IA está escrito em condoshield/ai.py e não tem chamador nem rota; o router sequer importa o módulo, então a task nem é registrada no gateway. O alerta de achado alto é uma linha de auditoria, não notificação: nada é enviado ao síndico.

Declaramos o limite porque software de segurança que promete tudo não é auditável. Se o que falta aqui é o que você precisa, o serviço consultivo cobre — ou dizemos que não cobre.

Conecta com

  • app.scan.passive (guard de SSRF, checagens HTTP/TLS/DNS e redação de evidência)
  • app.billing (hold, capture e release de créditos)
  • app.core.outbox (fato de domínio condoshield.scan.requested)
  • Celery: task condoshield.scan na fila isolada scanning e beat condoshield.reap na fila default
  • app.core.ratelimit e app.api.idempotency na rota de varredura
  • RLS FORCE nas tabelas condoshield_* (migration 0024)

Controle de acesso

O acesso é concedido por escopo, não por perfil genérico. Quem só precisa ler não recebe permissão de escrita, e a barreira é o banco de dados, não a interface.

  • condoshield:site:read
  • condoshield:site:write
  • condoshield:device:read
  • condoshield:device:write
  • condoshield:scan:read
  • condoshield:scan:write
  • condoshield:finding:read
  • condoshield:finding:write
CondoShield

Quer ver funcionando?

Demonstração com dado do seu ambiente, não com base de exemplo. É a única forma de saber se serve.