Skip to content

fix: quota bypass when editing a pending request's seasons#3265

Open
kushiemoon-dev wants to merge 3 commits into
seerr-team:developfrom
kushiemoon-dev:fix/633-quota-bypass-on-edit
Open

fix: quota bypass when editing a pending request's seasons#3265
kushiemoon-dev wants to merge 3 commits into
seerr-team:developfrom
kushiemoon-dev:fix/633-quota-bypass-on-edit

Conversation

@kushiemoon-dev

@kushiemoon-dev kushiemoon-dev commented Jul 19, 2026

Copy link
Copy Markdown

Description

Editing a pending TV request's seasons didn't re-check the requester's quota against the new season list. quota.tv.remaining is computed from the currently persisted seasons, so any seasons being removed in the same edit were still counted as "used" — a user (or an admin editing on a user's behalf) could add seasons beyond the quota limit as long as the request already existed, bypassing the quota enforcement that applies to new requests.

This adds a quota check on edit: removed seasons are credited back before comparing the new season count against the remaining quota, and an edit that would exceed quota is rejected (403) unless a manager explicitly passes ignoreQuota (only usable by accounts with MANAGE_REQUESTS permission; non-managers passing it get rejected).

Also fixes the 3 request modals (MovieRequestModal, TvRequestModal, CollectionRequestModal) fetching quota only for the modal owner or a manager — a non-manager editing their own existing request via requestOverrides wasn't shown their own quota.

Fixes #633

AI Disclosure: This PR was written primarily by Claude Code, reviewed and directed by the account holder.

How Has This Been Tested?

Added a test suite in server/routes/request.test.ts covering: rejecting an edit beyond quota, rejecting a manager edit beyond quota without ignoreQuota, rejecting a non-manager attempting to set ignoreQuota themselves, allowing a manager to bypass quota via ignoreQuota, and allowing a same-count season swap at full quota. Full suite passes (pnpm test, 126/126), along with pnpm lint and pnpm build.

Screenshots / Logs (if applicable)

N/A — backend/logic fix, no visual change.

Checklist:

  • I have read and followed the contribution guidelines.
  • Disclosed any use of AI (see our policy).
  • I have updated the documentation accordingly.
  • All new and existing tests passed.
  • Successful build pnpm build
  • Translation keys pnpm i18n:extract
  • Database migration (if required)

Summary by CodeRabbit

  • Bug Fixes
    • Improved TV season quota enforcement by correctly accounting for seasons removed during edits.
    • Season swaps and replacements are now allowed when the final season count stays within quota.
    • Updated admin quota-bypass behavior and ensured unauthorized bypass attempts are rejected.
    • Quota/permission failures now consistently return clear access-denied responses.
  • Improvements
    • Request modals now fetch and display quota information consistently for the currently selected requester (including overrides) across movies, TV, and collections.
  • Tests
    • Added coverage for TV quota enforcement and ignore-quota bypass behavior, including edge cases with concurrent request counts.

kushiemoon-dev and others added 2 commits July 12, 2026 19:31
TvRequestModal, MovieRequestModal, and CollectionRequestModal all skip
fetching the requester's quota whenever requestOverrides.user.id is set
and the acting user lacks MANAGE_USERS. Editing an existing request
always populates requestOverrides.user with the request's original
owner (regardless of the acting user's permissions), so this condition
was true for essentially every edit - meaning quota was silently never
fetched, and the UI never disabled submission once the user's season
limit was reached. A user at their quota could open a pending request
and add further seasons past their limit.

This also affected the new admin "Bypass User Quota" checkbox added in
seerr-team#2026: its visibility depends on quota.tv.limit / quota.movie.limit
being loaded, so it silently failed to appear whenever this same
condition suppressed the fetch.

The fix removes the permission-gated condition and always fetches
quota for the effective target user. This doesn't weaken anything:
User.getQuota() already returns an unrestricted quota server-side for
users with MANAGE_USERS, so admins editing their own request are
unaffected; the change only ensures quota is actually loaded (and
enforced) for the common case of a regular user editing their own
request.

The corresponding server-side gap is fixed as well: PUT /request/:id
recomputed which seasons were being added but never checked them
against the requester's quota, so the limit could already be bypassed
directly via the API regardless of the frontend. It now runs the same
quota check used on request creation, including support for the
explicit ignoreQuota flag from seerr-team#2026 (an admin must opt in via
ignoreQuota: true - having MANAGE_REQUESTS alone no longer grants a
silent bypass), and persists request.ignoreQuota so the seasons added
this way are correctly excluded from future quota calculations.

Added test coverage in request.test.ts for: a regular user exceeding
their quota on edit, an admin edit without the explicit flag (still
enforced), a non-admin attempting to set the flag themselves (rejected),
and an admin using the flag to legitimately bypass quota.

Fixes seerr-team#633
CodeRabbit flagged that newSeasons.length was compared against
quota.tv.remaining without crediting back seasons the same edit
drops, since getQuota() counts persisted rows before the save. A
same-count season swap at full quota was rejected even though the
net season total wouldn't change.
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8f1c892f-5be4-4d9a-9965-00c5064bd7c8

📥 Commits

Reviewing files that changed from the base of the PR and between 0f866d0 and fc9ad62.

📒 Files selected for processing (2)
  • server/routes/request.test.ts
  • server/routes/request.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • server/routes/request.ts
  • server/routes/request.test.ts

📝 Walkthrough

Walkthrough

TV request edits now account for removed seasons during quota checks, enforce authorized ignoreQuota usage, return quota and permission failures as 403 responses, and add coverage for these cases. Request modals fetch quota for the selected request user.

Changes

TV Request Quota Enforcement

Layer / File(s) Summary
TV edit quota enforcement and validation
server/routes/request.ts, server/routes/request.test.ts
TV edits credit removed seasons, preserve authorized quota bypasses, map quota and permission errors to 403 responses, and test enforcement, bypass, persistence, and season replacement.
Request modal quota lookup
src/components/RequestModal/*RequestModal.tsx
Collection, movie, and TV modals fetch quota for the override user when present, otherwise using the current user.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant RequestRoute
  participant QuotaCheck
  participant RequestDatabase
  Client->>RequestRoute: Edit TV request seasons
  RequestRoute->>QuotaCheck: Evaluate added seasons minus removed seasons
  QuotaCheck-->>RequestRoute: Allow, deny, or honor authorized ignoreQuota
  RequestRoute->>RequestDatabase: Persist updated seasons
  RequestDatabase-->>Client: Updated request or 403 response
Loading

Possibly related PRs

Suggested reviewers: gauthier-th, m0nsterrr, fallenbagel

Poem

I’m a rabbit counting seasons bright,
Quotas guard the burrow right.
Swap one out, add one in,
Admins bypass with a permitted grin.
Tests now watch each leafy trail—
No sneaky season hops prevail!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main fix: preventing quota bypass when editing pending request seasons.
Linked Issues check ✅ Passed The changes address #633 by enforcing season quotas on pending TV request edits and blocking unauthorized bypasses.
Out of Scope Changes check ✅ Passed The frontend quota-fetch updates support the same request-edit quota fix and do not introduce unrelated scope.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/routes/request.ts`:
- Around line 571-604: Update the quota validation around removedSeasonsCount
and request.ignoreQuota: only credit removed seasons when the persisted request
was quota-counted (ignoreQuota is false), then compare the resulting new-season
usage against quota.tv.used and quota.tv.limit directly rather than clamped
quota.tv.remaining. Preserve authorized bypass behavior while ensuring later
edits to previously bypassed requests cannot add seasons beyond the quota.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7d34d321-0db4-48e7-9a70-3a3ed9c84afa

📥 Commits

Reviewing files that changed from the base of the PR and between 5ae70d0 and 0f866d0.

📒 Files selected for processing (5)
  • server/routes/request.test.ts
  • server/routes/request.ts
  • src/components/RequestModal/CollectionRequestModal.tsx
  • src/components/RequestModal/MovieRequestModal.tsx
  • src/components/RequestModal/TvRequestModal.tsx

Comment thread server/routes/request.ts
Seasons removed from an already-exempt request (ignoreQuota: true from
creation or a prior authorized edit) were never counted in
quota.tv.used, so crediting them back inflated the allowed threshold
and let a subsequent non-bypass edit add seasons past the real limit.
Also compare against quota.tv.used/limit directly instead of the
clamped quota.tv.remaining, which under-restricts once usage already
exceeds the limit.

Adds a regression test covering the swap-past-quota scenario.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

series request limit can be bypassed by users

1 participant