From ceacaea34e94ba9f16b984df3f406e657965a984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Sibiril?= <81782+aureliensibiril@users.noreply.github.com> Date: Sun, 17 May 2026 17:22:50 +0200 Subject: [PATCH] =?UTF-8?q?Inline=20Vercel=20driver=20URL=20with=20url.URL?= =?UTF-8?q?=20=E2=86=92=20Drop=20allowedHosts:true=20from=20vite=20dev=20s?= =?UTF-8?q?erver?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Inline Vercel driver URL with url.URL - Escape URL path segments in connector name resolvers - Dedup Vercel /v2/user fallback in name resolver - Drop allowedHosts:true from vite dev server Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com> --- apps/console/vite.config.ts | 1 - pkg/accessreview/drivers/name_resolver.go | 64 ++++++++--------------- pkg/accessreview/drivers/vercel.go | 14 +++-- 3 files changed, 31 insertions(+), 48 deletions(-) diff --git a/apps/console/vite.config.ts b/apps/console/vite.config.ts index db673869c..9136e0114 100644 --- a/apps/console/vite.config.ts +++ b/apps/console/vite.config.ts @@ -55,7 +55,6 @@ export default defineConfig({ tailwindcss(), ], server: { - allowedHosts: true, proxy: { "/api": { target: "http://localhost:8080", diff --git a/pkg/accessreview/drivers/name_resolver.go b/pkg/accessreview/drivers/name_resolver.go index 746d9433e..9d7577003 100644 --- a/pkg/accessreview/drivers/name_resolver.go +++ b/pkg/accessreview/drivers/name_resolver.go @@ -20,10 +20,12 @@ import ( "encoding/json" "fmt" "net/http" + "net/url" admin "google.golang.org/api/admin/directory/v1" "google.golang.org/api/option" + "go.probo.inc/probo/pkg/connector" "go.probo.inc/probo/pkg/coredata" ) @@ -618,9 +620,9 @@ func (r *gitlabNameResolver) ResolveInstanceName(ctx context.Context) (string, e return "", nil } - url := fmt.Sprintf("https://gitlab.com/api/v4/groups/%s", r.groupID) + endpoint := fmt.Sprintf("https://gitlab.com/api/v4/groups/%s", url.PathEscape(r.groupID)) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) if err != nil { return "", fmt.Errorf("cannot create gitlab group request: %w", err) } @@ -665,9 +667,9 @@ func (r *bitbucketNameResolver) ResolveInstanceName(ctx context.Context) (string return "", nil } - url := fmt.Sprintf("https://api.bitbucket.org/2.0/workspaces/%s", r.workspace) + endpoint := fmt.Sprintf("https://api.bitbucket.org/2.0/workspaces/%s", url.PathEscape(r.workspace)) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) if err != nil { return "", fmt.Errorf("cannot create bitbucket workspace request: %w", err) } @@ -712,9 +714,9 @@ func (r *herokuNameResolver) ResolveInstanceName(ctx context.Context) (string, e return "", nil } - url := fmt.Sprintf("https://api.heroku.com/teams/%s", r.teamID) + endpoint := fmt.Sprintf("https://api.heroku.com/teams/%s", url.PathEscape(r.teamID)) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) if err != nil { return "", fmt.Errorf("cannot create heroku team request: %w", err) } @@ -770,9 +772,9 @@ func (r *asanaNameResolver) ResolveInstanceName(ctx context.Context) (string, er return "", nil } - url := fmt.Sprintf("https://app.asana.com/api/1.0/workspaces/%s", r.workspaceGID) + endpoint := fmt.Sprintf("https://app.asana.com/api/1.0/workspaces/%s", url.PathEscape(r.workspaceGID)) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) if err != nil { return "", fmt.Errorf("cannot create asana workspace request: %w", err) } @@ -815,9 +817,9 @@ func (r *snykNameResolver) ResolveInstanceName(ctx context.Context) (string, err return "", nil } - url := fmt.Sprintf("https://api.snyk.io/rest/orgs/%s?version=2024-10-15", r.orgID) + endpoint := fmt.Sprintf("https://api.snyk.io/rest/orgs/%s?version=2024-10-15", url.PathEscape(r.orgID)) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) if err != nil { return "", fmt.Errorf("cannot create snyk org request: %w", err) } @@ -862,9 +864,9 @@ func (r *netlifyNameResolver) ResolveInstanceName(ctx context.Context) (string, return "", nil } - url := fmt.Sprintf("https://api.netlify.com/api/v1/accounts/%s", r.accountSlug) + endpoint := fmt.Sprintf("https://api.netlify.com/api/v1/accounts/%s", url.PathEscape(r.accountSlug)) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) if err != nil { return "", fmt.Errorf("cannot create netlify account request: %w", err) } @@ -950,9 +952,9 @@ func (r *clickupNameResolver) ResolveInstanceName(ctx context.Context) (string, return "", nil } - url := fmt.Sprintf("https://api.clickup.com/api/v2/team/%s", r.teamID) + endpoint := fmt.Sprintf("https://api.clickup.com/api/v2/team/%s", url.PathEscape(r.teamID)) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) if err != nil { return "", fmt.Errorf("cannot create clickup team request: %w", err) } @@ -998,7 +1000,7 @@ func (r *vercelNameResolver) ResolveInstanceName(ctx context.Context) (string, e return "", nil } - teamURL := fmt.Sprintf("https://api.vercel.com/v2/teams/%s", r.teamID) + teamURL := fmt.Sprintf("https://api.vercel.com/v2/teams/%s", url.PathEscape(r.teamID)) teamReq, err := http.NewRequestWithContext(ctx, http.MethodGet, teamURL, nil) if err != nil { return "", fmt.Errorf("cannot create vercel team request: %w", err) @@ -1031,36 +1033,14 @@ func (r *vercelNameResolver) ResolveInstanceName(ctx context.Context) (string, e // Personal-account fallback: /v2/teams/ returns 404, but // /v2/user works with the same Bearer token. - userReq, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.vercel.com/v2/user", nil) + user, err := connector.FetchVercelUser(ctx, r.httpClient) if err != nil { - return "", fmt.Errorf("cannot create vercel user request: %w", err) + return "", err } - userReq.Header.Set("Accept", "application/json") - - userResp, err := r.httpClient.Do(userReq) - if err != nil { - return "", fmt.Errorf("cannot execute vercel user request: %w", err) + if user.Username != "" { + return user.Username, nil } - defer func() { _ = userResp.Body.Close() }() - - if userResp.StatusCode < 200 || userResp.StatusCode >= 300 { - return "", fmt.Errorf("cannot fetch vercel user: unexpected status %d", userResp.StatusCode) - } - - var userBody struct { - User struct { - Username string `json:"username"` - Name string `json:"name"` - } `json:"user"` - } - if err := json.NewDecoder(userResp.Body).Decode(&userBody); err != nil { - return "", fmt.Errorf("cannot decode vercel user response: %w", err) - } - - if userBody.User.Username != "" { - return userBody.User.Username, nil - } - return userBody.User.Name, nil + return user.Name, nil } // mondayNameResolver resolves the Monday.com account name via GraphQL. diff --git a/pkg/accessreview/drivers/vercel.go b/pkg/accessreview/drivers/vercel.go index de84d8942..0b361a1e2 100644 --- a/pkg/accessreview/drivers/vercel.go +++ b/pkg/accessreview/drivers/vercel.go @@ -74,11 +74,10 @@ type vercelMembersPage struct { func (d *VercelDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error) { var records []AccountRecord - base := fmt.Sprintf("https://api.vercel.com/v3/teams/%s/members", url.PathEscape(d.teamID)) cursor := "" for range maxPaginationPages { - page, err := d.queryMembers(ctx, base, cursor) + page, err := d.queryMembers(ctx, cursor) if err != nil { return nil, err } @@ -114,15 +113,20 @@ func (d *VercelDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error return nil, fmt.Errorf("cannot list all vercel accounts: %w", ErrPaginationLimitReached) } -func (d *VercelDriver) queryMembers(ctx context.Context, base, cursor string) (*vercelMembersPage, error) { +func (d *VercelDriver) queryMembers(ctx context.Context, cursor string) (*vercelMembersPage, error) { q := url.Values{} q.Set("limit", "100") if cursor != "" { q.Set("until", cursor) } - endpoint := base + "?" + q.Encode() + u := url.URL{ + Scheme: "https", + Host: "api.vercel.com", + Path: "/v3/teams/" + d.teamID + "/members", + RawQuery: q.Encode(), + } - req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) if err != nil { return nil, fmt.Errorf("cannot create vercel members request: %w", err) }