Repository: gogs/gogs
File: internal/route/api/v1/repo/repo.go (Go)

Code before:
		c.Error(err, "load attributes")
		return
	}

	// Early return for querying other user's repositories
	if c.User.ID != user.ID {
		repos := make([]*api.Repository, len(ownRepos))
		for i := range ownRepos {
			repos[i] = ownRepos[i].APIFormatLegacy(&api.Permission{Admin: true, Push: true, Pull: true})
		}
		c.JSONSuccess(&repos)
		return
	}

	accessibleRepos, err := database.Handle.Repositories().GetByCollaboratorIDWithAccessMode(c.Req.Context(), user.ID)
	if err != nil {
		c.Error(err, "get repositories accesses by collaborator")
		return
	}

	// Load attributes for accessible repositories
	accessibleRepoSlice := make([]*database.Repository, 0, len(accessibleRepos))
	for repo := range accessibleRepos {
		accessibleRepoSlice = append(accessibleRepoSlice, repo)
	}
	if err = database.RepositoryList(accessibleRepoSlice).LoadAttributes(); err != nil {
		c.Error(err, "load attributes for accessible repositories")
		return
	}

	numOwnRepos := len(ownRepos)
	repos := make([]*api.Repository, 0, numOwnRepos+len(accessibleRepos))
	for _, r := range ownRepos {
		repos = append(repos, r.APIFormatLegacy(&api.Permission{Admin: true, Push: true, Pull: true}))
	}

	for repo, access := range accessibleRepos {
		repos = append(repos,
			repo.APIFormatLegacy(&api.Permission{
				Admin: access >= database.AccessModeAdmin,
				Push:  access >= database.AccessModeWrite,
				Pull:  true,
			}),
		)
	}

	c.JSONSuccess(&repos)
}

func ListMyRepos(c *context.APIContext) {
	listUserRepositories(c, c.User.Name)

Review comment (bug):
While this fix loads the BaseRepo for forked repositories, the current implementation of RepositoryList.LoadAttributes does not recursively load the Owner of BaseRepo. This means that when APIFormatLegacy is called on a forked repository at line 143, it will call BaseRepo.APIFormatLegacy at repo.go:407, which will then try to access BaseRepo.Owner.APIFormat() at repo.go:368, potentially causing another nil pointer dereference if the BaseRepo's Owner is not loaded.

The same issue exists for ownRepos (line 104) and is a pre-existing bug in the codebase. Consider enhancing the LoadAttributes implementation in internal/database/repo.go to also load the owners of base repositories, or add a recursive LoadAttributes call for base repositories after line 130.

Code after:
		c.Error(err, "load attributes")
		return
	}

	// Early return for querying other user's repositories
	if c.User.ID != user.ID {
		repos := make([]*api.Repository, len(ownRepos))
		for i := range ownRepos {
			repos[i] = ownRepos[i].APIFormatLegacy(&api.Permission{Admin: true, Push: true, Pull: true})
		}
		c.JSONSuccess(&repos)
		return
	}

	accessibleReposWithAccessMode, err := database.Handle.Repositories().GetByCollaboratorIDWithAccessMode(c.Req.Context(), user.ID)
	if err != nil {
		c.Error(err, "get repositories accesses by collaborator")
		return
	}

	accessibleRepos := make([]*database.Repository, 0, len(accessibleReposWithAccessMode))
	for repo := range accessibleReposWithAccessMode {
		accessibleRepos = append(accessibleRepos, repo)
	}
	if err = database.RepositoryList(accessibleRepos).LoadAttributes(); err != nil {
		c.Error(err, "load attributes for accessible repositories")
		return
	}

	numOwnRepos := len(ownRepos)
	repos := make([]*api.Repository, 0, numOwnRepos+len(accessibleReposWithAccessMode))
	for _, r := range ownRepos {
		repos = append(repos, r.APIFormatLegacy(&api.Permission{Admin: true, Push: true, Pull: true}))
	}

	for repo, access := range accessibleReposWithAccessMode {
		repos = append(repos,
			repo.APIFormatLegacy(&api.Permission{
				Admin: access >= database.AccessModeAdmin,
				Push:  access >= database.AccessModeWrite,
				Pull:  true,
			}),
		)
	}

	c.JSONSuccess(&repos)
}

func ListMyRepos(c *context.APIContext) {
	listUserRepositories(c, c.User.Name)
}