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