diff --git a/pkg/models/project_view_permissions.go b/pkg/models/project_view_permissions.go index 0aa71fe5e..822ffd93c 100644 --- a/pkg/models/project_view_permissions.go +++ b/pkg/models/project_view_permissions.go @@ -45,6 +45,10 @@ func (pv *ProjectView) CanDelete(s *xorm.Session, a web.Auth) (bool, error) { return sf.CanDelete(s, a) } + if _, err := GetProjectViewByIDAndProject(s, pv.ID, pv.ProjectID); err != nil { + return false, err + } + pp := pv.getProject() return pp.IsAdmin(s, a) } @@ -59,6 +63,11 @@ func (pv *ProjectView) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) { return sf.CanUpdate(s, a) } + // Reject a view that isn't in the path project before authorizing against it. + if _, err := GetProjectViewByIDAndProject(s, pv.ID, pv.ProjectID); err != nil { + return false, err + } + pp := pv.getProject() return pp.IsAdmin(s, a) } diff --git a/pkg/webtests/huma_project_view_test.go b/pkg/webtests/huma_project_view_test.go index e7197ce6e..bd5907f25 100644 --- a/pkg/webtests/huma_project_view_test.go +++ b/pkg/webtests/huma_project_view_test.go @@ -321,9 +321,9 @@ func TestProjectView(t *testing.T) { }) t.Run("View from another project", func(t *testing.T) { // view 5 belongs to project 2. testuser1 admins the path project (1), - // so CanDelete passes — but the view isn't under project 1. The Delete - // guard must 404 before touching project 2's buckets/positions, which - // the old code wiped via a project_view_id-only delete. + // but the view isn't under it, so CanDelete now 404s at the permission + // layer; the Delete guard is a backstop against a project_view_id-only + // wipe of project 2's buckets/positions. _, err := owned.testDeleteWithUser(nil, map[string]string{"view": "5"}) require.Error(t, err) assert.Equal(t, http.StatusNotFound, getHTTPErrorCode(err))