Documentação
Monitoramento e alertas de mudanças
O AppSafe compara cada escaneamento de um site verificado com o anterior e informa o que mudou. Com escaneamentos agendados ou reescaneamento no deploy, isso vira um monitoramento contínuo com alertas por e-mail, Slack, Discord ou um webhook assinado.
Os alertas de mudanças estão incluídos nos planos Pro, Agency e Team. Os escaneamentos agendados rodam diária ou semanalmente no Pro e no Agency, e a cada hora no Team.
O que o AppSafe compara
- Achados novos e resolvidos.
- Cabeçalhos de segurança adicionados, removidos ou enfraquecidos (por exemplo, um Content-Security-Policy removido ou um max-age do HSTS mais curto).
- Substituições do certificado TLS e avisos de expiração 14 e 3 dias antes de o certificado expirar.
- Novos domínios de scripts de terceiros e scripts externos sem Subresource Integrity.
- Novos subdomínios do seu domínio nos logs de Certificate Transparency (crt.sh) e subdomínios cujo CNAME aponta para um recurso excluído em um serviço de hospedagem. Usamos apenas consultas DNS passivas.
- Mudanças nos registros SPF, DMARC, CAA e MX.
Os alertas são enviados nos escaneamentos agendados e no deploy, uma vez por problema: um problema que continua igual não gera um novo alerta no próximo escaneamento. Se ele sumir e voltar, você será alertado de novo.
Configure os alertas
Abra um site verificado no seu painel e adicione canais em Monitoramento e alertas. Cada canal tem uma severidade mínima e um botão Enviar alerta de teste.
- E-mail: Qualquer endereço de e-mail. Os alertas são escritos no idioma da sua conta.
- Slack: Uma URL de incoming webhook de um app do Slack (https://hooks.slack.com/services/…).
- Discord: Uma URL de webhook do canal (Configurações do servidor → Integrações → Webhooks).
- Webhook: Qualquer endpoint https. O AppSafe envia por POST o payload JSON abaixo, assinado com um segredo exibido uma única vez quando você cria o canal.
Payload do webhook
Webhooks genéricos recebem este JSON (Content-Type: application/json). Os alertas de teste usam o evento monitoring.test e têm scan definido como null.
{
"event": "monitoring.changes",
"deliveryId": "5f2c9a0e41b7d3a8c6e1f0b2",
"createdAt": "2026-10-26T12:00:00.000Z",
"site": {
"domain": "myapp.com"
},
"scan": {
"id": "3f1c2b9e-1111-4222-8333-944455556666",
"grade": "C",
"score": 67,
"reportUrl": "https://appsafe.dev/report/…"
},
"summary": "2 changes on myapp.com",
"changes": [
{
"type": "header_removed",
"severity": "high",
"subject": "content-security-policy",
"message": "content-security-policy header removed"
},
{
"type": "finding_new",
"severity": "critical",
"subject": "baas-supabase-table-readable",
"title": "Supabase tables readable without logging in",
"message": "New issue: Supabase tables readable without logging in"
}
]
}Tipos de mudança: finding_new, finding_resolved, header_added, header_removed, header_weakened, header_changed, tls_changed, tls_expiring (com days), script_domain_new, sri_missing, subdomain_new, subdomain_dangling, dns_changed. Severidades: low, medium, high, critical. Os alertas incluem apenas regressões, então finding_resolved e header_added aparecem no painel e no relatório, mas não disparam alertas sozinhos.
Verifique a assinatura
Toda requisição de webhook tem um cabeçalho X-AppSafe-Signature. Calcule um HMAC-SHA256 com o segredo do seu canal sobre o timestamp, um ponto e o corpo bruto da requisição, e compare com v1 em tempo constante. Rejeite requisições cujo timestamp tenha mais de 5 minutos.
X-AppSafe-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">
X-AppSafe-Event: monitoring.changes | monitoring.testimport { createHmac, timingSafeEqual } from "node:crypto";
// header: the X-AppSafe-Signature value, e.g. "t=1761480000,v1=5d41…"
// body: the raw request body (string), before JSON.parse
export function verifyAppSafeSignature(secret, header, body) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const timestamp = Number(parts.t);
if (!parts.v1 || Math.abs(Date.now() / 1000 - timestamp) > 300) return false;
const expected = createHmac("sha256", secret).update(`${timestamp}.${body}`).digest();
const given = Buffer.from(parts.v1, "hex");
return given.length === expected.length && timingSafeEqual(given, expected);
}O AppSafe envia cada alerta uma única vez e não tenta de novo. Responda com um status 2xx em até 8 segundos. O painel mostra o último erro de envio do canal.
O que os alertas nunca contêm
Os payloads dos alertas contêm tipos de mudança, severidades, títulos de achados, nomes de cabeçalhos, hostnames de scripts e subdomínios, e um link para o relatório. Eles nunca contêm detalhes dos achados, segredos (nem mesmo mascarados) ou dados pessoais.