/api/docs renders the spec with Redoc, which is read-only by design. The thing people mean when they ask for Swagger is firing a request from the page, so Swagger UI now sits beside it at /api/docs/try, reading the same spec - one contract, two renderers. It is OFF outside development and 404s rather than rendering a disabled page: a production box holds live fleet data, and a docs page that can POST to it is a different object from a docs page that cannot. Vendored, not linked. swagger-ui-bundle.min.js and swagger-ui.css are served from staticdocs/ beside the existing Redoc bundle, because production has no internet. validatorUrl is null: left unset, Swagger UI hands the spec to the public swagger validator to render a badge, which here is a request that cannot succeed and elsewhere would ship the internal API surface off the box. THE VENDORED REDOC BUNDLE WAS ALREADY REACHING THE INTERNET, found while proving this page does not. redoc.standalone.js fetched cdn.redoc.ly/redoc/logo-mini.svg for the "API docs by Redocly" mark, so every load of /api/docs on the air-gapped server has been waiting on a request that cannot complete. Replaced with an inline transparent GIF. Three gate holes closed, each one a dodge that worked: - the page is built by interpolating url_for output into a JS string literal, unescaped. SCRIPT_NAME is proxy-controlled and ProxyFix honours X-Forwarded-Prefix, so a mount path carrying a quote closed the literal and executed what followed. The spec URL is now json.dumps'd and the rest HTML-escaped; the injected quote renders as \" and is inert - the "no internet" assertion on the page scanned for https?://, so <script src="//cdn.jsdelivr.net/..."> - the form a copy-paste from any CDN's own documentation produces - matched nothing and passed - the same assertion on the vendored files was a five-host CDN allowlist. An upstream bump to any other host walked through it, and the one real leak in that directory was on no list. It now matches the SHAPES a browser fetches - @import, css url(), src/href, @font-face - whatever the host, ignoring the data: URIs this CSS is full of And the Redoc page's link to the try-it view is built with url_for, which raises BuildError when those routes are not mounted - every production box. Made unconditional it would 500 the docs page in exactly the deployment nobody tests locally, and no test caught that. One does now.
15 KiB
15 KiB