Проблема
Документация API почти всегда отстаёт от продакшена. Старые маршруты остаются ради обратной совместимости, тестовые методы случайно публикуются наружу, а новая авторизация появляется не везде одновременно.
Когда команда смотрит только на OpenAPI или только на сканер, она не видит архитектурный дрейф. Именно там часто живут shadow endpoint-ы, незакрытые debug-methods и неожиданные пути обхода rate limit или auth middleware.
Решение
Нужно строить inventory из нескольких источников: OpenAPI, HAR, reverse proxy access log, application route dump и release notes. Только сравнение этих слоёв показывает, что реально живёт в perimeter и какие маршруты никто больше не контролирует.
После inventory полезно разделить маршруты на documented, observed_only, doc_only и privileged. Это сразу даёт разговор не про payload, а про ownership: кто отвечает за endpoint, почему он опубликован и как проверить его безопасно.
Что проверить руками
- Собрать HAR из обычного пользовательского сценария и административного сценария.
- Извлечь список методов и paths из OpenAPI и сравнить его с observed traffic.
- Отдельно пометить observed_only endpoint-ы с object id, file download, export или debug semantics.
- Проверить, не отличаются ли для shadow routes security headers, auth chain и rate-limit policy.
Типичные ошибки
- Считать, что undocumented endpoint автоматически нерелевантен для аудита.
- Сканировать весь observed surface одинаково, не разделяя публичные и role-bound routes.
- Закрывать риск формулировкой 'не используется', не проверив gateway, code owners и release path.
Defensive checklist
- Есть diff между OpenAPI и observed traffic.
- Observed-only endpoints назначены владельцам.
- Role-bound methods проверены read-only replay или manual trace.
- Retest критерий описывает, как drift будет ловиться на следующем релизе.
Безопасный пример кода
Diff OpenAPI и observed traffic по path/method. Пример рассчитан на owned/lab-среду и показывает инженерную логику проверки, а не эксплуатационную цепочку.
import json
from collections import defaultdict
from pathlib import Path
def openapi_ops(path: str) -> set[tuple[str, str]]:
spec = json.loads(Path(path).read_text(encoding="utf-8"))
result = set()
for route, methods in spec.get("paths", {}).items():
for method in methods:
result.add((method.upper(), route))
return result
def observed_ops(path: str) -> set[tuple[str, str]]:
data = json.loads(Path(path).read_text(encoding="utf-8"))
result = set()
for item in data:
result.add((item["method"].upper(), item["path"]))
return result
doc = openapi_ops("openapi.json")
obs = observed_ops("traffic_inventory.json")
print("observed_only", sorted(obs - doc)[:10])
print("doc_only", sorted(doc - obs)[:10])
Как это должно попасть в отчёт
- Coverage: documented, observed_only, doc_only, privileged_only.
- Evidence: HAR/reference log, affected role, route owner, auth chain notes.
- Retest: release pipeline или nightly inventory должен поднимать alert на новый drift.