
Contrasta las vistas de tu superficie de ataque y encuentra los endpoints que no pueden corroborarse entre sí.
Contrasta las vistas de tu superficie de ataque y encuentra los endpoints que no pueden corroborarse entre sí.
Un endpoint debería poder dar cuenta de sí mismo. Está en el código, así que un contrato debería describirlo. Está en el contrato, así que algo debería implementarlo. Recibe tráfico real, así que más vale que exista en algún lugar. Cuando una vista conoce un endpoint y las demás no, esa brecha es el hallazgo.
alibi ejecuta OWASP noir, lee su JSON y compara las vistas entre sí.
Noir ya lee cinco vistas independientes de la misma superficie:
| Vista | Leída desde |
|---|---|
| code | más de 200 analizadores en 33 lenguajes |
| doc | OpenAPI, RAML, WSDL, GraphQL SDL, AsyncAPI, gRPC, Smithy, TypeSpec, OData, OpenRPC |
| traffic | HAR, mitmproxy, Burp, Caido, ZAP, Postman, Insomnia, Bruno, .http |
| gateway | nginx, Apache, Envoy, Kong, Traefik, APISIX, Caddy, Istio, Kubernetes Ingress y Gateway API |
| infra | Terraform, CloudFormation, CDK, Serverless, Vercel, Netlify, Wrangler, Azure Functions, Kamal |
Lo que no hace es compararlas. Ese es todo el trabajo aquí, y no requiere ningún cambio en noir: alibi lo ejecuta una vez por vista y une los resultados.
La parte por vista importa. Noir deduplica por (method, url) entre todos los analizadores, de modo que una ruta de Flask y una ruta de OpenAPI escritas de forma idéntica colapsan en un único endpoint que porta una sola tecnología. Eso es correcto para una herramienta de descubrimiento —es un solo endpoint—, pero borra la corroboración que esta herramienta está construida para medir, y la borra en la peor dirección posible: cuanto mejor coinciden dos vistas, más de ellas desaparecen. Casdoor se escanea como 372 endpoints de código y 9 documentados; escanea solo su directorio swagger/ y la especificación tiene 235.
--only-techs restringe el conjunto de detectores, de modo que un escaneo por vista mantiene cada una completa. Qué tecnología habla por qué vista es views.yml; qué tecnologías existen es lo que reporte noir list techs.
alibi no analiza ningún formato de API por sí mismo. Su única entrada es el JSON de noir.
Requiere noir 1.0.0 o superior en el
PATH -- esa es la versión en la que noir list techs pasó a ser un subcomando, y
ese catálogo es lo que asigna cada tecnología a una vista. El desarrollo sigue la
versión actual de noir. Un binario más antiguo es rechazado por su nombre en lugar de dejarlo
fallar en su primera lectura del catálogo.
$ uv tool install noir-alibi # or: pipx install noir-alibi
$ alibi scan ./my-service
$ alibi scan # the working directory
$ alibi scan ./service ./contracts ./prod.har # or wherever the views live
Cada ruta es una fuente, escaneada una vez por vista. Apúntala a lo que tengas — un árbol de código fuente, un directorio de especificaciones, un único archivo de captura — y las vistas que te faltan desactivan sus reglas en lugar de inundar el informe.
alibi · 1 source · 377 endpoints
code 372 doc 235
230 corroborated -- vouched for by more than one view
19 endpoints nearly matched another view -- these may be matching failures, not real gaps
SHADOW Shadow API -- Implemented, but no contract describes it
134 findings · 4 critical, 57 high, 62 medium, 11 low
critical POST /api/upload-groups router.go:87
upload paths carry more consequence than reads
critical POST /api/upload-permissions router.go:208
...
... and 122 more (SHADOW in full: -f json)
TWO SURFACES?
The doc view is 97% under /api, and 37 of these findings are outside it.
If that is a separate surface the contract never covered, narrow the scan:
alibi scan <paths> --ignore '^/(?!api(/|$))'
If it is the same surface left undocumented, they are the findings that matter most.
Los grupos se detienen en doce — el orden es de peor a mejor, por lo que la cola es la parte menos informativa, y -f json lo tiene todo.
Los flags para noir van después de un -- solo o mediante --noir-arg. Filtros como --exclude-path están permitidos; los flags que reemplazarían el contrato JSON o colapsarían los escaneos por vista de alibi (--format, --diff-*, --only-techs, …) se rechazan con estado de salida 2.
- run: alibi scan . ./contracts -f sarif > alibi.sarif
- uses: github/codeql-action/upload-sarif@v3
with: { sarif_file: alibi.sarif }
El informe indica que las vistas no coinciden; --endpoints indica qué contenía cada una de ellas.
$ alibi scan ./repo -f json --endpoints
Cada vista obtiene una lista: la clave, qué vistas la respaldaron, las tecnologías detrás de ella, los archivos y la ortografía antes de la normalización — que es donde siempre está la diferencia cuando dos filas deberían haber coincidido y no lo hicieron. Es de tres a cuatro veces el resto de la carga útil, por lo que es una opción en lugar del valor predeterminado.
O aplicar la puerta directamente: alibi scan . ./contracts --fail-on high sale con un código distinto de cero cuando un hallazgo alcanza esa severidad. Una exploración que noir no pudo leer por completo informa executionSuccessful: false, por lo que una ejecución degradada no pasa como una limpia. Un enlace simbólico, binario o imagen que noir omitió a propósito no cuenta.
Noir conserva la sintaxis de rutas propia de cada framework en lugar de inventar una común, por lo que el mismo endpoint llega escrito de varias formas:
python_flask /api/users/<int:user_id>
aiohttp /users/{id}
java_spring /api/catalog/{id}
oas3 /v1/pets/{petId}
rails /posts/:id
nginx /admin/.*
La regla que hace que estos sean comparables: el nombre de un parámetro de ruta no forma parte de su identidad. {petId} y <int:user_id> describen el mismo espacio; solo importan su posición y si abarca un /. Los nombres se conservan como evidencia y se reportan, pero nunca llegan a la clave.
Los hallazgos indican cómo se hizo la coincidencia:
| Grado | Significado |
|---|---|
G1 | las grafías ya coincidían |
G2 | coinciden una vez normalizada la sintaxis de parámetros |
G0 | solo una vista lo tiene — no se emparejó nada |
Una herramienta como esta muere por reportar cientos de hallazgos en su primera ejecución, o por reportar progreso que nadie hizo. Seis cosas se oponen:
Las reglas no se disparan sin ambas vistas. Escanea un código base sin contratos en ningún lado y cada endpoint técnicamente califica como una API en la sombra no documentada. Esos hallazgos no dicen nada excepto que no proporcionaste ninguna documentación, así que una regla solo se ejecuta cuando cada vista sobre la que razona estaba realmente en el escaneo. El informe nombra las reglas que se quedaron fuera.
Los casi-aciertos se reportan como duda, no como hallazgos. "En el código, no en la documentación" es indistinguible de "en ambos, pero alibi no logró alinearlos". Así que un endpoint que cae en una vista se compara con las demás en busca de un casi-acierto — misma ruta con un verbo diferente, o a un segmento de distancia donde un lado tiene un parámetro y el otro un literal. Los hallazgos que conllevan un casi-acierto se degradan y se marcan para revisión. Ese recuento aparece junto a los totales, porque cada hallazgo es tan confiable como pequeño es.