diff --git a/backend/internal/bootstrap/router_huma_test.go b/backend/internal/bootstrap/router_huma_test.go index 7c1502f0..0ef152e9 100644 --- a/backend/internal/bootstrap/router_huma_test.go +++ b/backend/internal/bootstrap/router_huma_test.go @@ -35,7 +35,7 @@ func TestHumaRouterOpenAPI(t *testing.T) { require.NoError(t, registerRoutes(router, db, services, nil)) response := httptest.NewRecorder() - router.ServeHTTP(response, httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/api/openapi.json", nil)) + router.ServeHTTP(response, httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/api/openai.json", nil)) require.Equal(t, http.StatusOK, response.Code) var document struct { @@ -97,10 +97,7 @@ func TestHumaRouterOpenAPI(t *testing.T) { response = httptest.NewRecorder() router.ServeHTTP(response, httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/api/docs", nil)) - require.Equal(t, http.StatusOK, response.Code) - require.Contains(t, response.Header().Get("Content-Security-Policy"), "https://cdn.jsdelivr.net") - require.NotContains(t, response.Header().Get("Content-Security-Policy"), "script-src 'unsafe-inline'") - require.Contains(t, response.Body.String(), "@scalar/api-reference@1.62.5") + require.Equal(t, http.StatusNotFound, response.Code) response = httptest.NewRecorder() newHTTPServer(router, nil).Handler.ServeHTTP(response, httptest.NewRequestWithContext(t.Context(), http.MethodHead, "/healthz", nil)) diff --git a/backend/internal/utils/csp.go b/backend/internal/utils/csp.go index 730f593c..ce42f41d 100644 --- a/backend/internal/utils/csp.go +++ b/backend/internal/utils/csp.go @@ -35,26 +35,6 @@ func BuildFormPostCSP(nonce, redirectURI, scriptHash string) string { return buildCSP(nonce, []string{redirectURI}, []string{scriptHash}) } -// BuildAPIDocsCSP allows the pinned Scalar bundle and the assets it creates -func BuildAPIDocsCSP(nonce string) string { - scriptSrc := "script-src 'self' https://cdn.jsdelivr.net" - if nonce != "" { - scriptSrc += " 'nonce-" + nonce + "'" - } - - return "default-src 'self'; " + - "base-uri 'self'; " + - "object-src 'none'; " + - "frame-ancestors 'none'; " + - "form-action 'self'; " + - "img-src * blob: data:; " + - "font-src 'self' https://cdn.jsdelivr.net data:; " + - "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; " + - "worker-src blob:; " + - "connect-src 'self'; " + - scriptSrc -} - func buildCSP(nonce string, formActionExtra, scriptSrcExtra []string) string { formAction := "'self'" scriptSrc := "script-src 'self'" diff --git a/backend/internal/utils/huma/api.go b/backend/internal/utils/huma/api.go index a1b4295a..5d1fb44a 100644 --- a/backend/internal/utils/huma/api.go +++ b/backend/internal/utils/huma/api.go @@ -3,33 +3,15 @@ package humautils import ( "encoding/json" "io" - "net/http" "github.com/danielgtaylor/huma/v2" "github.com/danielgtaylor/huma/v2/adapters/humagin" "github.com/gin-gonic/gin" "github.com/pocket-id/pocket-id/backend/internal/common" - "github.com/pocket-id/pocket-id/backend/internal/utils" "github.com/pocket-id/pocket-id/backend/internal/utils/cookie" ) -const scalarDocsHTML = ` - - - Pocket ID API Reference - - - - - - - -` - var ginCompatibleJSONFormat = huma.Format{ Marshal: func(w io.Writer, value any) error { data, err := json.Marshal(value) @@ -47,7 +29,7 @@ func New(r *gin.Engine, group *gin.RouterGroup) huma.API { config := huma.DefaultConfig("Pocket ID API", common.Version) config.CreateHooks = nil config.DocsPath = "" - config.OpenAPIPath = "/api/openapi" + config.OpenAPIPath = "/api/openai" config.SchemasPath = "/api/schemas" config.AllowAdditionalPropertiesByDefault = true config.Security = nil @@ -94,7 +76,6 @@ func New(r *gin.Engine, group *gin.RouterGroup) huma.API { humagin.MultipartMaxMemory = r.MaxMultipartMemory api := humagin.NewWithGroup(r, group, config) api.UseMiddleware(CaptureRequestContext) - registerScalarDocs(group) return api } @@ -108,11 +89,3 @@ func rewriteValidationResponse(_ *huma.OpenAPI, operation *huma.Operation) { } delete(operation.Responses, "422") } - -func registerScalarDocs(group *gin.RouterGroup) { - group.GET("/api/docs", func(ctx *gin.Context) { - nonce := utils.GetCSPNonce(ctx) - ctx.Header("Content-Security-Policy", utils.BuildAPIDocsCSP(nonce)) - ctx.Data(http.StatusOK, "text/html; charset=utf-8", []byte(scalarDocsHTML)) - }) -} diff --git a/backend/internal/utils/huma/api_test.go b/backend/internal/utils/huma/api_test.go index cfad589c..6a319362 100644 --- a/backend/internal/utils/huma/api_test.go +++ b/backend/internal/utils/huma/api_test.go @@ -154,7 +154,7 @@ func TestCookiesStreamingAndOpenAPI(t *testing.T) { require.True(t, reader.closed) response = httptest.NewRecorder() - router.ServeHTTP(response, httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/api/openapi.json", nil)) + router.ServeHTTP(response, httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/api/openai.json", nil)) require.Equal(t, http.StatusOK, response.Code) require.Contains(t, response.Body.String(), `"/api/test-raw"`) require.NotContains(t, response.Body.String(), `"422"`) @@ -162,10 +162,7 @@ func TestCookiesStreamingAndOpenAPI(t *testing.T) { response = httptest.NewRecorder() router.ServeHTTP(response, httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/api/docs", nil)) - require.Equal(t, http.StatusOK, response.Code) - require.Contains(t, response.Body.String(), "@scalar/api-reference@1.62.5") - require.Contains(t, response.Header().Get("Content-Security-Policy"), "worker-src blob:") - require.NotContains(t, response.Header().Get("Content-Security-Policy"), "script-src 'unsafe-inline'") + require.Equal(t, http.StatusNotFound, response.Code) } func TestRegisterAppliesDecoratorsInOrder(t *testing.T) { diff --git a/frontend/messages/en.json b/frontend/messages/en.json index ba4eafa7..92c7515e 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -140,7 +140,7 @@ "name_passkey": "Name Passkey", "name_your_passkey_to_easily_identify_it_later": "Name your passkey to easily identify it later.", "create_api_key": "Create API Key", - "add_a_new_api_key_for_programmatic_access": "Add a new API key for programmatic access to the Pocket ID API.", + "add_a_new_api_key_for_programmatic_access": "Add a new API key for programmatic access to the Pocket ID API.", "add_api_key": "Add API Key", "manage_api_keys": "Manage API Keys", "api_key_created": "API Key Created",