GHSA-V25J-WQCW-FVHJ: Partial fix for unbounded routine date ranges in wger
Summary
The patch introduces an upper bound on routine duration and enforces it in the API serializer, which directly mitigates the reported denial-of-service vector caused by iterating excessively large date sequences. However, the invariant is not enforced in the model clean path shown in the diff, so protection appears limited to serializer-mediated API writes rather than all object creation/update paths.
Analysis
Vulnerability
GHSA-V25J-WQCW-FVHJ describes an uncontrolled resource consumption issue in wger where authenticated attackers can create workout routines spanning excessively large date ranges. The security impact is a denial of service: downstream logic that walks a routine's date sequence can execute unbounded or extremely large loops, consuming CPU and tying up worker capacity.
The patch context indicates the root trigger is insufficient validation of start and end bounds. Before the change, the model validation shown in the diff only rejected inverted ranges, i.e. start > end, but did not cap the total duration. That left the application vulnerable to attacker-controlled amplification through very large but syntactically valid date intervals.
if self.end and self.start and self.start > self.end:
raise ValidationError(_('The start time cannot be after the end time.'))This aligns with the advisory and commit summary in the referenced sources: the issue is not malformed dates, but accepted oversized ranges that later drive expensive iteration. See the commit reference at the upstream patch and the advisory at GitHub Security Advisory.
Patch
The patch adds a hard maximum routine duration and enforces it during API serializer validation. Specifically, Routine.MAX_DURATION_DAYS = 120 is introduced in the model, and the serializer now rejects requests where (end - start).days > Routine.MAX_DURATION_DAYS. The existing check for start > end is also preserved in the serializer path.
def validate(self, data):
start = data.get('start') or getattr(self.instance, 'start', None)
end = data.get('end') or getattr(self.instance, 'end', None)
if start and end:
if start > end:
raise serializers.ValidationError(
{'end': 'The end date cannot be before the start date.'}
)
if (end - start).days > Routine.MAX_DURATION_DAYS:
raise serializers.ValidationError(
{'end': f'A routine cannot span more than {Routine.MAX_DURATION_DAYS} days.'}
)
return dataThe test suite additions are relevant and targeted. New API tests verify three cases: rejection of durations above the limit, acceptance exactly at the limit, and rejection of inverted ranges. That gives direct regression coverage for the reported attack surface exposed through the routine API. The patch source is available at commit 5f07a4473e2c32d298c8cdd31d78e5107840039c.
Review
Pros
The patch addresses the reported denial-of-service mechanism directly by bounding attacker-controlled iteration size. A fixed upper limit converts an unbounded workload into a predictable one, which is the correct control for this class of resource-exhaustion bug.
The implementation is simple and low risk: one constant, one validation branch, and focused tests. This reduces the chance of regressions while making the security invariant easy to understand.
The serializer logic handles both create and update flows by falling back to instance values when fields are omitted, which is important for partial updates. The tests also validate the exact boundary condition at MAX_DURATION_DAYS, which helps prevent off-by-one mistakes.
Cons
The main weakness is enforcement scope. In the provided diff, the new maximum-duration rule is implemented in wger/manager/api/serializers.py, but the model clean() snippet only shows the old chronological check and does not show the new duration cap being enforced at the model layer. If routines can be created or modified outside this serializer path—admin actions, management commands, fixtures, alternate serializers, direct ORM usage, or future endpoints—the invariant may be bypassed.
The introduction of Routine.MAX_DURATION_DAYS in the model is good for centralizing the constant, but the validation itself is not yet centralized in the model code shown. That means the patch mitigates the disclosed API vector without fully hardening the domain object against inconsistent callers.
The tests are API-focused and do not demonstrate model-level rejection for oversized ranges. As a result, they confirm endpoint behavior but not universal enforcement.
Verdict
Partial fix.
The patch is effective against the documented attack path because it blocks oversized date ranges at the API boundary and adds regression tests for that behavior. However, based on the supplied diff, it does not fully eliminate the root cause across all write paths, since the duration constraint is not shown in model validation alongside the existing start > end check. For a root-cause fix, the same maximum-span invariant should be enforced in the model layer as well, with corresponding non-API tests. References: upstream commit, GitHub advisory, report summary.
Recommended Labs
Try this vulnerability pattern yourself with hands-on labs.
- Energy.py
Best match for this wger issue because it is a Python hands-on lab mapped to CWE-400 and CWE-770, which align with uncontrolled resource consumption and allocation without limits. It should help practice defensive fixes such as bounding expensive operations, validating attacker-controlled input sizes, and adding guards to prevent CPU or worker exhaustion.
- No Sutpo.py
Another strong Python match focused on CWE-400/CWE-770 style exhaustion patterns. This is relevant to the unbounded date-sequence loop in the advisory because the core defensive skill is enforcing hard limits on request-driven work and preventing authenticated users from triggering disproportionate server-side processing.
- Badtar.py
Useful adjacent Python lab for practicing denial-of-service and resource exhaustion defenses under CWE-400. While not date-range specific, it reinforces defensive patterns that matter for this patch review: constrain expansion work, reject abusive input early, and avoid server-side operations whose cost grows unbounded from user-controlled data.