A # written into the source path or query gives another unsigned spelling of an allowlisted image #286

Closed
opened 2026-10-16 00:47:30 +02:00 by clawbot · 2 comments
Collaborator

Where: internal/imgcache/urlparser.go, parseImageComponents, on next at 22bfc43. Found by the review of #285.

What is wrong: a # written as %23 in the source path or query of an image URL reaches the parser decoded, and is kept in the source path or query. The source URL built from it holds a #, which Go reads as the start of a fragment and drops before fetching. So /v1/image/allowed.example.com/images/photo.jpg%23x/50x50.jpeg fetches the same upstream image as /v1/image/allowed.example.com/images/photo.jpg/50x50.jpeg, without a signature, but is cached under its own keys. Each new text after the # is another spelling of one upstream request, the problem #275 closes for the host segment.

How it fails: with a host allowlisted, an outsider varies the text after %23 to make pixa fetch and store the same upstream response under as many cache keys as it likes.

Severity: low

Definition of done:

  1. An image URL whose source path or query holds a # (written as %23) is refused with 400; README.md's rule for writing a source path and query says so.
  2. Test first, through the router, for %23 in the path and in the query; make check green.

Package: internal/imgcache. Starts after #285 lands, which changes the same parser.

Model: opus-5-5

Where: `internal/imgcache/urlparser.go`, `parseImageComponents`, on `next` at `22bfc43`. Found by the review of https://git.eeqj.de/sneak/pixa/pulls/285. What is wrong: a `#` written as `%23` in the source path or query of an image URL reaches the parser decoded, and is kept in the source path or query. The source URL built from it holds a `#`, which Go reads as the start of a fragment and drops before fetching. So `/v1/image/allowed.example.com/images/photo.jpg%23x/50x50.jpeg` fetches the same upstream image as `/v1/image/allowed.example.com/images/photo.jpg/50x50.jpeg`, without a signature, but is cached under its own keys. Each new text after the `#` is another spelling of one upstream request, the problem https://git.eeqj.de/sneak/pixa/issues/275 closes for the host segment. How it fails: with a host allowlisted, an outsider varies the text after `%23` to make pixa fetch and store the same upstream response under as many cache keys as it likes. Severity: low Definition of done: 1. An image URL whose source path or query holds a `#` (written as `%23`) is refused with 400; `README.md`'s rule for writing a source path and query says so. 2. Test first, through the router, for `%23` in the path and in the query; `make check` green. Package: `internal/imgcache`. Starts after https://git.eeqj.de/sneak/pixa/pulls/285 lands, which changes the same parser. Model: opus-5-5
clawbot self-assigned this 2026-10-16 00:47:44 +02:00
Author
Collaborator

Plan: parseImageComponents in internal/imgcache/urlparser.go refuses an image URL with 400 when its source path or query, as decoded, holds a #. The check sits after the host check, so a %23 in the host is still refused as an invalid source host, as #275 made it. The rule in README.md for writing a source path and query (added for #255) gains one sentence: neither may hold a #, and a URL that writes one as %23 is refused with 400. %2523 still passes, as a %23 the source URL keeps.

The tests come first, through the image route: %23 in the source path, %23 in the source query, and %23 in the source path of a URL signed for that exact path, each refused with 400. No existing test changes.

Model: opus-5-5

Plan: `parseImageComponents` in `internal/imgcache/urlparser.go` refuses an image URL with 400 when its source path or query, as decoded, holds a `#`. The check sits after the host check, so a `%23` in the host is still refused as an invalid source host, as https://git.eeqj.de/sneak/pixa/issues/275 made it. The rule in `README.md` for writing a source path and query (added for https://git.eeqj.de/sneak/pixa/issues/255) gains one sentence: neither may hold a `#`, and a URL that writes one as `%23` is refused with 400. `%2523` still passes, as a `%23` the source URL keeps. The tests come first, through the image route: `%23` in the source path, `%23` in the source query, and `%23` in the source path of a URL signed for that exact path, each refused with 400. No existing test changes. Model: opus-5-5
Author
Collaborator

Built as planned in #287. The 400 says invalid image URL: invalid image path: the source path or query holds a "#".

Model: opus-5-5

Built as planned in https://git.eeqj.de/sneak/pixa/pulls/287. The 400 says `invalid image URL: invalid image path: the source path or query holds a "#"`. Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/pixa#286