
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) en todos los
analizadores, así que una ruta de Flask y una ruta de OpenAPI escritas de forma
idéntica colapsan en un solo endpoint que lleva una sola tecnología. Eso es
correcto para una herramienta de descubrimiento — es un 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, así 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 se convirtió en
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 # o: pipx install noir-alibi
$ alibi scan ./my-service
$ alibi scan # el directorio de trabajo
$ alibi scan ./service ./contracts ./prod.har # o donde vivan las vistas
Cada ruta es una fuente, escaneada una vez por vista. Apúntala a lo que tengas — un árbol de código, un directorio de especificaciones, un único archivo de captura — y las vistas que falten 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, así que la cola es
la parte menos informativa, y -f json la tiene toda.
Los flags para noir van después de un -- solo o a través de --noir-arg. Los
filtros como --exclude-path están bien; los flags que reemplazarían el
contrato JSON o colapsarían los escaneos por vista de alibi (--format,
--diff-*, --only-techs, …) son rechazados 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 dice que las vistas no coinciden; --endpoints dice qué contenía
cada una de ellas.
$ alibi scan ./repo -f json --endpoints
Cada vista obtiene una lista: la clave, qué vistas la corroboraron, las tecnologías detrás de ella, los archivos, y la escritura 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 del payload, así que es un flag en lugar del comportamiento por defecto.
O filtra directamente: alibi scan . ./contracts --fail-on high sale con un
código distinto de cero cuando un hallazgo alcanza esa severidad. Un escaneo que
noir no pudo leer por completo reporta executionSuccessful: false, así que una
ejecución degradada no pasa como una limpia.
Noir conserva la sintaxis de rutas propia de cada framework en lugar de inventar una común, así 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 comparables estas formas: el nombre de un parámetro de ruta
no forma parte de su identidad. {petId} y <int:user_id> describen la misma
posición; 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 dicen cómo se hizo la coincidencia:
| Grado | Significado |
|---|---|
G1 | las escrituras 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 sin contratos en ningún lado y cada endpoint califica técnicamente como una shadow API 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 abstuvieron.
Los casi-aciertos se reportan como duda, no como hallazgos. "En el código, no en los docs" 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 llevan un casi-acierto se degradan y se marcan para revisión. Ese conteo aparece junto a los totales, porque cada hallazgo es tan confiable como pequeño sea.