docs/ui.go serves the Swagger UI page with three external resources from cdnjs:
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.15.5/swagger-ui.css" >
<script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.15.5/swagger-ui-bundle.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.15.5/swagger-ui-standalone-preset.js"> </script>
Three things follow from that:
- No
integrity/SRI. /docs is unauthenticated and shares an origin with the web UI, so anything served from that CDN path runs in the app origin. An SRI hash costs nothing here since the version is already pinned.
- 4.15.5 is from 2022, several majors behind swagger-ui 5.x.
/docs does not work offline, which is awkward for the air-gapped/LAN deployments Gotify is often used in — the rest of the UI is embedded via go:embed.
Vendoring swagger-ui-dist into ui/ and embedding it would solve all three at once. Would you take a PR for that, or is the CDN a deliberate choice to keep the binary small?
(Unrelated but in the same file: GET /swagger?base=… splices the query value into the spec JSON unescaped (docs/swagger.go:getSwaggerJSON), so a crafted base can break out of the "host" string. It is self-inflicted only — the page passes window.location.host, never a query param — and the response is sniffed as text/plain, so I do not think it is more than cosmetic. Mentioning it in case you want the value validated anyway.)
docs/ui.goserves the Swagger UI page with three external resources from cdnjs:Three things follow from that:
integrity/SRI./docsis unauthenticated and shares an origin with the web UI, so anything served from that CDN path runs in the app origin. An SRI hash costs nothing here since the version is already pinned./docsdoes not work offline, which is awkward for the air-gapped/LAN deployments Gotify is often used in — the rest of the UI is embedded viago:embed.Vendoring swagger-ui-dist into
ui/and embedding it would solve all three at once. Would you take a PR for that, or is the CDN a deliberate choice to keep the binary small?(Unrelated but in the same file:
GET /swagger?base=…splices the query value into the spec JSON unescaped (docs/swagger.go:getSwaggerJSON), so a craftedbasecan break out of the"host"string. It is self-inflicted only — the page passeswindow.location.host, never a query param — and the response is sniffed astext/plain, so I do not think it is more than cosmetic. Mentioning it in case you want the value validated anyway.)