Files
gitea/routers/api/v1/repo/issue_comment_test.go
d4ecd5eadf fix(api): check unit read permission in issue sub-resource endpoints (#39885)
Several `/repos/{owner}/{repo}/issues/{index}/...` API endpoints load
the issue by index without checking
`CanReadIssuesOrPulls(issue.IsPull)`, unlike `GetIssue`,
`ListIssueComments` and `GetIssueReactions`. The route group's
`mustEnableIssuesOrPulls` passes when the caller can read either unit,
so someone who can read issues but not pull requests (a team with only
the Issues unit, or per-unit anonymous access on a public repo) can read
a pull request's whole conversation through `/timeline`, plus its
labels, subscribers, attachments and tracked times, and can subscribe to
it. The reverse holds for issues.

These handlers now return 404 in that case, like their siblings.

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-22 12:04:20 +02:00

45 lines
1.3 KiB
Go

// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repo
import (
"net/http"
"testing"
perm_model "gitea.dev/models/perm"
access_model "gitea.dev/models/perm/access"
repo_model "gitea.dev/models/repo"
"gitea.dev/models/unit"
"gitea.dev/models/unittest"
"gitea.dev/services/context"
"gitea.dev/services/contexttest"
"github.com/stretchr/testify/assert"
)
func TestIssueReadEndpointsRequireUnitPermission(t *testing.T) {
unittest.PrepareTestEnv(t)
for name, handler := range map[string]func(*context.APIContext){
"timeline": ListIssueCommentsAndTimeline,
"labels": ListIssueLabels,
"subscribers": GetIssueSubscribers,
"attachments": ListIssueAttachments,
} {
t.Run(name, func(t *testing.T) {
request := func(index string) int {
ctx, resp := contexttest.MockAPIContext(t, "user2/repo1")
contexttest.LoadRepo(t, ctx, 1)
ctx.Repo.Permission = access_model.Permission{}
ctx.Repo.Permission.SetUnitsWithDefaultAccessMode([]*repo_model.RepoUnit{{Type: unit.TypeIssues}}, perm_model.AccessModeRead)
ctx.SetPathParam("index", index)
handler(ctx)
return resp.Code
}
assert.Equal(t, http.StatusOK, request("1"))
assert.Equal(t, http.StatusNotFound, request("2"))
})
}
}