CVE Patch Review

CVE-2026-27771: Root-Cause Patch Review for Gitea Registry Authorization Gaps

CVE-2026-27771 · Updated 2026-07-18 Root-cause

Summary

Gitea v1.26.2 addresses two related authorization flaws behind CVE-2026-27771: unauthenticated access patterns in the container registry token flow and Composer metadata disclosure of private repository source URLs. The container patch conditions the Basic authentication challenge on owner visibility and strict sign-in policy, while the Composer patch moves source URL emission behind an explicit repository permission check. The Composer changes are a strong root-cause fix for the disclosure path; the container changes appear to harden the auth negotiation path and align behavior with visibility policy, but the provided diff is narrower and should be validated against all token issuance paths.

Analysis

Vulnerability

CVE-2026-27771 describes missing authorization checks in Gitea package registries that allowed unauthenticated or under-authorized access to sensitive package resources. Per the advisory context, affected versions up to v1.26.1 exposed two distinct failure modes: private container image pull behavior in the container registry flow, and disclosure of internal/private Composer repository source URLs in package metadata. The upstream references are the container fix in PR #37290 and the Composer fix in PR #37610, with public tracking in CVE.org and NVD.

The root issue is inconsistent enforcement of visibility and repository authorization at registry boundaries. In the Composer path, package metadata generation included repository source information without first proving the caller could access the linked repository. In the container path, the auth challenge behavior did not properly reflect whether sign-in was required for the owner namespace, creating a path to unauthorized registry interactions for non-public owners.

ownerName := ctx.PathParam("username")
owner, _ := user_model.GetUserByName(ctx, ownerName)
requireSignIn := owner != nil && owner.Visibility != structs.VisibleTypePublic
requireSignIn = requireSignIn || setting.Service.RequireSignInViewStrict
if requireSignIn {
	ctx.Resp.Header().Add("WWW-Authenticate", `Basic realm="Gitea Container Registry"`)
}

The Composer disclosure is more explicit in the patch: source URLs are now emitted only when the caller has repository access, rather than being attached unconditionally to package metadata.

permission, err := access_model.GetDoerRepoPermission(ctx, pd.Repository, ctx.Doer)
if err != nil {
	return nil, err
}

if permission.HasAnyUnitAccess() {
	pkg.Source = Source{
		URL:       pd.Repository.HTMLURL(),
		Type:      "git",
		Reference: pd.Version.Version,
	}
}

Patch

The patch set is split across two pull requests.

Container registry: PR #37290 changes routers/api/packages/container/container.go so the Basic WWW-Authenticate challenge is only added when the owner namespace is non-public or when RequireSignInViewStrict is enabled. The associated integration tests now distinguish between public and sign-in-required challenge headers, indicating the auth negotiation behavior is intentionally visibility-aware.

Composer registry: PR #37610 changes createPackageMetadataResponse to accept request context and perform GetDoerRepoPermission before populating pkg.Source. The caller in routers/api/packages/composer/composer.go is updated to handle the new error return. Integration tests verify that users with repository access still receive source metadata, while users without repository access receive package metadata with empty source fields.

The same PR also adds UI visibility badges and explanatory settings text. Those template and locale changes do not enforce security directly, but they reduce operator confusion by making package visibility inheritance explicit and by warning that linking a package to a repository does not change package visibility.

Review

Pros

  • The Composer fix addresses the authorization decision at the data construction point, which is the correct layer for preventing metadata leakage. Instead of trying to sanitize output later, it gates pkg.Source on an explicit permission query.
  • The new Composer tests are strong and behavior-oriented: they verify both the allowed case and the denied-but-still-readable metadata case, which matches the intended product semantics.
  • The container patch aligns auth challenge behavior with owner visibility and strict sign-in policy, reducing the chance that private namespaces are treated like public ones during registry negotiation.
  • The UI and locale changes improve security clarity. Visibility inheritance and repository-link semantics are now surfaced to administrators and package owners, which helps prevent mistaken assumptions about access control.
  • Error handling in the Composer path is improved by propagating permission lookup failures instead of silently continuing.

Cons

  • The container diff shown is narrow: it changes when a Basic challenge header is emitted, but the provided snippet does not itself prove that token issuance or manifest/blob access paths now enforce authorization comprehensively. Engineers should verify the full request flow in PR #37290 and regression-test anonymous token acquisition for private owners.
  • The container code ignores the error from GetUserByName via owner, _ := .... That may be acceptable for challenge shaping, but it is not ideal for security-sensitive branching because lookup failures collapse into a nil-owner path.
  • The Composer permission check uses HasAnyUnitAccess(), which is broader than a repository-content-specific capability. That is likely sufficient for suppressing private source URLs from unauthorized users, but it is a coarse authorization predicate and should be reviewed against the intended repository visibility model.
  • The UI changes are informative rather than preventive. They help operators understand visibility, but they do not substitute for server-side authorization controls.

Verdict

Root-cause.

For the Composer information disclosure, the patch is a root-cause fix because it introduces an explicit authorization check exactly where sensitive repository source metadata is attached to the response. That closes the direct leak path and is backed by integration tests covering both authorized and unauthorized callers.

For the container registry issue, the available diff indicates a policy-correcting fix in the authentication negotiation layer, but the evidence in the provided snippet is less complete than for Composer. Still, taken together with the updated tests and the advisory guidance to upgrade to v1.26.2 or require sign-in, the patch set appears to remediate the underlying visibility/authorization mismatch rather than merely hiding symptoms. Teams should deploy the upgrade and additionally validate anonymous pull, token, manifest, and blob flows for private and internal owners under both default and REQUIRE_SIGNIN_VIEW=true configurations.

References: official container patch, Composer patch, NVD record, CVE record, advisory summary.

Sources