harden: escape every value that goes into an API path or query #609

Closed
opened 2026-09-23 15:43:59 +00:00 by hexajon · 0 comments
Owner

What

apiPath (internal/gitea/client_query.go:72) escapes each path segment with url.PathEscape. Most endpoint builders do not use it. They join strings instead: 173 call sites in 37 files in internal/gitea match c.repo+"..."+<variable>. Examples:

  • tags, branches, and collaborators by name (client_tags.go:10, client_branches.go:13, client_collaborators.go:18)
  • labels, milestones, and attachments by ID (client_label.go:11, client_comment_extra.go:63)

A value with a /, ?, #, or .. changes the path or adds a query. The request still goes to the same forge host, with the user's own token. It can hit a different endpoint than the caller meant. A .. segment cannot take a repo-scoped client out of its repo: pathUnderScope (internal/gitea/client_http.go:85) percent-decodes the path and resolves dot-segments before its prefix check. The fix must keep a / inside a value (a branch feat/x, a file path), so it escapes each /-separated part.

UploadReleaseAsset also puts the file name into the query string without escaping (client_release.go:57, ?name=+filename).

Fix

  • Build every path through apiPath, or escape each variable segment with url.PathEscape.
  • Build the upload query with url.Values.
  • Add a guard test that fails when a new builder joins a variable into a path without escaping it.

Acceptance

  • No endpoint builder in internal/gitea joins an unescaped variable into a path.
  • A release asset named a b&x=1.zip uploads with that exact name.
  • The guard test exists, and the gate is green.
## What `apiPath` (`internal/gitea/client_query.go:72`) escapes each path segment with `url.PathEscape`. Most endpoint builders do not use it. They join strings instead: 173 call sites in 37 files in `internal/gitea` match `c.repo+"..."+<variable>`. Examples: - tags, branches, and collaborators by name (`client_tags.go:10`, `client_branches.go:13`, `client_collaborators.go:18`) - labels, milestones, and attachments by ID (`client_label.go:11`, `client_comment_extra.go:63`) A value with a `/`, `?`, `#`, or `..` changes the path or adds a query. The request still goes to the same forge host, with the user's own token. It can hit a different endpoint than the caller meant. A `..` segment cannot take a repo-scoped client out of its repo: `pathUnderScope` (`internal/gitea/client_http.go:85`) percent-decodes the path and resolves dot-segments before its prefix check. The fix must keep a `/` inside a value (a branch `feat/x`, a file path), so it escapes each `/`-separated part. `UploadReleaseAsset` also puts the file name into the query string without escaping (`client_release.go:57`, `?name=`+filename). ## Fix - Build every path through `apiPath`, or escape each variable segment with `url.PathEscape`. - Build the upload query with `url.Values`. - Add a guard test that fails when a new builder joins a variable into a path without escaping it. ## Acceptance - No endpoint builder in `internal/gitea` joins an unescaped variable into a path. - A release asset named `a b&x=1.zip` uploads with that exact name. - The guard test exists, and the gate is green.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sixfold-space/madtea#609
No description provided.