Advisories for Pypi/Wger package

2026

wger: trainer_login accepts GET - CSRF bypass enables forced session rebinding

The trainer_login view in wger accepts GET requests and executes django_login() without any CSRF protection, because Django's CsrfViewMiddleware only enforces tokens on unsafe methods (POST/PUT/PATCH/DELETE). An attacker can embed a single <img> tag on a malicious page; when an authenticated trainer loads that page, their browser auto-issues the GET with the session cookie, forcibly rebinding the trainer's session to an arbitrary user account.

wger: Trainer Privilege Escalation - Improper Privilege Management

A user with only the gym_trainer permission can deactivate any account in the same gym, including gym_manager and general_gym_manager accounts. The UserDeactivateView grants access to anyone holding any one of gym.manage_gym, gym.manage_gyms, or gym.gym_trainer (OR logic via WgerMultiplePermissionRequiredMixin), and performs no privilege-hierarchy check to prevent a lower-privileged role from disabling a higher-privileged one.

wger: Cross-User Data Corruption via Missing Ownership Check on WorkoutLog.slot_entry

An authenticated attacker can inject arbitrary workout log entries into any other user's SlotEntry by supplying the victim's slot_entry ID in a POST /api/v2/workoutlog/ request. The slot_entry foreign key is not included in the ownership verification performed by WorkoutLogViewSet.get_owner_objects(), so the server accepts and persists the cross-user reference without error. Because SlotEntry.get_config_data() retrieves associated logs via self.workoutlog_set.all() with no user filter, the attacker's injected data is silently folded into the …

wger: cross-tenant admin notes/contracts leak via gym=None bypass (5 views)

Five gym management views in wger apply a flawed gym-scope guard (gym_a != gym_b) that silently passes when both operands are None. A trainer with gym.gym_trainer and gym.add_adminusernote permissions and no gym assignment (gym=None) can read private admin notes, uploaded documents, gym contracts, user configuration, and user permission data for any other unaffiliated user on the instance. The subsequent querysets filter only on the attacker-supplied member_id with no secondary gym-scoped …

wger: API credentials remain valid after logout/password change

A vulnerability exists in the authentication/session lifecycle of wger where bearer-style API credentials remain valid after a user logs out and after a user changes their password. An attacker who steals a victim’s DRF authtoken (Authorization: Token …) or JWT refresh token can continue to access protected /api/v2/* endpoints until the token is manually rotated/deleted (DRF token) or naturally expires (JWT refresh). lifecycle events do not revoke these credentials: Logout …

wger: cross-tenant account deletion / deactivation / activation by gym.manage_gym + gym=None

GHSA-mhc8-p3jx-84mm (CVE-2026-43948) reported that wger's reset_user_password and gym_permissions_user_edit views in wger/gym/views/user.py performed a gym-scope authorization check using Django ORM object comparison (if request.user.userprofile.gym != user.userprofile.gym) which silently passes when both sides are None (None != None evaluates to False). The maintainer's suggested patch ("Apply the same same_gym() helper pattern to all five views sharing this check") replaces every userprofile.gym != site with the new is_same_gym() helper that explicitly excludes None …

wger: Privilege escalation via trainer-login session chaining allows gym trainer to impersonate gym manager

A gym trainer can escalate their session to any higher-privileged account (gym manager, general manager) by chaining two calls to the trainer-login endpoint. Once a trainer performs a legitimate switch into a low-privileged user, the session flag trainer.identity is set and this flag alone bypasses the permission check on all subsequent trainer-login calls, allowing the trainer to hop into any account including gym managers.

wger Vulnerable to IDOR: Authenticated Users Can Read Any User's Private Workout Session Data via Template Routine API

Any authenticated user can read another user's private workout session notes, exercise history, and training statistics by calling the /logs/ and /stats/ actions on a routine they do not own. The RoutinePermission class grants read access to any authenticated user when a routine has is_template=True, regardless of ownership. The /logs/ and /stats/ API actions use the same permission check but return the routine owner's personal training data instead of the …

wger has an Uncontrolled Resource Consumption issue

Any authenticated user can create a routine spanning an arbitrarily long date range (e.g. 100 years) and then trigger the date_sequence computation via any of the routine detail endpoints. The server iterates once per day in an unbounded while loop with no maximum duration validation, causing a single HTTP request to consume multiple seconds of server CPU and return a response containing tens of thousands of entries. Repeated requests can …

wger: CSV/TSV formula injection in gym member export (first_name/last_name)

The gym member TSV export endpoint in wger writes first_name and last_name profile fields verbatim to TSV cells with no formula-prefix sanitization. Any gym member (including newly self-registered users) can pre-load a spreadsheet formula into their own profile. When a gym admin later exports the member list and opens the file in Excel, LibreOffice Calc, or Google Sheets, the formula executes in the admin's local spreadsheet context — enabling data …

wger: cross-tenant password reset and plaintext disclosure via gym=None bypass

The reset_user_password and gym_permissions_user_edit views in wger perform a gym-scope authorization check using Python object comparison (!=) that evaluates None != None as False, silently bypassing the guard when both the attacker and victim have no gym assignment (gym=None). A user with gym.manage_gym permission and gym=None can reset the password of any other gym=None user; the new plaintext password is returned verbatim in the HTML response body, enabling one-shot full …

wger has Stored XSS via Unescaped License Attribution Fields

The AbstractLicenseModel.attribution_link property in wger/utils/models.py constructs HTML strings by directly interpolating user-controlled fields (license_author, license_title, license_object_url, license_author_url, license_derivative_source_url) without any escaping. The resulting HTML is rendered in the ingredient view template using Django's |safe filter, which disables auto-escaping. An authenticated user can create an ingredient with a malicious license_author value containing JavaScript, which executes when any user (including unauthenticated visitors) views the ingredient page.

wger has Broken Access Control in Global Gym Configuration Update Endpoint

wger exposes a global configuration edit endpoint at /config/gym-config/edit implemented by GymConfigUpdateView. The view declares permission_required = 'config.change_gymconfig' but does not enforce it because it inherits WgerFormMixin (ownership-only checks) instead of the project’s permission-enforcing mixin (WgerPermissionMixin) . The edited object is a singleton (GymConfig(pk=1)) and the model does not implement get_owner_object(), so WgerFormMixin skips ownership enforcement. As a result, a low-privileged authenticated user can modify installation-wide configuration and trigger server-side …

2023

wger Workout Manager Cross-site Scripting vulnerability

Cross Site Scripting vulnerability in wger Project wger Workout Manager v.2.2.0a3 allows a remote attacker to gain privileges via the license_author field in the add-ingredient function in the templates/ingredients/view.html, models/ingredients.py, and views/ingredients.py components.

wger Workout Manager Cross-Site Request Forgery vulnerability

Cross Site Request Forgery (CSRF) vulnerability in wger Project wger Workout Manager 2.2.0a3 allows a remote attacker to gain privileges via the user-management feature in the gym/views/gym.py, templates/gym/reset_user_password.html, templates/user/overview.html, core/views/user.py, and templates/user/preferences.html, core/forms.py components.

2022