fix(security): harden access checks and migration validation (#38324) (#38400)

This commit is contained in:
Giteabot
2026-07-10 11:14:38 -07:00
committed by GitHub
parent af7ba2edef
commit ed493f0684
15 changed files with 304 additions and 15 deletions
@@ -6,6 +6,7 @@ package integration
import (
"fmt"
"net/http"
"strconv"
"testing"
auth_model "gitea.dev/models/auth"
@@ -150,3 +151,47 @@ func TestAPIDeleteIssueDependencyCrossRepoPermission(t *testing.T) {
DependencyID: dependencyIssue.ID,
})
}
func TestWebDeleteIssueDependencyCrossRepoPermission(t *testing.T) {
defer tests.PrepareTestEnv(t)()
targetRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
targetIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: targetRepo.ID, Index: 1})
dependencyRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
assert.True(t, dependencyRepo.IsPrivate)
dependencyIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: dependencyRepo.ID, Index: 1})
enableRepoDependencies(t, targetIssue.RepoID)
enableRepoDependencies(t, dependencyRepo.ID)
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
assert.NoError(t, issues_model.CreateIssueDependency(t.Context(), user1, targetIssue, dependencyIssue))
user40 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 40})
assert.NoError(t, repo_service.AddOrUpdateCollaborator(t.Context(), targetRepo, user40, perm.AccessModeWrite))
session := loginUser(t, user40.Name)
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/user2/repo1/issues/%d/dependency/delete", targetIssue.Index), map[string]string{
"removeDependencyID": strconv.FormatInt(dependencyIssue.ID, 10),
"dependencyType": "blockedBy",
})
session.MakeRequest(t, req, http.StatusSeeOther)
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueDependency{
IssueID: targetIssue.ID,
DependencyID: dependencyIssue.ID,
})
assert.NoError(t, repo_service.AddOrUpdateCollaborator(t.Context(), dependencyRepo, user40, perm.AccessModeRead))
req = NewRequestWithValues(t, "POST", fmt.Sprintf("/user2/repo1/issues/%d/dependency/delete", targetIssue.Index), map[string]string{
"removeDependencyID": strconv.FormatInt(dependencyIssue.ID, 10),
"dependencyType": "blockedBy",
})
session.MakeRequest(t, req, http.StatusSeeOther)
unittest.AssertNotExistsBean(t, &issues_model.IssueDependency{
IssueID: targetIssue.ID,
DependencyID: dependencyIssue.ID,
})
}
@@ -11,10 +11,13 @@ import (
auth_model "gitea.dev/models/auth"
issues_model "gitea.dev/models/issues"
"gitea.dev/models/perm"
repo_model "gitea.dev/models/repo"
"gitea.dev/models/unittest"
user_model "gitea.dev/models/user"
"gitea.dev/modules/json"
api "gitea.dev/modules/structs"
repo_service "gitea.dev/services/repository"
"gitea.dev/tests"
"github.com/stretchr/testify/assert"
@@ -100,6 +103,30 @@ func TestAPIGetTrackedTimesNonExistentUserFilter(t *testing.T) {
})
}
func TestAPIUserTrackedTimesOmitsInaccessiblePrivateIssues(t *testing.T) {
defer tests.PrepareTestEnv(t)()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 40})
privateRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
assert.True(t, privateRepo.IsPrivate)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: privateRepo.ID})
assert.NoError(t, repo_service.AddOrUpdateCollaborator(t.Context(), privateRepo, user, perm.AccessModeRead))
_, err := issues_model.AddTime(t.Context(), user, issue, 60, time.Time{})
assert.NoError(t, err)
assert.NoError(t, repo_service.DeleteCollaboration(t.Context(), privateRepo, user))
token := getUserToken(t, user.Name, auth_model.AccessTokenScopeReadUser)
req := NewRequest(t, "GET", "/api/v1/user/times").AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
trackedTimes := DecodeJSON(t, resp, api.TrackedTimeList{})
for _, trackedTime := range trackedTimes {
if assert.NotNil(t, trackedTime.Issue) {
assert.NotEqual(t, issue.ID, trackedTime.Issue.ID)
}
}
}
func TestAPIDeleteTrackedTime(t *testing.T) {
defer tests.PrepareTestEnv(t)()
+25
View File
@@ -20,6 +20,7 @@ import (
"gitea.dev/modules/structs"
api "gitea.dev/modules/structs"
"gitea.dev/services/convert"
repo_service "gitea.dev/services/repository"
"gitea.dev/tests"
"github.com/stretchr/testify/assert"
@@ -306,6 +307,30 @@ func TestAPIGetTeamRepo(t *testing.T) {
MakeRequest(t, req, http.StatusNotFound)
}
func TestAPIAddRemoveTeamRepositoryRequiresOrgOwnerOrSetting(t *testing.T) {
defer tests.PrepareTestEnv(t)()
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 12})
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: team.OrgID})
assert.False(t, org.RepoAdminChangeTeamAccess)
targetRepo := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 32, OwnerID: org.ID})
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 28})
assert.NoError(t, repo_service.AddOrUpdateCollaborator(t.Context(), targetRepo, user, perm.AccessModeAdmin))
token := getUserToken(t, user.Name, auth_model.AccessTokenScopeWriteOrganization)
url := fmt.Sprintf("/api/v1/teams/%d/repos/%s", team.ID, targetRepo.FullName())
req := NewRequest(t, "PUT", url).AddTokenAuth(token)
MakeRequest(t, req, http.StatusForbidden)
unittest.AssertNotExistsBean(t, &organization.TeamRepo{TeamID: team.ID, RepoID: targetRepo.ID})
assert.NoError(t, db.Insert(t.Context(), &organization.TeamRepo{OrgID: org.ID, TeamID: team.ID, RepoID: targetRepo.ID}))
req = NewRequest(t, "DELETE", url).AddTokenAuth(token)
MakeRequest(t, req, http.StatusForbidden)
unittest.AssertExistsAndLoadBean(t, &organization.TeamRepo{TeamID: team.ID, RepoID: targetRepo.ID})
}
func TestAPITeamVisibilityAccess(t *testing.T) {
defer tests.PrepareTestEnv(t)()
insertTestTeam := func(t *testing.T, orgID int64, name string, visibility structs.VisibleType) *organization.Team {
+20
View File
@@ -224,3 +224,23 @@ func TestAPIStarPublicOnly(t *testing.T) {
require.Len(t, repos, 1)
assert.Equal(t, "user5/repo4", repos[0].FullName)
}
func TestAPIStarredReposOmitsInaccessiblePrivateRepos(t *testing.T) {
defer tests.PrepareTestEnv(t)()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 40})
privateRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
assert.True(t, privateRepo.IsPrivate)
assert.NoError(t, repo_service.AddOrUpdateCollaborator(t.Context(), privateRepo, user, perm.AccessModeRead))
assert.NoError(t, repo_model.StarRepo(t.Context(), user, privateRepo, true))
assert.NoError(t, repo_service.DeleteCollaboration(t.Context(), privateRepo, user))
token := getUserToken(t, user.Name, auth_model.AccessTokenScopeReadUser, auth_model.AccessTokenScopeReadRepository)
req := NewRequest(t, "GET", "/api/v1/user/starred").AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
repos := DecodeJSON(t, resp, []api.Repository{})
for _, repo := range repos {
assert.NotEqual(t, privateRepo.FullName(), repo.FullName)
}
}