fix(sec): add tests for private issues on projects

- Add integration and unit tests to ensure that private issues on
projects are not shown in any way, shape or form when the doer has no
access to it.
This commit is contained in:
Gusted 2025-01-29 11:50:25 +01:00 committed by Earl Warren
parent b1b635c1d9
commit 51060d9826
No known key found for this signature in database
GPG key ID: 0579CB2928A78A00
6 changed files with 242 additions and 0 deletions

View file

@ -0,0 +1,23 @@
-
id: 1001
title: Org project that contains private issues
owner_id: 3
repo_id: 0
is_closed: false
creator_id: 2
board_type: 1
type: 3
created_unix: 1738000000
updated_unix: 1738000000
-
id: 1002
title: User project that contains private issues
owner_id: 2
repo_id: 0
is_closed: false
creator_id: 2
board_type: 1
type: 1
created_unix: 1738000000
updated_unix: 1738000000

View file

@ -0,0 +1,17 @@
-
id: 1001
project_id: 1001
title: Triage
creator_id: 2
default: true
created_unix: 1738000000
updated_unix: 1738000000
-
id: 1002
project_id: 1002
title: Triage
creator_id: 2
default: true
created_unix: 1738000000
updated_unix: 1738000000

View file

@ -0,0 +1,11 @@
-
id: 1001
issue_id: 6
project_id: 1001
project_board_id: 1001
-
id: 1002
issue_id: 7
project_id: 1002
project_board_id: 1002

View file

@ -1,42 +1,49 @@
- -
id: 1 id: 1
team_id: 1 team_id: 1
org_id: 3
type: 1 type: 1
access_mode: 4 access_mode: 4
- -
id: 2 id: 2
team_id: 1 team_id: 1
org_id: 3
type: 2 type: 2
access_mode: 4 access_mode: 4
- -
id: 3 id: 3
team_id: 1 team_id: 1
org_id: 3
type: 3 type: 3
access_mode: 4 access_mode: 4
- -
id: 4 id: 4
team_id: 1 team_id: 1
org_id: 3
type: 4 type: 4
access_mode: 4 access_mode: 4
- -
id: 5 id: 5
team_id: 1 team_id: 1
org_id: 3
type: 5 type: 5
access_mode: 4 access_mode: 4
- -
id: 6 id: 6
team_id: 1 team_id: 1
org_id: 3
type: 6 type: 6
access_mode: 4 access_mode: 4
- -
id: 7 id: 7
team_id: 1 team_id: 1
org_id: 3
type: 7 type: 7
access_mode: 4 access_mode: 4

View file

@ -0,0 +1,100 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package issues_test
import (
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/project"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPrivateIssueProjects(t *testing.T) {
defer tests.AddFixtures("models/fixtures/PrivateIssueProjects/")()
require.NoError(t, unittest.PrepareTestDatabase())
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
t.Run("Organization project", func(t *testing.T) {
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
orgProject := unittest.AssertExistsAndLoadBean(t, &project.Project{ID: 1001, OwnerID: org.ID})
column := unittest.AssertExistsAndLoadBean(t, &project.Column{ID: 1001, ProjectID: orgProject.ID})
t.Run("Authenticated user", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
issueList, err := issues.LoadIssuesFromColumn(db.DefaultContext, column, user2, org, optional.None[bool]())
require.NoError(t, err)
assert.Len(t, issueList, 1)
assert.EqualValues(t, 6, issueList[0].ID)
issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, orgProject, user2, org, optional.None[bool]())
require.NoError(t, err)
assert.EqualValues(t, 1, issuesNum)
issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, user2, org, optional.Some(true))
require.NoError(t, err)
assert.EqualValues(t, 0, issuesNum)
issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, user2, org, optional.Some(false))
require.NoError(t, err)
assert.EqualValues(t, 1, issuesNum)
})
t.Run("Anonymous user", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
issueList, err := issues.LoadIssuesFromColumn(db.DefaultContext, column, nil, org, optional.None[bool]())
require.NoError(t, err)
assert.Empty(t, issueList)
issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, orgProject, nil, org, optional.None[bool]())
require.NoError(t, err)
assert.EqualValues(t, 0, issuesNum)
})
})
t.Run("User project", func(t *testing.T) {
userProject := unittest.AssertExistsAndLoadBean(t, &project.Project{ID: 1002, OwnerID: user2.ID})
column := unittest.AssertExistsAndLoadBean(t, &project.Column{ID: 1002, ProjectID: userProject.ID})
t.Run("Authenticated user", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
issueList, err := issues.LoadIssuesFromColumn(db.DefaultContext, column, user2, nil, optional.None[bool]())
require.NoError(t, err)
assert.Len(t, issueList, 1)
assert.EqualValues(t, 7, issueList[0].ID)
issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, userProject, user2, nil, optional.None[bool]())
require.NoError(t, err)
assert.EqualValues(t, 1, issuesNum)
issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, userProject, user2, nil, optional.Some(true))
require.NoError(t, err)
assert.EqualValues(t, 0, issuesNum)
issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, userProject, user2, nil, optional.Some(false))
require.NoError(t, err)
assert.EqualValues(t, 1, issuesNum)
})
t.Run("Anonymous user", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
issueList, err := issues.LoadIssuesFromColumn(db.DefaultContext, column, nil, nil, optional.None[bool]())
require.NoError(t, err)
assert.Empty(t, issueList)
issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, userProject, nil, nil, optional.None[bool]())
require.NoError(t, err)
assert.EqualValues(t, 0, issuesNum)
})
})
}

View file

@ -0,0 +1,84 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package integration
import (
"net/http"
"strings"
"testing"
org_model "code.gitea.io/gitea/models/organization"
project_model "code.gitea.io/gitea/models/project"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
)
func TestPrivateIssueProject(t *testing.T) {
defer tests.AddFixtures("models/fixtures/PrivateIssueProjects/")()
defer tests.PrepareTestEnv(t)()
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
sess := loginUser(t, user2.Name)
test := func(t *testing.T, sess *TestSession, username string, projectID int64, hasAccess bool) {
t.Helper()
defer tests.PrintCurrentTest(t, 1)()
// Test that the projects overview page shows the correct open and close issues.
req := NewRequestf(t, "GET", "%s/-/projects", username)
resp := sess.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
openCloseStats := htmlDoc.Find(".milestone-toolbar .group").First().Text()
if hasAccess {
assert.Contains(t, openCloseStats, "1\u00a0Open")
} else {
assert.Contains(t, openCloseStats, "0\u00a0Open")
}
assert.Contains(t, openCloseStats, "0\u00a0Closed")
// Check that on the project itself the issue is not shown.
req = NewRequestf(t, "GET", "%s/-/projects/%d", username, projectID)
resp = sess.MakeRequest(t, req, http.StatusOK)
htmlDoc = NewHTMLParser(t, resp.Body)
htmlDoc.AssertElement(t, ".project-column .issue-card", hasAccess)
// And that the issue count is correct.
issueCount := strings.TrimSpace(htmlDoc.Find(".project-column-issue-count").Text())
if hasAccess {
assert.EqualValues(t, "1", issueCount)
} else {
assert.EqualValues(t, "0", issueCount)
}
}
t.Run("Organization project", func(t *testing.T) {
org := unittest.AssertExistsAndLoadBean(t, &org_model.Organization{ID: 3})
orgProject := unittest.AssertExistsAndLoadBean(t, &project_model.Project{ID: 1001, OwnerID: org.ID})
t.Run("Authenticated user", func(t *testing.T) {
test(t, sess, org.Name, orgProject.ID, true)
})
t.Run("Anonymous user", func(t *testing.T) {
test(t, emptyTestSession(t), org.Name, orgProject.ID, false)
})
})
t.Run("User project", func(t *testing.T) {
userProject := unittest.AssertExistsAndLoadBean(t, &project_model.Project{ID: 1002, OwnerID: user2.ID})
t.Run("Authenticated user", func(t *testing.T) {
test(t, sess, user2.Name, userProject.ID, true)
})
t.Run("Anonymous user", func(t *testing.T) {
test(t, emptyTestSession(t), user2.Name, userProject.ID, false)
})
})
}