fix(coderd/workspaceapps): verify workspace owner matches app usernam… · coder/coder@4c968b6
@@ -917,6 +917,50 @@ func Test_ResolveRequest(t *testing.T) {
917917require.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+920964t.Run("RedirectSubdomainAuth", func(t *testing.T) {
921965t.Parallel()
922966