Recoupez les vues de votre surface d'attaque et identifiez les points de terminaison qui ne peuvent pas se corroborer mutuellement.
Recoupez les vues de votre surface d'attaque et trouvez les endpoints qui ne peuvent pas se corroborer mutuellement.
Un endpoint devrait pouvoir rendre compte de lui-même. Il est dans le code, donc un contrat devrait le décrire. Il est dans le contrat, donc quelque chose devrait l'implémenter. Il reçoit du trafic réel, donc il ferait mieux d'exister quelque part. Quand une vue connaît un endpoint et que les autres non, cet écart est le constat.
alibi exécute OWASP noir, lit son JSON et compare les vues entre elles.
Noir lit déjà cinq vues indépendantes de la même surface :
| Vue | Lue depuis |
|---|---|
| code | plus de 200 analyseurs couvrant 33 langages |
| 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 et Gateway API |
| infra | Terraform, CloudFormation, CDK, Serverless, Vercel, Netlify, Wrangler, Azure Functions, Kamal |
Ce qu'il ne fait pas, c'est les comparer. C'est tout le travail ici, et cela ne nécessite aucune modification de noir — alibi l'exécute une fois par vue et joint les résultats.
La partie par vue est importante. Noir déduplique par (method, url) sur tous les analyseurs, donc une route Flask et un chemin OpenAPI orthographiés identiquement fusionnent en un seul endpoint portant une seule technologie. C'est correct pour un outil de découverte — c'est un seul endpoint — mais cela efface la corroboration que cet outil est conçu pour mesurer, et l'efface dans la pire direction possible : mieux deux vues s'accordent, plus nombreuses sont celles qui disparaissent. Casdoor se scanne en 372 endpoints de code et 9 documentés ; scannez son répertoire swagger/ seul et la spécification en compte 235.
--only-techs restreint le pool de détecteurs, donc un scan par vue garde chacune entière. Quelle technologie parle pour quelle vue est défini dans views.yml ; quelles technologies existent est ce que rapporte noir list techs.
alibi ne parse aucun format d'API par lui-même. Sa seule entrée est le JSON de noir.
Nécessite noir 1.0.0 ou plus récent dans le PATH — c'est la version où noir list techs est devenu une sous-commande, et ce catalogue est ce qui assigne chaque technologie à une vue. Le développement suit la version actuelle de noir. Un binaire plus ancien est refusé par son nom plutôt que laissé échouer à sa première lecture de catalogue.
$ 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
Chaque chemin est une source, scannée une fois par vue. Pointez-le vers ce que vous avez — une arborescence de sources, un répertoire de spécifications, un seul fichier de capture — et les vues manquantes désactivent leurs règles plutôt que d'inonder le rapport.
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.
Les groupes s'arrêtent à douze — l'ordre va du pire au meilleur, donc la queue est la partie la moins informative, et -f json contient tout.
Les flags destinés à noir se placent après un -- seul ou via --noir-arg. Les filtres tels que --exclude-path sont acceptés ; les flags qui remplaceraient le contrat JSON ou écraseraient les scans par vue d'alibi (--format, --diff-*, --only-techs, …) sont refusés avec un statut de sortie 2.
- run: alibi scan . ./contracts -f sarif > alibi.sarif
- uses: github/codeql-action/upload-sarif@v3
with: { sarif_file: alibi.sarif }
Le rapport dit que les vues divergent ; --endpoints dit ce que chacune contenait.
$ alibi scan ./repo -f json --endpoints
Chaque vue obtient une liste : la clé, quelles vues l'ont corroborée, les technologies derrière elle, les fichiers, et l'orthographe avant normalisation — c'est là que se trouve toujours la différence quand deux lignes auraient dû correspondre et ne l'ont pas fait. Cela représente trois à quatre fois le reste de la charge utile, donc c'est un flag plutôt que le comportement par défaut.
Ou filtrez directement : alibi scan . ./contracts --fail-on high se termine avec un code non nul quand un constat atteint cette sévérité. Un scan que noir n'a pas pu lire entièrement rapporte executionSuccessful: false, donc une exécution dégradée ne passe pas pour une exécution propre.
Noir conserve la syntaxe de route propre à chaque framework plutôt que d'en inventer une commune, donc le même endpoint arrive orthographié de plusieurs façons :
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 règle qui les rend comparables : le nom d'un paramètre de chemin ne fait pas partie de son identité. {petId} et <int:user_id> décrivent le même emplacement ; seuls sa position et le fait qu'il couvre ou non un / importent. Les noms sont conservés comme preuve et rapportés, mais n'entrent jamais dans la clé.
Les constats indiquent comment la correspondance a été faite :
| Grade | Signification |
|---|---|
G1 | les orthographes concordaient déjà |
G2 | elles concordent une fois la syntaxe des paramètres normalisée |
G0 | une seule vue le possède — rien n'a été mis en correspondance |
Un outil comme celui-ci meurt en rapportant des centaines de constats à sa première exécution, ou en rapportant des progrès que personne n'a faits. Six choses s'y opposent :
Les règles ne se déclenchent pas sans les deux vues. Scannez une base de code sans aucun contrat nulle part et chaque endpoint se qualifie techniquement comme une shadow API non documentée. Ces constats ne disent rien d'autre que le fait que vous n'avez fourni aucune documentation, donc une règle ne s'exécute que lorsque chaque vue sur laquelle elle raisonne était réellement présente dans le scan. Le rapport nomme les règles qui se sont abstenues.
Les quasi-correspondances sont rapportées comme un doute, pas comme des constats. « Dans le code, pas dans la doc » est indiscernable de « dans les deux, mais alibi a échoué à les aligner ». Donc un endpoint qui atterrit dans une vue est vérifié contre les autres pour une quasi-correspondance — même chemin avec un verbe différent, ou à un segment près là où un côté a un paramètre et l'autre un littéral. Les constats portant une quasi-correspondance sont rétrogradés et signalés pour revue. Ce décompte figure à côté des totaux, car chaque constat n'est fiable que dans la mesure où il est petit.