◐ Shell
clean mode source ↗

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

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

917917

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

918918

})

919919920+

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

921+

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

922+

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

923+

// reads.

924+

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

925+

t.Parallel()

926+927+

req := (workspaceapps.Request{

928+

AccessMethod: workspaceapps.AccessMethodPath,

929+

BasePath: "/app",

930+

UsernameOrID: secondUser.Username,

931+

WorkspaceNameOrID: workspace.ID.String(),

932+

AgentNameOrID: agentName,

933+

AppSlugOrPort: appNamePublic,

934+

}).Normalize()

935+936+

connLogger := connectionlog.NewFake()

937+

auditableIP := testutil.RandomIPv6(t)

938+939+

rw := httptest.NewRecorder()

940+

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

941+

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

942+

r.RemoteAddr = auditableIP

943+944+

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

945+

Logger: api.Logger,

946+

SignedTokenProvider: api.WorkspaceAppsProvider,

947+

DashboardURL: api.AccessURL,

948+

PathAppBaseURL: api.AccessURL,

949+

AppHostname: api.AppHostname,

950+

AppRequest: req,

951+

})

952+

require.False(t, ok)

953+

require.Nil(t, token)

954+955+

w := rw.Result()

956+

defer w.Body.Close()

957+

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

958+

require.NoError(t, err)

959+

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

960+

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

961+

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

962+

})

963+920964

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

921965

t.Parallel()

922966