{"id":"GHSA-3fwp-p5rj-2pxf","summary":"Gitea: Missing repository-unit authorization on issue-template API endpoints","details":"## Summary\n\nThree Gitea API endpoints — `GET /repos/{owner}/{repo}/issue_templates`,\n`GET /repos/{owner}/{repo}/issue_config` and `GET /repos/{owner}/{repo}/issue_config/validate`\n— read files from the repository's **Code** default branch (`.gitea/ISSUE_TEMPLATE/*`\nand `issue_config.yaml`) and return their contents, but are registered **without**\nthe `reqRepoReader(unit.TypeCode)` authorization middleware that every sibling\nCode-tree endpoint in the same route group carries.\n\nA user who has access to a private repository through *any single repository unit*\n(for example an organization team granted only the **Issues** unit, with no Code\naccess) can therefore read the issue-template and issue-config files of that\nrepository's Code tree, which their permission set should not expose.\n\n---\n\n## Root cause\n\n### The three endpoints lack the unit guard\n\n`routers/api/v1/api.go:1433-1437`:\n\n    m.Get(\"/issue_templates\", context.ReferencesGitRepo(), repo.GetIssueTemplates)\n    m.Get(\"/issue_config\", context.ReferencesGitRepo(), repo.GetIssueConfig)\n    m.Get(\"/issue_config/validate\", context.ReferencesGitRepo(), repo.ValidateIssueConfig)\n    m.Get(\"/languages\", reqRepoReader(unit.TypeCode), repo.GetLanguages)\n    m.Get(\"/licenses\", reqRepoReader(unit.TypeCode), repo.GetLicenses)\n\n`context.ReferencesGitRepo()` only opens the git repository — it performs no\npermission check. Every other endpoint in this group that reads Code-tree content\nis guarded with `reqRepoReader(unit.TypeCode)`: `/languages`, `/licenses`,\n`/contents/*`, `/file-contents`, and `/{ball_type:tarball|zipball|bundle}/*`\n(api.go:1418-1445). The three issue-template endpoints are the only Code-tree\nreaders in the group missing that guard.\n\nThe enclosing group runs `repoAssignment()` (api.go:1446), whose access check is\nsatisfied by `HasAnyUnitAccessOrPublicAccess` — i.e. access to **any** unit of the\nrepository is sufficient to pass. Without a per-unit `reqRepoReader`, the handlers\nrun for a caller who has no Code permission.\n\n### The handlers return Code-tree file contents\n\n`routers/api/v1/repo/repo.go`:\n\n    func GetIssueTemplates(ctx *context.APIContext) {                       // :1179\n        ret := issue.ParseTemplatesFromDefaultBranch(ctx.Repo.Repository, ctx.Repo.GitRepo)\n        ...\n        ctx.JSON(http.StatusOK, ret.IssueTemplates)\n    }\n\n    func GetIssueConfig(ctx *context.APIContext) {                          // :1209\n        issueConfig, _ := issue.GetTemplateConfigFromDefaultBranch(ctx.Repo.Repository, ctx.Repo.GitRepo)\n        ctx.JSON(http.StatusOK, issueConfig)\n    }\n\n`ParseTemplatesFromDefaultBranch` / `GetTemplateConfigFromDefaultBranch` read\n`.gitea/ISSUE_TEMPLATE/*` and `issue_config.yaml` from the default (Code) branch\nand return them in the JSON response.\n\n---\n\n## Proof of Concept\n\n`victim-org/private-repo` is a private repository. The attacker is a member of an\norganization team granted access to that repository through a non-Code unit only\n(e.g. the Issues unit) — a supported Gitea permission configuration.\n\n    GET /api/v1/repos/victim-org/private-repo/issue_templates HTTP/1.1\n    Host: TARGET\n    Authorization: token \u003cattacker token\u003e\n\nThe response is `200 OK` with the parsed contents of the repository's\n`.gitea/ISSUE_TEMPLATE/*` files. The same applies to `/issue_config`. Because the\ncaller lacks the Code unit, every other Code-tree endpoint\n(`/contents`, `/languages`, …) correctly returns `404`/`403` for the same token —\nonly these three return data.\n\n---\n\n## Impact\n\nA repository collaborator whose granted permissions exclude the Code unit can read\nthe issue-template and issue-config files from the Code default branch of a private\nrepository. The exposure is limited to those specific configuration files (not\narbitrary Code-tree content), which is why this is rated low impact. It is\nnonetheless a unit-level authorization bypass: the endpoints disclose Code-unit\ncontent to callers the permission model is meant to exclude.\n\n---\n\n## Suggested fix\n\nAdd the same unit guard the sibling endpoints use, in `routers/api/v1/api.go`:\n\n    m.Get(\"/issue_templates\", reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(), repo.GetIssueTemplates)\n    m.Get(\"/issue_config\", reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(), repo.GetIssueConfig)\n    m.Get(\"/issue_config/validate\", reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(), repo.ValidateIssueConfig)\n\n(If issue templates are intended to be visible to Issues-unit users for issue\ncreation, `reqRepoReader(unit.TypeIssues)` is the appropriate guard — but the\ncurrent absence of any unit guard is the bug.)\n\n---\n\n## References\n\n- CWE-862 Missing Authorization\n- CWE-284 Improper Access Control\n- OWASP A01:2021 Broken Access Control","aliases":["CVE-2026-27783","GO-2026-5081"],"modified":"2026-06-25T18:56:13.658512313Z","published":"2026-06-16T23:41:42Z","database_specific":{"github_reviewed":true,"github_reviewed_at":"2026-06-16T23:41:42Z","nvd_published_at":null,"cwe_ids":["CWE-862"],"severity":"MODERATE"},"references":[{"type":"WEB","url":"https://github.com/go-gitea/gitea/security/advisories/GHSA-3fwp-p5rj-2pxf"},{"type":"PACKAGE","url":"https://github.com/go-gitea/gitea"}],"affected":[{"package":{"name":"code.gitea.io/gitea","ecosystem":"Go","purl":"pkg:golang/code.gitea.io/gitea"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"1.26.2"}]}],"database_specific":{"last_known_affected_version_range":"\u003c= 1.26.1","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/06/GHSA-3fwp-p5rj-2pxf/GHSA-3fwp-p5rj-2pxf.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N"}]}