◐ Shell
clean mode source ↗

fix(coderd/workspaceapps): verify workspace owner matches app usernam… · coder/coder@e01d3f4

@@ -916,6 +916,50 @@ func Test_ResolveRequest(t *testing.T) {

916916

require.Len(t, connLogger.ConnectionLogs(), 0)

917917

})

918918919+

// Security (PLAT-260): a UUID workspace lookup must reject when

920+

// the URL's username segment names a different owner. Otherwise a

921+

// same-owner origin can be spoofed for credentialed cross-origin

922+

// reads.

923+

t.Run("WorkspaceUUIDOwnerMismatch", func(t *testing.T) {

924+

t.Parallel()

925+926+

req := (workspaceapps.Request{

927+

AccessMethod: workspaceapps.AccessMethodPath,

928+

BasePath: "/app",

929+

UsernameOrID: secondUser.Username,

930+

WorkspaceNameOrID: workspace.ID.String(),

931+

AgentNameOrID: agentName,

932+

AppSlugOrPort: appNamePublic,

933+

}).Normalize()

934+935+

connLogger := connectionlog.NewFake()

936+

auditableIP := testutil.RandomIPv6(t)

937+938+

rw := httptest.NewRecorder()

939+

r := httptest.NewRequest("GET", "/app", nil)

940+

r.Header.Set(codersdk.SessionTokenHeader, client.SessionToken())

941+

r.RemoteAddr = auditableIP

942+943+

token, ok := workspaceappsResolveRequest(t, connLogger, rw, r, workspaceapps.ResolveRequestOptions{

944+

Logger: api.Logger,

945+

SignedTokenProvider: api.WorkspaceAppsProvider,

946+

DashboardURL: api.AccessURL,

947+

PathAppBaseURL: api.AccessURL,

948+

AppHostname: api.AppHostname,

949+

AppRequest: req,

950+

})

951+

require.False(t, ok)

952+

require.Nil(t, token)

953+954+

w := rw.Result()

955+

defer w.Body.Close()

956+

b, err := io.ReadAll(w.Body)

957+

require.NoError(t, err)

958+

require.Contains(t, string(b), "404 - Application Not Found")

959+

require.Equal(t, http.StatusNotFound, w.StatusCode)

960+

require.Len(t, connLogger.ConnectionLogs(), 0)

961+

})

962+919963

t.Run("RedirectSubdomainAuth", func(t *testing.T) {

920964

t.Parallel()

921965