codaprobe: prueba de la app viva

codaprobe prueba una aplicación viva por la red, solo en los objetivos que un archivo de alcance autoriza; la ambigüedad en el alcance es rechazo.

Antes de que salga cualquier paquete, el objetivo debe estar en la allowlist del alcance. Por eso esta guía empieza por la autorización.

Licencia. En el binario público, codaprobe requiere licencia Pro, Verified o Platform (Vibe no incluye runtime). Sin ella, sale con código 40 (el 3 es rechazo de alcance). check-scope, schema y verify-audit siguen libres. Antes del lanzamiento todavía no se acepta ninguna licencia: el binario público sale con 40 incluso con token. Ver planes.

1. Autorización y alcance (lee antes de correr)

El alcance es un JSON que declara quién autorizó y qué está autorizado:

{
  "authorization": {
    "attestation": "pentest autorizado — ticket SEC-4210, aprobado por Maria Silva (CISO) el 2026-07-20",
    "granted": true
  },
  "entries": [
    { "host": "app.cliente.test", "port": 443, "path_prefix": "/api", "mutating_opt_in": false }
  ]
}
CampoSignificadoSi está mal
attestationtexto libre de procedencia (quién, ticket, fecha); va al informeel informe pierde valor como evidencia
grantedel operador declara la autorización; ausente = falsetodo se rechaza
host · portobjetivo en forma canónica, puerto explícitoforma no canónica reprueba la carga; un puerto distinto se rechaza
path_prefixprefijo casado por segmento completo (/api cubre /api/x, no /apix); obligatoriouna clave mal escrita reprueba la carga, en lugar de autorizar el host entero
mutating_opt_inlibera POST/PUT/PATCH/DELETE en este objetivo; ausente = falseel método mutante se rechaza antes de la red

Un campo desconocido reprueba la carga: un error de tipeo no puede convertirse en permiso. Verifica sin tocar la red:

codaprobe check-scope --scope alcance.json --url https://app.cliente.test/api/

2. Escanear

# pasivo — solo el tráfico legítimo, ningún payload (headers, cookies, CORS, fugas)
codaprobe scan --scope alcance.json --url https://app.cliente.test/api > informe.json

# activo, dirigido por el contrato (OpenAPI 3 JSON/YAML, colección Postman o HAR)
codaprobe scan --scope alcance.json --url https://app.cliente.test/api \
  --contract openapi.yaml --active --auth-file auth.txt --audit-out audit.json > informe.json

# sin contrato: descubre la superficie por crawl (sin navegador)
codaprobe scan --scope alcance.json --url https://app.cliente.test --crawl --active > informe.json

# GraphQL a partir de la introspección
codaprobe scan --scope alcance.json --url https://app.cliente.test/graphql --graphql introspection.json --active

El modo activo prueba query, header, cookie, formulario, segmento de path y el nombre del parámetro. Los payloads son benignos: revelan la falla sin explotarla. Para fallas sin respuesta visible está --temporal; la detección out-of-band (--oob) viene apagada por defecto.

Flags que cambian el resultado: --rps <n> (cadencia total, por defecto 5), --concorrencia <n> (concurrencia), --fail-on <sev> (reprueba solo por finding confirmado), --ca-cert <pem> (prefiérelo a --insecure, que también apaga la verificación de hostname), --correlate-src <envelope> (cruza con el envelope de codafort).

3. El informe y el registro de auditoría

El informe es coda-dast/1: findings moment: run, URL redactada (sin userinfo ni query), el texto de la autorización y el ancla del registro de auditoría. El registro (--audit-out) está encadenado por SHA-256 y se verifica así:

codaprobe verify-audit --file audit.json --report informe.json    # exit 0 INTACT · 21 TAMPERED

Con --report (o --expect-anchor), el comando verifica el registro contra el ancla del informe y responde INTACT (exit 0) o TAMPERED (exit 21). Sin ellos, el mejor veredicto posible es CONSISTENT: quien edita el registro puede recalcular la cadena, y solo el ancla del informe detecta una remoción con la cadena rehecha. Verifica siempre con el informe.

4. En el pipeline

codaprobe scan --scope alcance.json --url "$OBJETIVO" --contract openapi.yaml --active \
  --auth-file auth.txt --rps 10 --fail-on high --audit-out audit.json > informe.json

Solo un finding confirmado reprueba el build; un candidato no.

ExitSignificado
0corrió (los findings no son error)
1--fail-on reprobó
3objetivo rechazado por el alcance
20entrada inválida (alcance, contrato, credencial, --rps absurdo)
21verify-audit: cadena rota o ancla que no coincide
30error de I/O o de red al arrancar
40sin licencia (ver arriba)

Lo que nunca hace

No relaja el alcance, el modo no destructivo ni la redacción de la URL para encontrar más fallas. No guarda secretos en el informe.