-
-
Notifications
You must be signed in to change notification settings - Fork 40
Security: version argument unvalidated and concatenated into firmware download URL #2071
Description
Problem
The version parameter accepted by upgrade(version=...) is interpolated twice into the download URL with no validation:
update_file = f"WLED_{version}_{architecture}{ethernet}.bin{gzip}" download_url = ( f"https://github.com/{repo}/releases/download/v{version}/{update_file}" )
A value such as version="0.14.0/../../attacker/wled-mal/releases/download/v9.9.9/payload" is normalized by yarl/aiohttp into a path that points at a different release in a different repository. The same hole exists for the repo argument, which is appended raw and defaults to wled/WLED. Any downstream application that takes version/repo from user input (Home Assistant integrations, web dashboards, etc.) inherits this URL-injection primitive.
Why This Matters
Allows an attacker who controls the version (or repo) input — for instance via a web UI that passes user input straight to this library — to force download of firmware from an arbitrary GitHub repository, which is then flashed onto the target device.
Suggested Fix
Validate version against a strict semantic-version pattern (^v?\d+\.\d+\.\d+(?:[-+.][A-Za-z0-9.]+)?$) and repo against ^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$ before use. Raise WLEDUpgradeError on mismatch:
import re if not re.match(r"^\d+\.\d+\.\d+([-+.][A-Za-z0-9.]+)?$", str(version)): raise WLEDUpgradeError(f"Invalid version: {version!r}") if not re.match(r"^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$", repo): raise WLEDUpgradeError(f"Invalid repo: {repo!r}")
Details
| Severity | 🟡 Medium |
| Category | request_handling |
| Location | src/wled/wled.py:702-716 |
| Effort | ⚡ Quick fix |
🤖 Created by Kōan from audit session