fix: add missing checks to several API and web handlers (#39501) (#39507)

Backport #39501 by @silverwind

Several handlers skipped checks that their sibling routes or settings
already enforce. This brings them in line.

- Push mirror API honors `DISABLE_NEW_PUSH` and checks the caller's
permission
- Media API serves small files with the usual content headers
- Issue attachment API ignores comment attachments
- Push-to-create respects `FORCE_PRIVATE`
- Profile feeds and follow actions respect `ENABLE_FEED` and owner
visibility
- Tag delete route refuses release tags
- Refresh token grant only accepts refresh tokens
- Gitea migrations bound the source's page size

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: bircni <bircni@icloud.com>
This commit is contained in:
Giteabot
2026-09-30 11:46:59 -07:00
committed by GitHub
parent 1e28bb1bd7
commit 2d58c8c3df
18 changed files with 124 additions and 27 deletions
@@ -38,6 +38,11 @@ func TestAPIGetIssueAttachment(t *testing.T) {
apiAttachment := DecodeJSON(t, resp, &api.Attachment{})
unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: apiAttachment.ID, IssueID: issue.ID})
commentAttachment := unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: 3, RepoID: repo.ID})
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets/%d", repoOwner.Name, repo.Name, unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: commentAttachment.IssueID}).Index, commentAttachment.ID)).
AddTokenAuth(token)
session.MakeRequest(t, req, http.StatusNotFound)
}
func TestAPIListIssueAttachments(t *testing.T) {
@@ -22,6 +22,10 @@ func TestAPIGetRawFileOrLFS(t *testing.T) {
resp := MakeRequest(t, req, http.StatusOK)
assert.Equal(t, "# repo1\n\nDescription for repo1", resp.Body.String())
req = NewRequest(t, "GET", "/api/v1/repos/user2/repo2/media/test.xml").AddTokenAuth(getUserToken(t, "user2", auth_model.AccessTokenScopeReadRepository))
resp = MakeRequest(t, req, http.StatusOK)
assert.Equal(t, "text/plain; charset=utf-8", resp.Header().Get("Content-Type"))
// Test with LFS
onGiteaRun(t, func(t *testing.T, u *url.URL) {
createLFSTestRepository(t, "repo-lfs-test")
+10
View File
@@ -16,6 +16,8 @@ import (
user_model "gitea.dev/models/user"
"gitea.dev/modules/git"
"gitea.dev/modules/git/gitcmd"
"gitea.dev/modules/setting"
"gitea.dev/modules/test"
repo_service "gitea.dev/services/repository"
"github.com/stretchr/testify/assert"
@@ -175,6 +177,14 @@ func TestGitPushVisibilityOption(t *testing.T) {
doGitPushTestRepository(gitPath, "origin", "branch2", "-o", "repo.private=false")(t)
repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo.ID})
assert.True(t, repo.IsPrivate, "repo.private option must be ignored on an existing repository")
defer test.MockVariableValue(&setting.Repository.ForcePrivate, true)()
forcedRepo, err := repo_service.CreateRepository(t.Context(), user, user, repo_service.CreateRepoOptions{Name: "repo-visibility-forced", DefaultBranch: "master", IsPrivate: true})
require.NoError(t, err)
u.Path = forcedRepo.FullName() + ".git"
doGitAddRemote(gitPath, "forced", u)(t)
doGitPushTestRepository(gitPath, "forced", "master", "-o", "repo.private=false")(t)
assert.True(t, unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: forcedRepo.ID}).IsPrivate)
})
}
+9
View File
@@ -571,6 +571,15 @@ func testRefreshTokenInvalidation(t *testing.T) {
assert.Equal(t, "unauthorized_client", string(parsedError.ErrorCode))
assert.Equal(t, "unable to parse refresh token", parsedError.ErrorDescription)
req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
"grant_type": "refresh_token",
"client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
"client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
"redirect_uri": "https://example.com",
"refresh_token": parsed.AccessToken,
})
MakeRequest(t, req, http.StatusBadRequest)
req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
"grant_type": "refresh_token",
"client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
+9
View File
@@ -84,6 +84,7 @@ func testViewLimitedAndPrivateUserAndRename(t *testing.T) {
org22 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 22})
req := NewRequest(t, "GET", "/"+org22.Name)
MakeRequest(t, req, http.StatusNotFound)
MakeRequest(t, NewRequest(t, "GET", "/"+org22.Name).SetHeader("Accept", "application/rss+xml"), http.StatusNotFound)
session := loginUser(t, "user1")
oldName := org22.Name
@@ -106,6 +107,8 @@ func testViewLimitedAndPrivateUserAndRename(t *testing.T) {
org23 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 23})
req = NewRequest(t, "GET", "/"+org23.Name)
MakeRequest(t, req, http.StatusNotFound)
strangerSession := loginUser(t, "user4")
strangerSession.MakeRequest(t, NewRequest(t, "POST", "/"+org23.Name+"?action=follow"), http.StatusNotFound)
oldName = org23.Name
newName = "org23_renamed"
@@ -127,6 +130,8 @@ func testViewLimitedAndPrivateUserAndRename(t *testing.T) {
user31 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 31})
req = NewRequest(t, "GET", "/"+user31.Name)
MakeRequest(t, req, http.StatusNotFound)
MakeRequest(t, NewRequest(t, "GET", "/"+user31.Name).SetHeader("Accept", "application/rss+xml"), http.StatusNotFound)
strangerSession.MakeRequest(t, NewRequest(t, "POST", "/"+user31.Name+"?action=follow"), http.StatusNotFound)
oldName = user31.Name
newName = "user31_renamed"
@@ -330,6 +335,10 @@ func testGetUserRss(t *testing.T) {
session := loginUser(t, "user2")
req = NewRequestf(t, "GET", "/non-existent-user.rss")
session.MakeRequest(t, req, http.StatusNotFound)
defer test.MockVariableValue(&setting.Other.EnableFeed, false)()
MakeRequest(t, NewRequestf(t, "GET", "/%s.rss", user34), http.StatusNotFound)
MakeRequest(t, NewRequestf(t, "GET", "/%s", user34).SetHeader("Accept", "application/rss+xml"), http.StatusNotFound)
}
func testUserListStopWatches(t *testing.T) {