Webhooks

PromptShield envía notificaciones HTTP a tu URL cuando se bloquea un ataque o se detecta actividad sospechosa. Firmados con HMAC-SHA256 estilo Stripe.

Configuración

  1. Ve a dashboard → Webhooks y crea un endpoint.
  2. Selecciona los eventos a recibir.
  3. Guarda el signing_secret que aparece una sola vez.

Eventos disponibles

  • attack.blocked — Mensaje bloqueado por L1, L1.5 o L2.
  • attack.medium_risk — Mensaje sospechoso (no bloqueado).
  • output.blocked — Respuesta del LLM bloqueada por L5.

Payload

{
  "id": "evt_abc",
  "type": "attack.blocked",
  "created_at": "2025-05-02T19:00:00Z",
  "project_id": "prj_xxx",
  "data": {
    "request_id": "req_xyz",
    "message_hash": "sha256:...",
    "risk_level": "high",
    "category": "prompt_injection",
    "reason": "pattern_match",
    "layer_blocked": 1,
    "session_id": "sess-1",
    "user_id": "user-42"
  }
}

Verificar la firma (Node)

El header PromptShield-Signature tiene formato t=<unix>,v1=<hex>. Calcula HMAC-SHA256(secret, "${t}.${rawBody}") y compara con tiempo constante.

import crypto from 'node:crypto';

export function verify(rawBody: string, header: string, secret: string): boolean {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  const t = parts.t as string;
  const v1 = parts.v1 as string;
  if (!t || !v1) return false;
  const skew = Math.abs(Date.now() / 1000 - Number(t));
  if (skew > 300) return false; // 5 min tolerance
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${t}.${rawBody}`)
    .digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}

Verificar la firma (Python)

import hmac, hashlib, time

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    t, v1 = parts.get("t"), parts.get("v1")
    if not t or not v1: return False
    if abs(time.time() - int(t)) > 300: return False
    expected = hmac.new(
        secret.encode(),
        f"{t}.".encode() + raw_body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, v1)

Reintentos

Si tu endpoint no responde con 2xx en 10s, reintentamos hasta 5 veces con backoff: 1 min → 5 min → 30 min → 2 h → 12 h. Después marcamos el delivery como fallido.