diff --git a/src/murfey/server/api/bootstrap.py b/src/murfey/server/api/bootstrap.py index 5a9661b34..5558305d6 100644 --- a/src/murfey/server/api/bootstrap.py +++ b/src/murfey/server/api/bootstrap.py @@ -255,7 +255,7 @@ def parse_cygwin_request(request_path: str): # Variables used by the MSYS2 functions below msys2_url = "https://repo.msys2.org" -msys2_setup_file = "msys2-x86_64-latest.exe" +msys2_file_ext = (".exe", ".sig", ".tar.xz", "tar.zst") valid_envs = ( # Tuple of systems and supported libraries/compilers/architectures within ( @@ -289,15 +289,25 @@ def parse_cygwin_request(request_path: str): ) -@msys2.get("/setup-x86_64.exe", response_class=Response) -def get_msys2_setup(): +@msys2.get("/distrib/{setup_file}", response_class=Response) +def get_msys2_setup(setup_file: str): """ Obtain and pass through an MSYS2 installer from an official source. This is used during client bootstrapping, and can download and install the MSYS2 distribution that then remains on the client machines. """ - installer = requests.get(f"{msys2_url}/distrib/{msys2_setup_file}") + # Validate characters in sent path + if not bool(re.fullmatch(r"^[\w\.\-]+$", setup_file)): + raise ValueError("Unallowed characters present in requested setup file") + + # Allow only '.exe', 'tar.xz', 'tar.zst', or '.sig' files + if not setup_file.startswith("msys2") and not any( + setup_file.endswith(ext) for ext in (msys2_file_ext) + ): + raise ValueError(f"{setup_file!r} is not a valid executable") + + installer = requests.get(f"{msys2_url}/distrib/{setup_file}") return Response( content=installer.content, media_type=installer.headers.get("Content-Type"), @@ -314,24 +324,6 @@ def get_msys2_main_index( from the main MSYS2 repository. """ - def get_msys2_setup_html(): - """ - Returns the HTML line for the latest MSYS2 installer for Windows from an official - source. - """ - url = f"{msys2_url}/distrib" - index = requests.get(url) - content: bytes = index.content - content_text: str = content.decode("latin1") - - for line in content_text.splitlines(): - if line.startswith("]*)">([^<]*)', # Regex search criteria _rewrite_url, # Function to apply search criteria to @@ -367,26 +359,6 @@ def _rewrite_url(match): ) content_text_list.append(line_new) - # Replace the "distrib/" hyperlink with one to the setup file - elif "distrib" in line: - # Set up URL to be requested on the Murfey server - mirror_file_name = "setup-x86_64.exe" - setup_url = f"{base_path}/{mirror_file_name}" - - # Get request from the "distrib" page and rewrite it - setup_html = get_msys2_setup_html() - if setup_html is None: - # Skip showing the setup file link if it fails to find it - continue - - line_new = " ".join( # Adjust spaces to align columns - re.sub( - '^]*)">([^"<]*)', - f'{mirror_file_name}', - setup_html, - ).split(" ", 1) - ) - content_text_list.append(line_new) # Other URLs don't need to be mirrored else: pass @@ -430,8 +402,8 @@ def _rewrite_url(match): path = request.url.path.strip("/") base_path = f"{base_url}/{path}" - # Validate provided system - if any(system in env[0] for env in valid_envs) is False: + # Validate provided system; use this endpoint to display 'distrib' folder too + if not (any(system in env[0] for env in valid_envs) or system == "distrib"): raise ValueError(f"{system!r} is not a valid msys2 environment") # Construct URL to main MSYS repo and get response @@ -444,6 +416,11 @@ def _rewrite_url(match): content_text_list = [] for line in content_text.splitlines(): if line.startswith("]*)">([^<]*)', # Regex search criteria