mirror of
https://github.com/mudler/LocalAI.git
synced 2026-11-07 04:53:24 -05:00
The GitHub add-on refused classic tokens in the page only, and its repository list took exact owner/name entries. A classic token (ghp_) is now an explicit choice of the connection. The kind declares a token guard: the host refuses a token with that prefix unless the connection sets the option classicToken to allow, and the request that turns it on must carry confirm_wider_token. It is checked when the token is saved and again each time it is used, so switching the option off stops a stored token. The list and the write mode still apply. The page shows a warning and two ticks. The list takes owner/* and *. The host matches them before the add-on runs, without regard to case and on whole names, so other-owner/x and owner-evil/x are not covered by acme/*, and a key that holds a star is never a name. A kind opts in with wildcardResources; only GitHub does, so the other add-ons keep exact matching. Saving * needs confirm_any_resource, and the write mode stays separate. A read tool that declares enumerates (the new list-repos) runs only for a list of * and asks GitHub which repositories the token reaches. Assisted-by: Claude:claude-sonnet-5-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
67 lines
2.1 KiB
Go
67 lines
2.1 KiB
Go
// SPDX-License-Identifier: MIT
|
|
package tools
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type listReposArgs struct {
|
|
Page int `json:"page"`
|
|
PerPage int `json:"perPage"`
|
|
}
|
|
|
|
// listRepos lists the repositories the token can reach, as GitHub says. The
|
|
// host runs it only for a connection that lists * (every repository the token
|
|
// reaches): that list is the permission, so the add-on asks GitHub and does
|
|
// not narrow the answer. The key it gets is *; any other key is a bug of the
|
|
// caller and is refused.
|
|
func (c call) listRepos(raw map[string]json.RawMessage) (any, error) {
|
|
var a listReposArgs
|
|
if err := decodeArgs(raw, &a); err != nil {
|
|
return nil, err
|
|
}
|
|
if c.key != "*" {
|
|
return nil, bad("the host did not allow every repository for this call")
|
|
}
|
|
page, per := pageOr(a.Page, 1), perPageOr(a.PerPage, 30)
|
|
q := url.Values{"sort": {"full_name"}, "page": {strconv.Itoa(page)}, "per_page": {strconv.Itoa(per)}}
|
|
var list []struct {
|
|
FullName string `json:"full_name"`
|
|
DefaultBranch string `json:"default_branch"`
|
|
Private bool `json:"private"`
|
|
Archived bool `json:"archived"`
|
|
HTMLURL string `json:"html_url"`
|
|
Permissions struct {
|
|
Push bool `json:"push"`
|
|
} `json:"permissions"`
|
|
}
|
|
resp, err := c.r.API.GetJSON(c.ctx, c.token, "/user/repos", q, &list)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
type item struct {
|
|
FullName string `json:"fullName"`
|
|
DefaultBranch string `json:"defaultBranch"`
|
|
Private bool `json:"private"`
|
|
Archived bool `json:"archived"`
|
|
CanWrite bool `json:"canWrite"`
|
|
URL string `json:"url"`
|
|
}
|
|
items := make([]item, 0, len(list))
|
|
for _, r := range list {
|
|
// A name that is not owner/name is dropped; it could not be used anyway.
|
|
if checkRepo(r.FullName) != nil || strings.Contains(r.FullName, "*") {
|
|
continue
|
|
}
|
|
items = append(items, item{FullName: r.FullName, DefaultBranch: r.DefaultBranch, Private: r.Private, Archived: r.Archived, CanWrite: r.Permissions.Push, URL: r.HTMLURL})
|
|
}
|
|
res := map[string]any{"page": page, "items": items}
|
|
if resp.NextPage() {
|
|
res["next"] = page + 1
|
|
}
|
|
return res, nil
|
|
}
|