Skip to content

Commit c4a9d7a

Browse files
dguidoclaude
andcommitted
feat: add URL scheme validation for security
- Add validate_url_scheme() function using urlparse - Validate all urllib operations only allow http/https schemes - Prevent potential file:// URL injection attacks - Important security hardening for a tool that downloads executables 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 8b0f917 commit c4a9d7a

File tree

1 file changed

+13
-0
lines changed

1 file changed

+13
-0
lines changed

solc_select/solc_select.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import sys
88
import urllib.request
99
from pathlib import Path
10+
from urllib.parse import urlparse
1011
from zipfile import ZipFile
1112

1213
from Crypto.Hash import keccak
@@ -27,6 +28,13 @@
2728
Path.mkdir(ARTIFACTS_DIR, parents=True, exist_ok=True)
2829

2930

31+
def validate_url_scheme(url: str) -> None:
32+
"""Validate that URL uses a safe scheme (http or https only)."""
33+
parsed = urlparse(url)
34+
if parsed.scheme not in ("http", "https"):
35+
raise ValueError(f"Unsafe URL scheme '{parsed.scheme}' in URL: {url}")
36+
37+
3038
def halt_old_architecture(path: Path) -> None:
3139
if not Path.is_file(path):
3240
raise argparse.ArgumentTypeError(
@@ -123,6 +131,7 @@ def install_artifacts(versions: [str], silent: bool = False) -> bool:
123131
Path.mkdir(artifact_file_dir, parents=True, exist_ok=True)
124132
if not silent:
125133
print(f"Installing solc '{version}'...")
134+
validate_url_scheme(url)
126135
urllib.request.urlretrieve(url, artifact_file_dir.joinpath(f"solc-{version}"))
127136

128137
verify_checksum(version)
@@ -179,6 +188,7 @@ def verify_checksum(version: str) -> None:
179188
def get_soliditylang_checksums(version: str) -> (str, str):
180189
(_, list_url) = get_url(version=version)
181190
# pylint: disable=consider-using-with
191+
validate_url_scheme(list_url)
182192
list_json = urllib.request.urlopen(list_url).read()
183193
builds = json.loads(list_json)["builds"]
184194
matches = list(filter(lambda b: b["version"] == version, builds))
@@ -262,11 +272,13 @@ def get_installable_versions() -> [str]:
262272
# pylint: disable=consider-using-with
263273
def get_available_versions() -> [str]:
264274
(_, list_url) = get_url()
275+
validate_url_scheme(list_url)
265276
list_json = urllib.request.urlopen(list_url).read()
266277
available_releases = json.loads(list_json)["releases"]
267278
# pylint: disable=consider-using-with
268279
if soliditylang_platform() == LINUX_AMD64:
269280
(_, list_url) = get_url(version=EARLIEST_RELEASE[LINUX_AMD64])
281+
validate_url_scheme(list_url)
270282
github_json = urllib.request.urlopen(list_url).read()
271283
additional_linux_versions = json.loads(github_json)["releases"]
272284
available_releases.update(additional_linux_versions)
@@ -288,6 +300,7 @@ def soliditylang_platform() -> str:
288300

289301
def get_latest_release() -> str:
290302
(_, list_url) = get_url()
303+
validate_url_scheme(list_url)
291304
list_json = urllib.request.urlopen(list_url).read()
292305
latest_release = json.loads(list_json)["latestRelease"]
293306
return latest_release

0 commit comments

Comments
 (0)