CVE-2026-27838
LOW3.1EPSS 0.04%wger: IDOR via user-unscoped cache keys on routine API actions exposes workout data
Description
### Summary Five routine detail action endpoints check a cache before calling `self.get_object()`. Cache keys are scoped only by `pk` — no user ID is included. When a victim has previously accessed their routine via the API, an attacker can retrieve the cached response for the same PK without any ownership check. ### Details `wger/manager/api/views.py` — five actions follow this pattern (lines 134–201): ```python @action(detail=True) def date_sequence_display_mode(self, request, pk=None): cache_key = make_routine_api_date_sequence_display_cache_key(pk) cached = cache.get(cache_key) if cached: return Response(cached) # returned WITHOUT calling self.get_object() # only reaches ownership check on cache miss routine = self.get_object() ... ``` Cache key construction in `wger/utils/cache.py:89–106`: ```python def make_routine_api_date_sequence_display_cache_key(routine_id): return f"routine-api-date-sequence-display-{routine_id}" # No user ID in key ``` Cache TTL: 1 month (`4 * 604800` seconds, `settings_global.py:461`). Affected endpoints: ``` GET /api/v2/routine/{pk}/date-sequence-display/ GET /api/v2/routine/{pk}/date-sequence-gym/ GET /api/v2/routine/{pk}/structure/ GET /api/v2/routine/{pk}/logs/ GET /api/v2/routine/{pk}/stats/ ``` ### PoC ``` 1. Victim (user A) visits GET /api/v2/routine/5/structure/ → response cached under key "routine-api-structure-5" 2. Attacker (user B) visits GET /api/v2/routine/5/structure/ → cache hit → returns user A's routine structure without any ownership check ``` Requires the victim to have previously accessed the endpoint (cache must be populated). Once populated, the cache entry is valid for 1 month. ### Impact An attacker with a registered account can retrieve another user's routine details — workout day sequences, exercise structure, training logs, and statistics — from cache without ownership verification. **Fix**: Include the user ID in the cache key: ```python def make_routine_api_date_sequence_display_cache_key(routine_id, user_id): return f"routine-api-date-sequence-display-{user_id}-{routine_id}" ``` Or move `self.get_object()` before the cache lookup so ownership is always verified first.
Affected packages (1)
- PyPI/wgerfrom 0, <= 2.1
CVSS scores
| Source | Version | Severity | Vector |
|---|---|---|---|
| osv | CVSS 3.1 | LOW3.1 | CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N |