We are excited to announce the release of Gitea 28.1.0, the first maintenance release for the 28 series. It contains security fixes and fixes for regressions reported after 28.0.0, including pushes to protected branches from Actions, the npm registry, and MySQL, MariaDB and MSSQL transactions. We strongly recommend that all users upgrade as soon as possible.
Security
Section titled “Security”This release addresses the following security vulnerabilities. Please upgrade as soon as possible.
- CVE-2026-97208: The push mirror API did not check
[mirror] DISABLE_NEW_PUSH, so repository administrators could create push mirrors after the site administrator had disabled them. Fixed by #39501 / #39507. Thanks to @tao0845 for reporting the issue, and to @silverwind and @bircni for the patch. - CVE-2026-86684: The push mirror API checked the repository owner’s permission to use local paths instead of the caller’s, so a repository administrator could push to a local Git repository on the server when
IMPORT_LOCAL_PATHSis enabled and the owner may import local paths. Fixed by #39501 / #39507. Thanks to @mayank-jangid-moon, @Kushalkhemka and @skigeek16 for reporting the issue with @opensec-intelligence, and to @silverwind and @bircni for the patch. - CVE-2026-96594: The repository media API wrote small non-LFS files without the usual user-content headers, so an HTML file in a repository was rendered on the Gitea origin (stored XSS). Fixed by #39501 / #39507. Thanks to @KadirArslan and @cruzzer for reporting the issue, and to @silverwind and @bircni for the patch.
- CVE-2026-101023: The OAuth2 refresh token grant did not check the token kind, so an unexpired access token could be exchanged for new access and refresh tokens, extending access beyond its lifetime. Fixed by #39501 / #39507. Thanks to @rezmoss, @manus-use, @danieltk76, @gigioneggiando, @DshtAnger and @manus-pi for reporting the issue, and to @silverwind and @bircni for the patch.
- CVE-2026-105268: The issue attachment API accepted attachments that belong to comments on the issue, so an issue author could rename or delete attachments other users had posted in comments. Fixed by #39501 / #39507. Thanks to @N0K0 for reporting the issue, and to @silverwind and @bircni for the patch.
- CVE-2026-89182: Push-to-create with
-o repo.private=falseignored[repository] FORCE_PRIVATE, so non-admin users could create public repositories. Fixed by #39501 / #39507. Thanks to @manus-pi for reporting the issue, and to @silverwind and @bircni for the patch. - CVE-2026-97626: Requesting a profile page with an RSS or Atom
Acceptheader returned the activity feed of private and limited users and organizations without a visibility check, also whenENABLE_FEEDwas disabled. Fixed by #39501 / #39507. Thanks to @tienpa99 for reporting the issue, @Black1hp and @Mon3mRT for finding it, and to @silverwind and @bircni for the patch. - CVE-2026-105267: The tag delete route, which only requires code write access, also deleted releases and their attachments. Tags attached to a release can no longer be deleted through it. Fixed by #39501 / #39507. Thanks to @N0K0 for reporting the issue, and to @silverwind and @bircni for the patch.
- CVE-2026-104633: When migrating from another Gitea instance, a source reporting a page size of
0made the downloader loop forever and exhaust server memory. The page size is now bounded. Fixed by #39501 / #39507. Thanks to @kewmine for reporting the issue, and to @silverwind and @bircni for the patch.
We are grateful to all the security researchers who responsibly disclosed these issues.
Notable fixes
Section titled “Notable fixes”Accounts
Section titled “Accounts”- Bot accounts with a pending password change can use the API again (#39551), and users can approve OAuth2 scope changes on an existing grant instead of getting an error (#38942).
Actions
Section titled “Actions”- Workflows can push to protected branches again (#39564).
- The status of a reusable workflow caller is refreshed when its child jobs are skipped (#39589), and the run list keeps its order after an automatic refresh (#39479).
Database and Git
Section titled “Database and Git”- MySQL and MariaDB use
READ COMMITTEDtransactions (#39506) and MSSQL usesREAD_COMMITTED_SNAPSHOT(#39512), and commit status queries are chunked to stay below SQLite’s expression depth limit (#39611). - The go-git storage tolerates concurrent repacks (#39510, #39536), and branch names with special characters are handled correctly (#39483).
- Pull request head refs are fetched instead of pushed (#39603), and AGit pull requests refresh their “commits behind” count when updated (#39613).
Egress
Section titled “Egress”- Private, link-local, CGNAT and documentation ranges are now classified as restricted instead of reserved, so they can be dialed when explicitly allowed in
laxmode (#39560).
Packages and UI
Section titled “Packages and UI”- The npm registry route works again and npm client compatibility is improved (#39488, #39434).
- The image captcha works with the content security policy (#39555), the organizations tab is back on user profiles (#39577), and MathML rendering is fixed (#39491, #39493, #39497).
This release includes 38 merged pull requests, thanks to the amazing contributions from our community.
How to install or update
Section titled “How to install or update”Download our pre-built binaries from the Gitea downloads page — make sure to select the version compatible with your platform. For a step-by-step guide on installation or upgrades, check out our installation documentation
Special Thanks
Section titled “Special Thanks”We would also like to thank all of our supporters on Open Collective who are helping to sustain us financially.
Looking for a seamless, hassle-free solution to manage your Git repositories? Discover Gitea Cloud — A fully-managed, scalable platform designed to streamline your development workflow.
Changelog
Section titled “Changelog”28.1.0 - 2026-10-06
Section titled “28.1.0 - 2026-10-06”Enhancements
Section titled “Enhancements”Bug Fixes
Section titled “Bug Fixes”- (actions) Keep runs order after auto refresh (#39479) (#39481)
- Npm route (#39488) (#39490)
- Copy new access token to clipboard (#39496) (#39499)
- (markup) Skip post-processing inside MathML (#39497) (#39502)
- (markup) Don’t escape ambiguous characters in MathML (#39493) (#39505)
- Add missing checks to several API and web handlers (#39501) (#39507)
- Handle git branch name with special chars correctly (#39483) (#39515)
- Trace git command correctly (#39520) (#39524)
- (git) Reindex go-git storage when a concurrent repack removes packs (#39510) (#39534)
- (oauth2) Allow users to approve scope changes (#38942) (#39521)
- (api) Add index tiebreaker to commit status ordering (#39508) (#39525)
- (git) Avoid unnecessary timers during language stats (#39531) (#39535)
- Use READ COMMITTED transactions on MySQL and MariaDB (#39506) (#39537)
- (markup) Use installed math fonts for MathML in Chromium (#39491) (#39547)
- (git) Tolerate concurrent repacks in go-git storage (#39536) (#39556)
- Make image captcha work with csp (#39555)
- Use READ_COMMITTED_SNAPSHOT on MSSQL (#39512) (#39558)
- Make navbar stopwatch button can show popup (#39562)
- (actions) Restore pushes to protected branches (#39564) (#39567)
- (api) Allow bots with pending password changes (#39551) (#39566)
- (user) Restore organizations tab on user profile (#39577) (#39581)
- (actions) Return 401 for unregistered runner (#39578) (#39585)
- (markup) Link team mentions that use a different org name case (#39436) (#39588)
- (httpcache) Raw files return 304 after a change when the new commit is older (#39435) (#39591)
- (git) Return no submodule web link when the URL cannot be parsed (#39274) (#39592)
- (pull) Fetch PR head refs instead of pushing them (#39603) (#39607)
- Migrate broken team authorize access mode (#39579) (#39580)
- (actions) Refresh reusable caller status when children are skipped (#39589) (#39609)
- Make “edit pr title & target branch” get correct branch (#39612) (#39621)
- (egress) Expose more ranges as restricted rather than reserved (#39560) (#39624)
- (web) Normalize content for edit history diff and fix comment history dropdown (#39616) (#39627)
- Correct RemoveWithRetry error handling (#39619) (#39626)
- (pull) Refresh commits behind when an AGit pull request is updated (#39613) (#39625)
- (models) Chunk commit status queries to avoid SQLite expression depth limit (#39611) (#39638)
- Show “merge” form for empty PR (#39640) (#39645)
Miscellaneous
Section titled “Miscellaneous”- Return 401 for unregistered runner (#39599)

