From 0b81393bdfe428be7a09be91753302760234b6ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Sibiril?= <81782+aureliensibiril@users.noreply.github.com> Date: Sun, 12 Jul 2026 11:24:01 +0200 Subject: [PATCH] Escape dynamic path segments in the Google Analytics and Segment drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GA4 driver passed the account and property IDs to url.JoinPath as raw segments, and the Segment driver built its per-user endpoint by concatenating the user ID into url.URL.Path — both bypass the url.PathEscape rule that every sibling driver (and the matching name resolvers) already follow. The IDs are numeric today so there is no behaviour change, but this keeps the drivers consistent and safe if a provider ever returns a segment with a reserved character. Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com> --- pkg/accessreview/drivers/google_analytics.go | 4 ++-- pkg/accessreview/drivers/segment.go | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pkg/accessreview/drivers/google_analytics.go b/pkg/accessreview/drivers/google_analytics.go index e2ff59550..6443db12a 100644 --- a/pkg/accessreview/drivers/google_analytics.go +++ b/pkg/accessreview/drivers/google_analytics.go @@ -94,7 +94,7 @@ func (d *GoogleAnalyticsDriver) ListAccounts(ctx context.Context) ([]AccountReco members := make(map[string]*googleAnalyticsMember) // Account-level bindings. - if err := d.collectBindings(ctx, members, "v1alpha", "accounts", d.accountID, "accessBindings"); err != nil { + if err := d.collectBindings(ctx, members, "v1alpha", "accounts", url.PathEscape(d.accountID), "accessBindings"); err != nil { return nil, err } @@ -105,7 +105,7 @@ func (d *GoogleAnalyticsDriver) ListAccounts(ctx context.Context) ([]AccountReco } for _, propertyID := range propertyIDs { - if err := d.collectBindings(ctx, members, "v1alpha", "properties", propertyID, "accessBindings"); err != nil { + if err := d.collectBindings(ctx, members, "v1alpha", "properties", url.PathEscape(propertyID), "accessBindings"); err != nil { return nil, err } } diff --git a/pkg/accessreview/drivers/segment.go b/pkg/accessreview/drivers/segment.go index 39c1b7841..e6ab490fd 100644 --- a/pkg/accessreview/drivers/segment.go +++ b/pkg/accessreview/drivers/segment.go @@ -204,12 +204,13 @@ func (d *SegmentDriver) listUsers(ctx context.Context, base *url.URL) ([]segment } func (d *SegmentDriver) userPermissions(ctx context.Context, base *url.URL, userID string) ([]segmentPermission, error) { - endpoint := *base - // Path holds the decoded value; endpoint.String() escapes the id once. - endpoint.Path = "/users/" + userID + endpoint, err := url.JoinPath(base.String(), "users", url.PathEscape(userID)) + if err != nil { + return nil, fmt.Errorf("cannot build segment user URL: %w", err) + } var resp segmentUserResponse - if err := d.getJSON(ctx, endpoint.String(), &resp); err != nil { + if err := d.getJSON(ctx, endpoint, &resp); err != nil { return nil, err }