From c2595d0263412ec1f4d44dbb81fae7863231a7ba Mon Sep 17 00:00:00 2001 From: Rodin Date: Sat, 2 May 2026 02:41:51 -0700 Subject: [PATCH] fix: consistent url.PathEscape across all Gitea client endpoints Apply url.PathEscape to owner, repo, and sha path segments in all methods that were previously interpolating raw values. Methods already using PathEscape (ListReviews, DeleteReview, GetTimelineReviewCommentID, EditComment) are unchanged. This eliminates an inconsistency flagged in PRs #17, #20, and #22 and prevents potential path-injection bugs for names with special characters. Closes #24 --- gitea/client.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/gitea/client.go b/gitea/client.go index 9355e29..7fd22de 100644 --- a/gitea/client.go +++ b/gitea/client.go @@ -66,7 +66,7 @@ type ReviewComment struct { // GetPullRequest fetches PR metadata. func (c *Client) GetPullRequest(ctx context.Context, owner, repo string, number int) (*PullRequest, error) { - reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/pulls/%d", c.baseURL, owner, repo, number) + reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/pulls/%d", c.baseURL, url.PathEscape(owner), url.PathEscape(repo), number) body, err := c.doGet(ctx, reqURL) if err != nil { return nil, fmt.Errorf("fetch PR: %w", err) @@ -80,7 +80,7 @@ func (c *Client) GetPullRequest(ctx context.Context, owner, repo string, number // GetPullRequestDiff fetches the unified diff for a PR. func (c *Client) GetPullRequestDiff(ctx context.Context, owner, repo string, number int) (string, error) { - reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/pulls/%d.diff", c.baseURL, owner, repo, number) + reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/pulls/%d.diff", c.baseURL, url.PathEscape(owner), url.PathEscape(repo), number) body, err := c.doGet(ctx, reqURL) if err != nil { return "", fmt.Errorf("fetch diff: %w", err) @@ -90,7 +90,7 @@ func (c *Client) GetPullRequestDiff(ctx context.Context, owner, repo string, num // GetPullRequestFiles fetches the list of files changed in a PR. func (c *Client) GetPullRequestFiles(ctx context.Context, owner, repo string, number int) ([]ChangedFile, error) { - reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/pulls/%d/files", c.baseURL, owner, repo, number) + reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/pulls/%d/files", c.baseURL, url.PathEscape(owner), url.PathEscape(repo), number) body, err := c.doGet(ctx, reqURL) if err != nil { return nil, fmt.Errorf("fetch PR files: %w", err) @@ -104,7 +104,7 @@ func (c *Client) GetPullRequestFiles(ctx context.Context, owner, repo string, nu // GetCommitStatuses fetches CI statuses for a commit SHA. func (c *Client) GetCommitStatuses(ctx context.Context, owner, repo, sha string) ([]CommitStatus, error) { - reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/commits/%s/statuses", c.baseURL, owner, repo, sha) + reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/commits/%s/statuses", c.baseURL, url.PathEscape(owner), url.PathEscape(repo), url.PathEscape(sha)) body, err := c.doGet(ctx, reqURL) if err != nil { return nil, fmt.Errorf("fetch commit statuses: %w", err) @@ -118,7 +118,7 @@ func (c *Client) GetCommitStatuses(ctx context.Context, owner, repo, sha string) // GetFileContent fetches a file from the default branch of a repo. func (c *Client) GetFileContent(ctx context.Context, owner, repo, filepath string) (string, error) { - reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/raw/%s", c.baseURL, owner, repo, escapePath(filepath)) + reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/raw/%s", c.baseURL, url.PathEscape(owner), url.PathEscape(repo), escapePath(filepath)) body, err := c.doGet(ctx, reqURL) if err != nil { return "", fmt.Errorf("fetch file %s: %w", filepath, err) @@ -128,7 +128,7 @@ func (c *Client) GetFileContent(ctx context.Context, owner, repo, filepath strin // GetFileContentRef fetches a file from a specific ref (branch/tag/sha) in a repo. func (c *Client) GetFileContentRef(ctx context.Context, owner, repo, filepath, ref string) (string, error) { - reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/raw/%s?ref=%s", c.baseURL, owner, repo, escapePath(filepath), url.QueryEscape(ref)) + reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/raw/%s?ref=%s", c.baseURL, url.PathEscape(owner), url.PathEscape(repo), escapePath(filepath), url.QueryEscape(ref)) body, err := c.doGet(ctx, reqURL) if err != nil { return "", fmt.Errorf("fetch file %s@%s: %w", filepath, ref, err) @@ -140,7 +140,7 @@ func (c *Client) GetFileContentRef(ctx context.Context, owner, repo, filepath, r // event should be "APPROVED" or "REQUEST_CHANGES". // comments are optional inline comments attached to specific lines. func (c *Client) PostReview(ctx context.Context, owner, repo string, number int, event, body string, comments []ReviewComment) (*Review, error) { - reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/pulls/%d/reviews", c.baseURL, owner, repo, number) + reqURL := fmt.Sprintf("%s/api/v1/repos/%s/%s/pulls/%d/reviews", c.baseURL, url.PathEscape(owner), url.PathEscape(repo), number) payload := struct { Body string `json:"body"` @@ -230,9 +230,9 @@ type ContentEntry struct { func (c *Client) ListContents(ctx context.Context, owner, repo, path string) ([]ContentEntry, error) { var reqURL string if path == "" { - reqURL = fmt.Sprintf("%s/api/v1/repos/%s/%s/contents", c.baseURL, owner, repo) + reqURL = fmt.Sprintf("%s/api/v1/repos/%s/%s/contents", c.baseURL, url.PathEscape(owner), url.PathEscape(repo)) } else { - reqURL = fmt.Sprintf("%s/api/v1/repos/%s/%s/contents/%s", c.baseURL, owner, repo, escapePath(path)) + reqURL = fmt.Sprintf("%s/api/v1/repos/%s/%s/contents/%s", c.baseURL, url.PathEscape(owner), url.PathEscape(repo), escapePath(path)) } body, err := c.doGet(ctx, reqURL) if err != nil {