Provinite / Provinite/chardb

Unbounded `limit` on media, comments and social list queries

Open
#294 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
0
Forks
0
Avg merge
2h 52m
Merged PRs (30d)
85

Description

Summary

Several list queries accept an unbounded limit. The DTOs validate @Min(1) but no maximum, and the services fall back to a default without clamping — so a caller can ask for any page size and the server will try to serve it.

Four of the affected media queries are @AllowUnauthenticated, so no account is needed.

Where

Unbounded

  • apps/backend/src/media/dto/media.dto.ts:83-87MediaFiltersInput.limit has @IsInt() @Min(1) and no @Max
  • apps/backend/src/media/media.service.ts:133const limit = filters?.limit || 20, used directly as take
  • apps/backend/src/social/social.service.ts:533 — same pattern
  • apps/backend/src/comments/comments.service.ts:252take: filters.limit, and the DTO (comment.dto.ts:75-77) has @Min(1) with no @Max

MediaFiltersInput reaches four unauthenticated queries: media, userMedia, characterMedia, galleryMedia.

Already correct, and worth copying

  • apps/backend/src/social/dto/social-query.dto.ts:56ActivityFeedInput.limit has @Max(100)
  • apps/backend/src/communities/communities.service.ts:224take: Math.min(filters.limit || 10, 20)

So the codebase already does this both ways; the gap is that it was never applied consistently.

Why it matters

An anonymous request like

query { characterMedia(characterId: "…", filters: { limit: 1000000 }) { media { id } } }

asks Postgres for a million rows and hydrates them with character, species and owner joined, then serialises the lot. A handful of concurrent requests is enough to be a problem, and none of them need to log in.

This is availability rather than disclosure — the visibility filters still apply, so nothing is exposed that the caller could not already page through.

Not urgent, but newly load-bearing

Found during the security review of #292, which adds pagination to the character media gallery. That change only ever asks in steps of 8 or 24, so it does not make this any more reachable — but it does mean the UI now depends on the limit being honoured, which is worth knowing before someone adds a cap.

Suggested fix

A @Max on each input, plus a service-side clamp as the backstop.

One trap worth avoiding. A @Max alone turns an over-large request into a validation error, and this codebase has already been bitten by that: the shop admin page asked for limit: 200 against a @Max(100), the query failed, and the page rendered with its "New listing" button silently missing and nothing to explain why. Either pick a maximum comfortably above any real caller, or clamp server-side and return what fits rather than erroring — but do not add a cap without checking what already asks for more than it.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the DTO and service locations listed in apps/backend/src/media, apps/backend/src/social, and apps/backend/src/comments, then inspect existing callers for limits above the proposed maximum. Compare the ActivityFeedInput and communities service patterns. Done means the affected inputs and service queries consistently bound page sizes without breaking real callers.

Written by the indexing model from the issue text.

Assessment

Tech stack
postgresql, typescript
Domain
api, backend, databases, security
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.