|
5 | 5 |
|
6 | 6 | try: |
7 | 7 | from urllib.request import urlopen, Request |
| 8 | + from urllib.parse import urlparse |
8 | 9 | except ImportError: |
9 | 10 | from urllib2 import urlopen, Request |
| 11 | + from urlparse import urlparse |
10 | 12 |
|
11 | 13 | class LocalBinary: |
12 | 14 | _version = None |
| 15 | + ALLOWED_DOWNLOAD_HOSTS = ("browserstack.com",) |
| 16 | + ALLOWED_DOWNLOAD_HOST_SUFFIXES = (".browserstack.com",) |
| 17 | + |
| 18 | + @staticmethod |
| 19 | + def _validate_source_url(url): |
| 20 | + parsed = urlparse(url or "") |
| 21 | + if parsed.scheme != "https": |
| 22 | + raise BrowserStackLocalError( |
| 23 | + "Refusing binary download from non-HTTPS source URL") |
| 24 | + host = (parsed.hostname or "").lower() |
| 25 | + if not host: |
| 26 | + raise BrowserStackLocalError( |
| 27 | + "Refusing binary download: source URL has no host") |
| 28 | + if host in LocalBinary.ALLOWED_DOWNLOAD_HOSTS: |
| 29 | + return url |
| 30 | + for suffix in LocalBinary.ALLOWED_DOWNLOAD_HOST_SUFFIXES: |
| 31 | + if host.endswith(suffix): |
| 32 | + return url |
| 33 | + raise BrowserStackLocalError( |
| 34 | + "Refusing binary download: host '{}' is not in the allowed host list".format(host)) |
13 | 35 |
|
14 | 36 | def __init__(self, key, error_object=None): |
15 | 37 | self.key = key |
@@ -64,7 +86,9 @@ def fetch_source_url(self): |
64 | 86 | resp_bytes = response.read() |
65 | 87 | resp_str = resp_bytes.decode('utf-8') |
66 | 88 | resp_json = json.loads(resp_str) |
67 | | - return resp_json["data"]["endpoint"] |
| 89 | + return self._validate_source_url(resp_json["data"]["endpoint"]) |
| 90 | + except BrowserStackLocalError: |
| 91 | + raise |
68 | 92 | except Exception as e: |
69 | 93 | raise BrowserStackLocalError('Error trying to fetch the source url for downloading the binary: {}'.format(e)) |
70 | 94 |
|
|
0 commit comments