Skip to content

fix: enforce public-only token scope and harden push options / locale parsing - #38323

Merged
bircni merged 8 commits into
go-gitea:mainfrom
bircni:fix/security-token-scope-and-dos
Jul 10, 2026
Merged

fix: enforce public-only token scope and harden push options / locale parsing#38323
bircni merged 8 commits into
go-gitea:mainfrom
bircni:fix/security-token-scope-and-dos

Conversation

@bircni

@bircni bircni commented Jul 3, 2026

Copy link
Copy Markdown
Member

Three independent security hardening fixes, each with a regression test:

  • Locale DoS: the Locale middleware passed the raw Accept-Language header to ParseAcceptLanguage, whose guard only counts - while the scanner aliases _ to - — a large _-separated header on an unauthenticated request burned CPU. The header is now length-bounded before parsing.
  • Public-only token scope: GET /teams/{id}/repos, .../repos/{org}/{repo}, /teams/{id}/activities/feeds, and /users/{username}/orgs/{org}/permissions still returned private repo/activity/permission data to a public-only token. They now filter via TokenCanAccessRepo / ApplyPublicOnly and reject non-public org permissions.
  • Push-option visibility: repo.private / repo.template push options were applied to any existing repo, letting an owner/admin silently flip visibility bypassing audit, webhooks, and notifications. They are now honored only on push-to-create.

bircni added 3 commits July 3, 2026 19:51
The Locale middleware runs on every unauthenticated request and passed the
raw Accept-Language header to language.ParseAcceptLanguage. That parser has
quadratic-time behavior on long malformed inputs; its guard only counts "-"
separators while the scanner aliases "_" to "-", so a single unauthenticated
request with a large "_"-separated header burns seconds of CPU. Bound the
header length before parsing (only the leading languages are used anyway).
…endpoints

A public-only access token could still read private data through routes whose
handlers did not apply the restriction:

- GET /api/v1/teams/{id}/repos and .../repos/{org}/{repo} returned private team
  repository metadata (the doer's own access was checked, but not the token's
  public-only scope).
- GET /api/v1/teams/{id}/activities/feeds returned private activity entries.
- GET /api/v1/users/{username}/orgs/{org}/permissions disclosed membership
  permissions for a non-public org.

Filter repos via TokenCanAccessRepo, apply ApplyPublicOnly to the team feed, and
reject public-only access to non-public org permissions.
…-to-create

The post-receive hook applied the repo.private and repo.template git push
options to any existing repository, letting a repo owner or admin collaborator
silently toggle visibility with a bare "git push -o repo.private=..." while
bypassing the audit trail, webhook, and notifications a settings change fires.
These options are only meant to set the initial state during push-to-create, so
skip them once the repository is populated.
@GiteaBot GiteaBot added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Jul 3, 2026
@bircni bircni added the backport/v1.27 This PR should be backported to Gitea 1.27 label Jul 3, 2026
@GiteaBot GiteaBot added lgtm/need 1 This PR needs approval from one additional maintainer to be merged. and removed lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. labels Jul 5, 2026
}

// FIXME: these options are not quite right, for example: changing visibility should do more works than just setting the is_private flag
// These options should only be used for "push-to-create"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"FIXME"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to clean up the FIXME now?

Comment thread routers/private/hook_post_receive.go Outdated
Comment thread routers/private/hook_post_receive.go Outdated
Comment thread tests/integration/api_public_only_test.go Outdated
Comment thread tests/integration/git_push_test.go Outdated
@bircni
bircni marked this pull request as draft July 5, 2026 15:38
Comment thread routers/private/hook_post_receive.go Outdated
@bircni
bircni marked this pull request as ready for review July 7, 2026 16:31
@bircni bircni mentioned this pull request Jul 7, 2026
22 tasks
@bircni
bircni requested a review from wxiaoguang July 9, 2026 13:02
Comment thread routers/api/v1/org/team.go
Comment thread routers/private/hook_post_receive.go
@bircni
bircni requested review from Zettat123 and wxiaoguang July 10, 2026 16:10
@GiteaBot GiteaBot removed the lgtm/need 1 This PR needs approval from one additional maintainer to be merged. label Jul 10, 2026
@GiteaBot GiteaBot added the lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. label Jul 10, 2026
@bircni
bircni enabled auto-merge (squash) July 10, 2026 16:22
@bircni
bircni merged commit f452c36 into go-gitea:main Jul 10, 2026
27 checks passed
@GiteaBot GiteaBot added this to the 28.0.0 milestone Jul 10, 2026
@GiteaBot GiteaBot added the backport/done All backports for this PR have been created label Jul 10, 2026
bircni added a commit that referenced this pull request Jul 10, 2026
… parsing (#38323) (#38399)

Backport #38323 by @bircni

Three independent security hardening fixes, each with a regression test:

- **Locale DoS:** the `Locale` middleware passed the raw
`Accept-Language` header to `ParseAcceptLanguage`, whose guard only
counts `-` while the scanner aliases `_` to `-` — a large `_`-separated
header on an unauthenticated request burned CPU. The header is now
length-bounded before parsing.
- **Public-only token scope:** `GET /teams/{id}/repos`,
`.../repos/{org}/{repo}`, `/teams/{id}/activities/feeds`, and
`/users/{username}/orgs/{org}/permissions` still returned private
repo/activity/permission data to a public-only token. They now filter
via `TokenCanAccessRepo` / `ApplyPublicOnly` and reject non-public org
permissions.
- **Push-option visibility:** `repo.private` / `repo.template` push
options were applied to any existing repo, letting an owner/admin
silently flip visibility bypassing audit, webhooks, and notifications.
They are now honored only on push-to-create.

Co-authored-by: bircni <bircni@icloud.com>
zjjhot added a commit to zjjhot/gitea that referenced this pull request Jul 13, 2026
* 'main' of https://github.com/go-gitea/gitea:
  fix: various security fixes (go-gitea#38406)
  fix(util): reject invalid characters between time-estimate units (go-gitea#38416)
  feat(actions): implement adaptive auto-refresh for workflow runs list (go-gitea#38329)
  fix(turnstile): route CAPTCHA verification through the configured proxy (go-gitea#38412)
  fix: represent a deleted assignee team as a Ghost team (go-gitea#38413)
  [skip ci] Updated translations via Crowdin
  fix: refresh pull request merge box when the commit status is pending (go-gitea#38410)
  chore: remove Yarden Shoham from maintainers (go-gitea#38407)
  fix: actions task state concurrent update (go-gitea#38405)
  fix(actions): keep workflow run trailing on one row with long branch names (go-gitea#38382)
  fix(pull): re-evaluate review official flag on target branch change (go-gitea#38319)
  fix(web): use locale-aware date formatting for contribution calendar tooltips (go-gitea#38398)
  fix(security): harden access checks and migration validation (go-gitea#38324)
  fix: enforce public-only token scope and harden push options / locale parsing (go-gitea#38323)
  fix: co-author detection (go-gitea#38392)
  fix(api): stop leaking private repo metadata after access revocation (go-gitea#38321)
  fix(lfs): require proof of possession for cross-repo objects (go-gitea#38322)
  fix: incorrect co-author detection on commit page (go-gitea#38386)
zjjhot added a commit to zjjhot/gitea that referenced this pull request Jul 13, 2026
* main:
  fix: various security fixes (go-gitea#38406)
  fix(util): reject invalid characters between time-estimate units (go-gitea#38416)
  feat(actions): implement adaptive auto-refresh for workflow runs list (go-gitea#38329)
  fix(turnstile): route CAPTCHA verification through the configured proxy (go-gitea#38412)
  fix: represent a deleted assignee team as a Ghost team (go-gitea#38413)
  [skip ci] Updated translations via Crowdin
  fix: refresh pull request merge box when the commit status is pending (go-gitea#38410)
  chore: remove Yarden Shoham from maintainers (go-gitea#38407)
  fix: actions task state concurrent update (go-gitea#38405)
  fix(actions): keep workflow run trailing on one row with long branch names (go-gitea#38382)
  fix(pull): re-evaluate review official flag on target branch change (go-gitea#38319)
  fix(web): use locale-aware date formatting for contribution calendar tooltips (go-gitea#38398)
  fix(security): harden access checks and migration validation (go-gitea#38324)
  fix: enforce public-only token scope and harden push options / locale parsing (go-gitea#38323)
  fix: co-author detection (go-gitea#38392)
  fix(api): stop leaking private repo metadata after access revocation (go-gitea#38321)
  fix(lfs): require proof of possession for cross-repo objects (go-gitea#38322)
  fix: incorrect co-author detection on commit page (go-gitea#38386)
zeekay pushed a commit to hanzoai/git that referenced this pull request Jul 26, 2026
… parsing (go-gitea#38323)

- **Locale DoS:** the `Locale` middleware passed the raw
`Accept-Language` header to `ParseAcceptLanguage`, whose guard only
counts `-` while the scanner aliases `_` to `-` — a large `_`-separated
header on an unauthenticated request burned CPU. The header is now
length-bounded before parsing.
- **Public-only token scope:** `GET /teams/{id}/repos`,
`.../repos/{org}/{repo}`, `/teams/{id}/activities/feeds`, and
`/users/{username}/orgs/{org}/permissions` still returned private
repo/activity/permission data to a public-only token. They now filter
via `TokenCanAccessRepo` / `ApplyPublicOnly` and reject non-public org
permissions.
- **Push-option visibility:** `repo.private` / `repo.template` push
options were applied to any existing repo, letting an owner/admin
silently flip visibility bypassing audit, webhooks, and notifications.
They are now honored only on push-to-create.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport/done All backports for this PR have been created backport/v1.27 This PR should be backported to Gitea 1.27 lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. type/bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants