Skip to content

Missing Object-Level Authorization Allowed Deleting and Reassigning Other Users' PACER Fetch Requests

Moderate
mlissner published GHSA-5f8h-qjq5-6h64 Aug 15, 2026

Package

courtlistener (git)

Affected versions

main

Patched versions

None

Description

Summary

PacerFetchRequestViewSet serves PacerFetchQueue, which records the PACER purchases made by our users. Due to insufficient authorization constraints, it was possible for an attacker to alter or delete the requests of other users, including reassigning the fetch requests to other users.

By design this API created an anonymous public log of every request — such requests were not considered private and the developer convenience of seeing requests without authentication was determined to outweigh the risk of publicly accessible logs. In resolving this vulnerability, we determined that it is best to make previous requests private, and we've implemented that change in version 4.7 of the API.

Severity

High (CVSS 3.1: 7.1)

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L

  • Attack Vector: Network -- plain HTTP requests to a public REST API.
  • Attack Complexity: Low -- primary keys are sequential/enumerable integers; no race condition or special conditions.
  • Privileges Required: Low -- every part of the actual impact (tampering, cancellation, ownership reassignment) required an authenticated account (trivial self-registration). The unauthenticated read path disclosed no identity/ownership data, so it carried no confidentiality impact of its own.
  • User Interaction: None -- fully automatable, no victim action required.
  • Scope: Unchanged -- impact stayed within the CourtListener application's own data/authorization boundary.
  • Confidentiality Impact: None -- the exposed fields carried no user attribution. Anonymous read access to this shared activity log was intentional design, not a defect, and has been left unchanged by the fix.
  • Integrity Impact: Low -- any authenticated account could modify another user's pending fetch-queue row or reassign its ownership,, but such modifications would be quickly identified and of limited consequence.
  • Availability Impact: Low -- deleting a completed row had no functional effect (it's a disposable receipt once the underlying PACER purchase has already run); the only real availability impact was the ability to disrupt a still-pending, in-flight fetch before it executed.

Affected Component

  • cl/recap/views.py -- PacerFetchRequestViewSet (unscoped queryset, no object-level permission, no get_queryset() override)
  • cl/api/urls.py -- router registration (recap-fetch -> PacerFetchRequestViewSet) and v3/v4 route inclusion
  • cl/recap/api_serializers.py -- PacerFetchQueueSerializer (user hidden on both read and write, but no scoping anywhere)
  • cl/recap/filters.py -- PacerFetchQueueFilter (no user-scoping in the filter definition)

CWE

  • CWE-862: Missing Authorization (no object-level permission class or ownership check existed for this viewset -- unlike sibling viewsets in the same codebase)
  • CWE-639: Authorization Bypass Through User-Controlled Key (IDOR by primary key across list/retrieve/update/delete)

Impact

  • Any self-registered account could delete or tamper with any other user's pending or historical fetch-queue row, with no ownership check. Deleting a completed row had no real functional impact (it's a disposable receipt once the underlying PACER purchase already ran), but deleting or PUT-overwriting a still-pending row could disrupt or redirect an in-flight, real-money PACER purchase the victim had already initiated, or silently reassign that row's ownership to the attacker.
  • No special permission was required for any part of this attack -- read access required nothing, and write/delete access required only ordinary free registration.

Remediation

Fixed by removing update/destroy support from the viewset entirely and scoping reads by ownership.

  • No update or delete action is defined on the viewset at all, so DRF's router never wires up PATCH/PUT/DELETE for it in the first place -- structurally simpler and more robust than gating those actions with a permission check, since the actions no longer exist to be reached by any caller, ever.
  • get_queryset() now scopes list/retrieve to the requesting user's own rows, closing the confidentiality/enumeration surface even though it was never identity-linked.
  • IsOwner (the same permission already used by VisualizationViewSet/JSONViewSet) is layered on top as defense in depth, so an object-level check still fires even if a future change to get_queryset() ever stops scoping correctly.
  • IsAuthenticatedOrReadOnly was replaced with IsAuthenticated: anonymous access is removed entirely, since there is nothing for a logged-out visitor to see once results are owner-scoped. AnonRateThrottle was dropped from throttle_classes for the same reason -- it can never trigger once every request must be authenticated.

Net effect: an account can only ever see and create its own fetch requests; nothing can be edited or deleted via the API once created, by anyone, including the owner.

Credit

This vulnerability was discovered and reported by bugbunny.ai.

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
Low
Availability
Low

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L

CVE ID

No known CVE

Weaknesses

No CWEs

Credits