From d8c449b68ef897c9a249a113f2029116fccba7c1 Mon Sep 17 00:00:00 2001 From: rounak bhatia Date: Thu, 4 Jun 2026 19:21:20 +0530 Subject: [PATCH] added host check for binary download url --- browserstack/local_binary.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/browserstack/local_binary.py b/browserstack/local_binary.py index 13144fa..f2baf58 100644 --- a/browserstack/local_binary.py +++ b/browserstack/local_binary.py @@ -5,11 +5,33 @@ try: from urllib.request import urlopen, Request + from urllib.parse import urlparse except ImportError: from urllib2 import urlopen, Request + from urlparse import urlparse class LocalBinary: _version = None + ALLOWED_DOWNLOAD_HOSTS = ("browserstack.com",) + ALLOWED_DOWNLOAD_HOST_SUFFIXES = (".browserstack.com",) + + @staticmethod + def _validate_source_url(url): + parsed = urlparse(url or "") + if parsed.scheme != "https": + raise BrowserStackLocalError( + "Refusing binary download from non-HTTPS source URL") + host = (parsed.hostname or "").lower() + if not host: + raise BrowserStackLocalError( + "Refusing binary download: source URL has no host") + if host in LocalBinary.ALLOWED_DOWNLOAD_HOSTS: + return url + for suffix in LocalBinary.ALLOWED_DOWNLOAD_HOST_SUFFIXES: + if host.endswith(suffix): + return url + raise BrowserStackLocalError( + "Refusing binary download: host '{}' is not in the allowed host list".format(host)) def __init__(self, key, error_object=None): self.key = key @@ -64,7 +86,9 @@ def fetch_source_url(self): resp_bytes = response.read() resp_str = resp_bytes.decode('utf-8') resp_json = json.loads(resp_str) - return resp_json["data"]["endpoint"] + return self._validate_source_url(resp_json["data"]["endpoint"]) + except BrowserStackLocalError: + raise except Exception as e: raise BrowserStackLocalError('Error trying to fetch the source url for downloading the binary: {}'.format(e))