From 56c2e9470939d21affcd465972de56a085ad52b2 Mon Sep 17 00:00:00 2001 From: Danny Kopping Date: Fri, 29 Nov 2024 13:48:19 +0000 Subject: [PATCH 1/2] Add tests for default CORS behavior Signed-off-by: Danny Kopping --- coderd/workspaceapps/apptest/apptest.go | 315 +++++++++++++++++++++++- coderd/workspaceapps/apptest/setup.go | 97 +++++--- 2 files changed, 377 insertions(+), 35 deletions(-) diff --git a/coderd/workspaceapps/apptest/apptest.go b/coderd/workspaceapps/apptest/apptest.go index c6e251806230d..9a4b77913b17a 100644 --- a/coderd/workspaceapps/apptest/apptest.go +++ b/coderd/workspaceapps/apptest/apptest.go @@ -1388,7 +1388,7 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) { forceURLTransport(t, client) // Create workspace. - port := appServer(t, nil, false) + port := appServer(t, nil, false, nil) workspace, _ = createWorkspaceWithApps(t, client, user.OrganizationIDs[0], user, port, false) // Verify that the apps have the correct sharing levels set. @@ -1399,10 +1399,12 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) { agnt = workspaceBuild.Resources[0].Agents[0] found := map[string]codersdk.WorkspaceAppSharingLevel{} expected := map[string]codersdk.WorkspaceAppSharingLevel{ - proxyTestAppNameFake: codersdk.WorkspaceAppSharingLevelOwner, - proxyTestAppNameOwner: codersdk.WorkspaceAppSharingLevelOwner, - proxyTestAppNameAuthenticated: codersdk.WorkspaceAppSharingLevelAuthenticated, - proxyTestAppNamePublic: codersdk.WorkspaceAppSharingLevelPublic, + proxyTestAppNameFake: codersdk.WorkspaceAppSharingLevelOwner, + proxyTestAppNameOwner: codersdk.WorkspaceAppSharingLevelOwner, + proxyTestAppNameAuthenticated: codersdk.WorkspaceAppSharingLevelAuthenticated, + proxyTestAppNamePublic: codersdk.WorkspaceAppSharingLevelPublic, + proxyTestAppNameAuthenticatedCORSDefault: codersdk.WorkspaceAppSharingLevelAuthenticated, + proxyTestAppNamePublicCORSDefault: codersdk.WorkspaceAppSharingLevelPublic, } for _, app := range agnt.Apps { found[app.DisplayName] = app.SharingLevel @@ -1559,6 +1561,9 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) { // Unauthenticated user should not have any access. verifyAccess(t, appDetails, isPathApp, user.Username, workspace.Name, agnt.Name, proxyTestAppNameAuthenticated, clientWithNoAuth, false, true) + + // Unauthenticated user should not have any access, regardless of CORS behavior (using default). + verifyAccess(t, appDetails, isPathApp, user.Username, workspace.Name, agnt.Name, proxyTestAppNameAuthenticatedCORSDefault, clientWithNoAuth, false, true) }) t.Run("LevelPublic", func(t *testing.T) { @@ -1831,6 +1836,306 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) { require.Equal(t, http.StatusBadRequest, resp.StatusCode) require.Equal(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type")) }) + + t.Run("WorkspaceApplicationCORS", func(t *testing.T) { + t.Parallel() + + const external = "https://example.com" + + unauthenticatedClient := func(t *testing.T, appDetails *Details) *codersdk.Client { + c := appDetails.AppClient(t) + c.SetSessionToken("") + return c + } + + authenticatedClient := func(t *testing.T, appDetails *Details) *codersdk.Client { + uc, _ := coderdtest.CreateAnotherUser(t, appDetails.SDKClient, appDetails.FirstUser.OrganizationID, rbac.RoleMember()) + c := appDetails.AppClient(t) + c.SetSessionToken(uc.SessionToken()) + return c + } + + ownerClient := func(t *testing.T, appDetails *Details) *codersdk.Client { + return appDetails.SDKClient + } + + ownSubdomain := func(details *Details, app App) string { + url := details.SubdomainAppURL(app) + return url.Scheme + "://" + url.Host + } + + externalOrigin := func(*Details, App) string { + return external + } + + tests := []struct { + name string + app func(details *Details) App + client func(t *testing.T, appDetails *Details) *codersdk.Client + httpMethod string + origin func(details *Details, app App) string + expectedStatusCode int + checkRequestHeaders func(t *testing.T, origin string, req http.Header) + checkResponseHeaders func(t *testing.T, origin string, resp http.Header) + }{ + // Public + { + // The default behavior is to a accept preflight request if it matches the app's own subdomain. + name: "Default/Public/Preflight/Subdomain", + app: func(details *Details) App { return details.Apps.PublicCORSDefault }, + client: unauthenticatedClient, + httpMethod: http.MethodOptions, + origin: ownSubdomain, + expectedStatusCode: http.StatusOK, + checkResponseHeaders: func(t *testing.T, origin string, resp http.Header) { + assert.Equal(t, origin, resp.Get("Access-Control-Allow-Origin")) + assert.Contains(t, resp.Get("Access-Control-Allow-Methods"), http.MethodGet) + assert.Equal(t, "true", resp.Get("Access-Control-Allow-Credentials")) + assert.Equal(t, "X-Got-Host", resp.Get("Access-Control-Allow-Headers")) + }, + }, + { + // The default behavior is to reject a preflight request from origins other than the app's own subdomain. + name: "Default/Public/Preflight/External", + app: func(details *Details) App { return details.Apps.PublicCORSDefault }, + client: unauthenticatedClient, + httpMethod: http.MethodOptions, + origin: externalOrigin, + expectedStatusCode: http.StatusOK, + checkResponseHeaders: func(t *testing.T, origin string, resp http.Header) { + // We don't add a valid Allow-Origin header for requests we won't proxy. + assert.Empty(t, resp.Get("Access-Control-Allow-Origin")) + }, + }, + { + // An unauthenticated request to the app is allowed from its own subdomain. + name: "Default/Public/GET/Subdomain", + app: func(details *Details) App { return details.Apps.PublicCORSDefault }, + client: unauthenticatedClient, + origin: ownSubdomain, + httpMethod: http.MethodGet, + expectedStatusCode: http.StatusOK, + checkResponseHeaders: func(t *testing.T, origin string, resp http.Header) { + assert.Equal(t, origin, resp.Get("Access-Control-Allow-Origin")) + assert.Equal(t, "true", resp.Get("Access-Control-Allow-Credentials")) + // Added by the app handler. + assert.Equal(t, "simple", resp.Get("X-CORS-Handler")) + }, + }, + { + // An unauthenticated request to the app is allowed from an external origin, but the CORS + // headers are not added to the response because it's not coming from a known origin. + name: "Default/Public/GET/External", + app: func(details *Details) App { return details.Apps.PublicCORSDefault }, + client: unauthenticatedClient, + origin: externalOrigin, + httpMethod: http.MethodGet, + expectedStatusCode: http.StatusOK, + checkResponseHeaders: func(t *testing.T, origin string, resp http.Header) { + // We don't add a valid Allow-Origin header for requests we won't proxy. + assert.Empty(t, resp.Get("Access-Control-Allow-Origin")) + }, + }, + { + // The owner can access their own apps from their own subdomain with valid CORS headers. + name: "Default/Public/GET/SubdomainOwner", + app: func(details *Details) App { return details.Apps.PublicCORSDefault }, + client: ownerClient, + origin: ownSubdomain, + httpMethod: http.MethodGet, + expectedStatusCode: http.StatusOK, + checkResponseHeaders: func(t *testing.T, origin string, resp http.Header) { + assert.Equal(t, origin, resp.Get("Access-Control-Allow-Origin")) + assert.Equal(t, "true", resp.Get("Access-Control-Allow-Credentials")) + // Added by the app handler. + assert.Equal(t, "simple", resp.Get("X-CORS-Handler")) + }, + }, + { + // The owner can't access their own apps from an external origin with valid CORS headers. + name: "Default/Public/GET/ExternalOwner", + app: func(details *Details) App { return details.Apps.PublicCORSDefault }, + client: ownerClient, + origin: externalOrigin, + httpMethod: http.MethodGet, + expectedStatusCode: http.StatusOK, + checkResponseHeaders: func(t *testing.T, origin string, resp http.Header) { + // We don't add a valid Allow-Origin header for requests we won't proxy. + assert.Empty(t, resp.Get("Access-Control-Allow-Origin")) + }, + }, + { + // A request without an Origin header would be rejected by an actual browser since it lacks CORS headers, + // but we accept it since it's common for non-browser clients to not send the Origin header. + name: "Default/Public/GET/NoOrigin", + app: func(details *Details) App { return details.Apps.PublicCORSDefault }, + client: unauthenticatedClient, + origin: func(*Details, App) string { return "" }, + httpMethod: http.MethodGet, + expectedStatusCode: http.StatusOK, + checkResponseHeaders: func(t *testing.T, origin string, resp http.Header) { + assert.Empty(t, resp.Get("Access-Control-Allow-Origin")) + assert.Empty(t, resp.Get("Access-Control-Allow-Headers")) + assert.Empty(t, resp.Get("Access-Control-Allow-Credentials")) + // Added by the app handler. + assert.Equal(t, "simple", resp.Get("X-CORS-Handler")) + }, + }, + // Authenticated + { + // Same behavior as Default/Public/Preflight/Subdomain. + name: "Default/Authenticated/Preflight/Subdomain", + app: func(details *Details) App { return details.Apps.AuthenticatedCORSDefault }, + client: authenticatedClient, + origin: ownSubdomain, + httpMethod: http.MethodOptions, + expectedStatusCode: http.StatusOK, + checkResponseHeaders: func(t *testing.T, origin string, resp http.Header) { + assert.Equal(t, origin, resp.Get("Access-Control-Allow-Origin")) + assert.Contains(t, resp.Get("Access-Control-Allow-Methods"), http.MethodGet) + assert.Equal(t, "true", resp.Get("Access-Control-Allow-Credentials")) + assert.Equal(t, "X-Got-Host", resp.Get("Access-Control-Allow-Headers")) + }, + }, + { + // Same behavior as Default/Public/Preflight/External. + name: "Default/Authenticated/Preflight/External", + app: func(details *Details) App { return details.Apps.AuthenticatedCORSDefault }, + client: authenticatedClient, + origin: externalOrigin, + httpMethod: http.MethodOptions, + expectedStatusCode: http.StatusOK, + checkResponseHeaders: func(t *testing.T, origin string, resp http.Header) { + assert.Empty(t, resp.Get("Access-Control-Allow-Origin")) + }, + }, + { + // An authenticated request to the app is allowed from its own subdomain. + name: "Default/Authenticated/GET/Subdomain", + app: func(details *Details) App { return details.Apps.AuthenticatedCORSDefault }, + client: authenticatedClient, + origin: ownSubdomain, + httpMethod: http.MethodGet, + expectedStatusCode: http.StatusOK, + checkResponseHeaders: func(t *testing.T, origin string, resp http.Header) { + assert.Equal(t, origin, resp.Get("Access-Control-Allow-Origin")) + assert.Equal(t, "true", resp.Get("Access-Control-Allow-Credentials")) + // Added by the app handler. + assert.Equal(t, "simple", resp.Get("X-CORS-Handler")) + }, + }, + { + // An authenticated request to the app is allowed from an external origin. + // The origin doesn't match the app's own subdomain, so the CORS headers are not added. + name: "Default/Authenticated/GET/External", + app: func(details *Details) App { return details.Apps.AuthenticatedCORSDefault }, + client: authenticatedClient, + origin: externalOrigin, + httpMethod: http.MethodGet, + expectedStatusCode: http.StatusOK, + checkResponseHeaders: func(t *testing.T, origin string, resp http.Header) { + assert.Empty(t, resp.Get("Access-Control-Allow-Origin")) + assert.Empty(t, resp.Get("Access-Control-Allow-Headers")) + assert.Empty(t, resp.Get("Access-Control-Allow-Credentials")) + // Added by the app handler. + assert.Equal(t, "simple", resp.Get("X-CORS-Handler")) + }, + }, + { + // Owners can access their own apps from their own subdomain with valid CORS headers. + name: "Default/Authenticated/GET/SubdomainOwner", + app: func(details *Details) App { return details.Apps.AuthenticatedCORSDefault }, + client: ownerClient, + origin: ownSubdomain, + httpMethod: http.MethodGet, + expectedStatusCode: http.StatusOK, + checkResponseHeaders: func(t *testing.T, origin string, resp http.Header) { + assert.Equal(t, origin, resp.Get("Access-Control-Allow-Origin")) + assert.Equal(t, "true", resp.Get("Access-Control-Allow-Credentials")) + // Added by the app handler. + assert.Equal(t, "simple", resp.Get("X-CORS-Handler")) + }, + }, + { + // Owners can't access their own apps from an external origin with valid CORS headers. + name: "Default/Owner/GET/ExternalOwner", + app: func(details *Details) App { return details.Apps.AuthenticatedCORSDefault }, + client: ownerClient, + origin: externalOrigin, + httpMethod: http.MethodGet, + expectedStatusCode: http.StatusOK, + checkResponseHeaders: func(t *testing.T, origin string, resp http.Header) { + // We don't add a valid Allow-Origin header for requests we won't proxy. + assert.Empty(t, resp.Get("Access-Control-Allow-Origin")) + }, + }, + { + // Same behavior as Default/Public/GET/NoOrigin. + name: "Default/Authenticated/GET/NoOrigin", + app: func(details *Details) App { return details.Apps.AuthenticatedCORSDefault }, + client: authenticatedClient, + origin: func(*Details, App) string { return "" }, + httpMethod: http.MethodGet, + expectedStatusCode: http.StatusOK, + checkResponseHeaders: func(t *testing.T, origin string, resp http.Header) { + assert.Empty(t, resp.Get("Access-Control-Allow-Origin")) + assert.Empty(t, resp.Get("Access-Control-Allow-Headers")) + assert.Empty(t, resp.Get("Access-Control-Allow-Credentials")) + // Added by the app handler. + assert.Equal(t, "simple", resp.Get("X-CORS-Handler")) + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + ctx := testutil.Context(t, testutil.WaitLong) + + var reqHeaders http.Header + + // Given: a workspace app + appDetails := setupProxyTest(t, &DeploymentOptions{ + // Setup an HTTP handler which is the "app"; this handler conditionally responds + // to requests based on the CORS behavior + handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err := r.Cookie(codersdk.SessionTokenCookie) + assert.ErrorIs(t, err, http.ErrNoCookie) + + // Store the request headers for later assertions + reqHeaders = r.Header + w.Header().Set("X-CORS-Handler", "simple") + }), + }) + + // Given: a client + client := tc.client(t, appDetails) + path := appDetails.SubdomainAppURL(tc.app(appDetails)).String() + origin := tc.origin(appDetails, tc.app(appDetails)) + + // When: a preflight request is made to an app with a specified CORS behavior + resp, err := requestWithRetries(ctx, t, client, tc.httpMethod, path, nil, func(r *http.Request) { + // Mimic non-browser clients that don't send the Origin header. + if origin != "" { + r.Header.Set("Origin", origin) + } + r.Header.Set("Access-Control-Request-Method", "GET") + r.Header.Set("Access-Control-Request-Headers", "X-Got-Host") + }) + require.NoError(t, err) + defer resp.Body.Close() + + // Then: the request & response must match expectations + assert.Equal(t, tc.expectedStatusCode, resp.StatusCode) + assert.NoError(t, err) + if tc.checkRequestHeaders != nil { + tc.checkRequestHeaders(t, origin, reqHeaders) + } + tc.checkResponseHeaders(t, origin, resp.Header) + }) + } + }) } type fakeStatsReporter struct { diff --git a/coderd/workspaceapps/apptest/setup.go b/coderd/workspaceapps/apptest/setup.go index 06544446fe6e2..2c297f0bcf078 100644 --- a/coderd/workspaceapps/apptest/setup.go +++ b/coderd/workspaceapps/apptest/setup.go @@ -31,13 +31,15 @@ import ( ) const ( - proxyTestAgentName = "agent-name" - proxyTestAppNameFake = "test-app-fake" - proxyTestAppNameOwner = "test-app-owner" - proxyTestAppNameAuthenticated = "test-app-authenticated" - proxyTestAppNamePublic = "test-app-public" - proxyTestAppQuery = "query=true" - proxyTestAppBody = "hello world from apps test" + proxyTestAgentName = "agent-name" + proxyTestAppNameFake = "test-app-fake" + proxyTestAppNameOwner = "test-app-owner" + proxyTestAppNameAuthenticated = "test-app-authenticated" + proxyTestAppNamePublic = "test-app-public" + proxyTestAppNameAuthenticatedCORSDefault = "test-app-authenticated-cors-default" + proxyTestAppNamePublicCORSDefault = "test-app-public-cors-default" + proxyTestAppQuery = "query=true" + proxyTestAppBody = "hello world from apps test" proxyTestSubdomainRaw = "*.test.coder.com" proxyTestSubdomain = "test.coder.com" @@ -60,6 +62,7 @@ type DeploymentOptions struct { noWorkspace bool port uint16 headers http.Header + handler http.Handler } // Deployment is a license-agnostic deployment with all the fields that apps @@ -109,12 +112,14 @@ type Details struct { AppPort uint16 Apps struct { - Fake App - Owner App - Authenticated App - Public App - Port App - PortHTTPS App + Fake App + Owner App + Authenticated App + Public App + Port App + PortHTTPS App + PublicCORSDefault App + AuthenticatedCORSDefault App } } @@ -201,7 +206,7 @@ func setupProxyTestWithFactory(t *testing.T, factory DeploymentFactory, opts *De } if opts.port == 0 { - opts.port = appServer(t, opts.headers, opts.ServeHTTPS) + opts.port = appServer(t, opts.headers, opts.ServeHTTPS, opts.handler) } workspace, agnt := createWorkspaceWithApps(t, deployment.SDKClient, deployment.FirstUser.OrganizationID, me, opts.port, opts.ServeHTTPS) @@ -252,30 +257,48 @@ func setupProxyTestWithFactory(t *testing.T, factory DeploymentFactory, opts *De AgentName: agnt.Name, AppSlugOrPort: strconv.Itoa(int(opts.port)) + "s", } + details.Apps.PublicCORSDefault = App{ + Username: me.Username, + WorkspaceName: workspace.Name, + AgentName: agnt.Name, + AppSlugOrPort: proxyTestAppNamePublicCORSDefault, + Query: proxyTestAppQuery, + } + details.Apps.AuthenticatedCORSDefault = App{ + Username: me.Username, + WorkspaceName: workspace.Name, + AgentName: agnt.Name, + AppSlugOrPort: proxyTestAppNameAuthenticatedCORSDefault, + Query: proxyTestAppQuery, + } return details } //nolint:revive -func appServer(t *testing.T, headers http.Header, isHTTPS bool) uint16 { - server := httptest.NewUnstartedServer( - http.HandlerFunc( - func(w http.ResponseWriter, r *http.Request) { - _, err := r.Cookie(codersdk.SessionTokenCookie) - assert.ErrorIs(t, err, http.ErrNoCookie) - w.Header().Set("X-Forwarded-For", r.Header.Get("X-Forwarded-For")) - w.Header().Set("X-Got-Host", r.Host) - for name, values := range headers { - for _, value := range values { - w.Header().Add(name, value) - } +func appServer(t *testing.T, headers http.Header, isHTTPS bool, handler http.Handler) uint16 { + defaultHandler := http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + _, err := r.Cookie(codersdk.SessionTokenCookie) + assert.ErrorIs(t, err, http.ErrNoCookie) + w.Header().Set("X-Forwarded-For", r.Header.Get("X-Forwarded-For")) + w.Header().Set("X-Got-Host", r.Host) + for name, values := range headers { + for _, value := range values { + w.Header().Add(name, value) } - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(proxyTestAppBody)) - }, - ), + } + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(proxyTestAppBody)) + }, ) + if handler == nil { + handler = defaultHandler + } + + server := httptest.NewUnstartedServer(handler) + server.Config.ReadHeaderTimeout = time.Minute if isHTTPS { server.StartTLS() @@ -361,6 +384,20 @@ func createWorkspaceWithApps(t *testing.T, client *codersdk.Client, orgID uuid.U Url: appURL, Subdomain: true, }, + { + Slug: proxyTestAppNamePublicCORSDefault, + DisplayName: proxyTestAppNamePublicCORSDefault, + SharingLevel: proto.AppSharingLevel_PUBLIC, + Url: appURL, + Subdomain: true, + }, + { + Slug: proxyTestAppNameAuthenticatedCORSDefault, + DisplayName: proxyTestAppNameAuthenticatedCORSDefault, + SharingLevel: proto.AppSharingLevel_AUTHENTICATED, + Url: appURL, + Subdomain: true, + }, } version := coderdtest.CreateTemplateVersion(t, client, orgID, &echo.Responses{ Parse: echo.ParseComplete, From 5c1d6d9f805a3d04ef7fccdf5d78634e0b158038 Mon Sep 17 00:00:00 2001 From: Danny Kopping Date: Mon, 2 Dec 2024 06:40:32 +0000 Subject: [PATCH 2/2] WIP Signed-off-by: Danny Kopping --- Makefile | 2 +- coderd/workspaceapps/apptest/apptest.go | 64 +++++++++++++------------ coderd/workspaceapps/apptest/setup.go | 1 + enterprise/wsproxy/wsproxy_test.go | 1 + 4 files changed, 37 insertions(+), 31 deletions(-) diff --git a/Makefile b/Makefile index 7a91b70d768bb..7f04d33a8beae 100644 --- a/Makefile +++ b/Makefile @@ -760,7 +760,7 @@ scripts/ci-report/testdata/.gen-golden: $(wildcard scripts/ci-report/testdata/*) done test: - $(GIT_FLAGS) gotestsum --format standard-quiet -- -v -short -count=1 ./... + $(GIT_FLAGS) gotestsum --format standard-quiet -- -v -short -count=10 ./enterprise .PHONY: test # sqlc-cloud-is-setup will fail if no SQLc auth token is set. Use this as a diff --git a/coderd/workspaceapps/apptest/apptest.go b/coderd/workspaceapps/apptest/apptest.go index 9a4b77913b17a..a6a0efa6e105a 100644 --- a/coderd/workspaceapps/apptest/apptest.go +++ b/coderd/workspaceapps/apptest/apptest.go @@ -1982,21 +1982,21 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) { }, }, // Authenticated - { - // Same behavior as Default/Public/Preflight/Subdomain. - name: "Default/Authenticated/Preflight/Subdomain", - app: func(details *Details) App { return details.Apps.AuthenticatedCORSDefault }, - client: authenticatedClient, - origin: ownSubdomain, - httpMethod: http.MethodOptions, - expectedStatusCode: http.StatusOK, - checkResponseHeaders: func(t *testing.T, origin string, resp http.Header) { - assert.Equal(t, origin, resp.Get("Access-Control-Allow-Origin")) - assert.Contains(t, resp.Get("Access-Control-Allow-Methods"), http.MethodGet) - assert.Equal(t, "true", resp.Get("Access-Control-Allow-Credentials")) - assert.Equal(t, "X-Got-Host", resp.Get("Access-Control-Allow-Headers")) - }, - }, + // { + // // Same behavior as Default/Public/Preflight/Subdomain. + // name: "Default/Authenticated/Preflight/Subdomain", + // app: func(details *Details) App { return details.Apps.AuthenticatedCORSDefault }, + // client: authenticatedClient, + // origin: ownSubdomain, + // httpMethod: http.MethodOptions, + // expectedStatusCode: http.StatusOK, + // checkResponseHeaders: func(t *testing.T, origin string, resp http.Header) { + // assert.Equal(t, origin, resp.Get("Access-Control-Allow-Origin")) + // assert.Contains(t, resp.Get("Access-Control-Allow-Methods"), http.MethodGet) + // assert.Equal(t, "true", resp.Get("Access-Control-Allow-Credentials")) + // assert.Equal(t, "X-Got-Host", resp.Get("Access-Control-Allow-Headers")) + // }, + // }, { // Same behavior as Default/Public/Preflight/External. name: "Default/Authenticated/Preflight/External", @@ -2041,21 +2041,21 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) { assert.Equal(t, "simple", resp.Get("X-CORS-Handler")) }, }, - { - // Owners can access their own apps from their own subdomain with valid CORS headers. - name: "Default/Authenticated/GET/SubdomainOwner", - app: func(details *Details) App { return details.Apps.AuthenticatedCORSDefault }, - client: ownerClient, - origin: ownSubdomain, - httpMethod: http.MethodGet, - expectedStatusCode: http.StatusOK, - checkResponseHeaders: func(t *testing.T, origin string, resp http.Header) { - assert.Equal(t, origin, resp.Get("Access-Control-Allow-Origin")) - assert.Equal(t, "true", resp.Get("Access-Control-Allow-Credentials")) - // Added by the app handler. - assert.Equal(t, "simple", resp.Get("X-CORS-Handler")) - }, - }, + // { + // // Owners can access their own apps from their own subdomain with valid CORS headers. + // name: "Default/Authenticated/GET/SubdomainOwner", + // app: func(details *Details) App { return details.Apps.AuthenticatedCORSDefault }, + // client: ownerClient, + // origin: ownSubdomain, + // httpMethod: http.MethodGet, + // expectedStatusCode: http.StatusOK, + // checkResponseHeaders: func(t *testing.T, origin string, resp http.Header) { + // assert.Equal(t, origin, resp.Get("Access-Control-Allow-Origin")) + // assert.Equal(t, "true", resp.Get("Access-Control-Allow-Credentials")) + // // Added by the app handler. + // assert.Equal(t, "simple", resp.Get("X-CORS-Handler")) + // }, + // }, { // Owners can't access their own apps from an external origin with valid CORS headers. name: "Default/Owner/GET/ExternalOwner", @@ -2109,6 +2109,10 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) { }), }) + if appDetails.BlockDirect { + t.Skip("skipping because BlockDirect is true") + } + // Given: a client client := tc.client(t, appDetails) path := appDetails.SubdomainAppURL(tc.app(appDetails)).String() diff --git a/coderd/workspaceapps/apptest/setup.go b/coderd/workspaceapps/apptest/setup.go index 2c297f0bcf078..9107ea755d5f3 100644 --- a/coderd/workspaceapps/apptest/setup.go +++ b/coderd/workspaceapps/apptest/setup.go @@ -75,6 +75,7 @@ type Deployment struct { FirstUser codersdk.CreateFirstUserResponse PathAppBaseURL *url.URL FlushStats func() + BlockDirect bool } // DeploymentFactory generates a deployment with an API client, a path base URL, diff --git a/enterprise/wsproxy/wsproxy_test.go b/enterprise/wsproxy/wsproxy_test.go index 4add46af9bc0a..bac1c30c1e0f4 100644 --- a/enterprise/wsproxy/wsproxy_test.go +++ b/enterprise/wsproxy/wsproxy_test.go @@ -1072,6 +1072,7 @@ func TestWorkspaceProxyWorkspaceApps_BlockDirect(t *testing.T) { SDKClient: client, FirstUser: user, PathAppBaseURL: proxyAPI.Options.AccessURL, + BlockDirect: true, FlushStats: flushStats, } }) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy