Part 2 of 3 — splits PR #451. Depends on #468 (uses the schemas added there). Part 3 (branded types) stacks on top of this.
Goal
Extend zod validation to the second boundary: the avatar/image REST route GET /:type/:id (and /clear/:type/:id), which currently re-enters the resolvers via parseQuery with no validation.
Scope
src/utils.ts: validate the stripped, lowercased id in parseQuery against an avatarIdSchema (valid address OR valid handle/name). Invalid → throw InvalidQueryError, mapped to HTTP 400 in the route handler. Keep the lenient clamping of s/w/h size params exactly as-is.
- Apply the same 400 path to
/clear/:type/:id.
resolvers/basename.ts, addressResolvers/basename.ts getAvatar: accept the validated id.
- Tests: bad avatar id (e.g.
GET /avatar/not-an-address) → 400; valid address/handle ids do not 400.
Critical — regression to fix (caught reviewing PR #451)
parseQuery is the shared chokepoint for all image types: avatar, token, space, space-cover, space-logo, space-sx, space-cover-sx, user-cover. PR #451's avatarIdSchema only accepted an address or a dotted handle, so a plain offchain Snapshot space/token slug (no dot, no 0x) would 400 instead of resolving — a production regression masked by tests that only used .eth handles and 64-hex Starknet ids.
Required: avatarIdSchema (or per-type schema selection) must allow the id shapes each type actually uses — including plain non-dotted slugs for space/token/space-*. Add e2e cases for a plain space slug and a plain token id returning 200 (resolved or fallback), not 400.
Acceptance criteria
- All existing image-route id shapes that worked before still return 200.
- Genuinely malformed ids → 400.
- Handle validation rejects degenerate handles (
., a., .b).
yarn lint, yarn typecheck, yarn build pass; new + existing e2e pass.
Out of scope
Part 2 of 3 — splits PR #451. Depends on #468 (uses the schemas added there). Part 3 (branded types) stacks on top of this.
Goal
Extend zod validation to the second boundary: the avatar/image REST route
GET /:type/:id(and/clear/:type/:id), which currently re-enters the resolvers viaparseQuerywith no validation.Scope
src/utils.ts: validate the stripped, lowercasedidinparseQueryagainst anavatarIdSchema(valid address OR valid handle/name). Invalid → throwInvalidQueryError, mapped to HTTP 400 in the route handler. Keep the lenient clamping ofs/w/hsize params exactly as-is./clear/:type/:id.resolvers/basename.ts,addressResolvers/basename.tsgetAvatar: accept the validated id.GET /avatar/not-an-address) → 400; valid address/handle ids do not 400.Critical — regression to fix (caught reviewing PR #451)
parseQueryis the shared chokepoint for all image types:avatar,token,space,space-cover,space-logo,space-sx,space-cover-sx,user-cover. PR #451'savatarIdSchemaonly accepted an address or a dotted handle, so a plain offchain Snapshotspace/tokenslug (no dot, no0x) would 400 instead of resolving — a production regression masked by tests that only used.ethhandles and 64-hex Starknet ids.Required:
avatarIdSchema(or per-type schema selection) must allow the id shapes each type actually uses — including plain non-dotted slugs forspace/token/space-*. Add e2e cases for a plain space slug and a plain token id returning 200 (resolved or fallback), not 400.Acceptance criteria
.,a.,.b).yarn lint,yarn typecheck,yarn buildpass; new + existing e2e pass.Out of scope