From e63965deb31552cd8535fab9aec226e421c8fe37 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:14:22 +0000 Subject: [PATCH 1/3] Initial plan From cecff8a4657c1d7beadb73af0bb3e193ad755a4e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:17:43 +0000 Subject: [PATCH 2/3] fix: follow redirects when mirroring plugin archives Agent-Logs-Url: https://github.com/HexRaysSA/plugin-repository/sessions/4e69d154-2431-429a-892a-9b09cc77a213 Co-authored-by: williballenthin <156560+williballenthin@users.noreply.github.com> --- scripts/mirror_plugin_archive_contents.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/scripts/mirror_plugin_archive_contents.py b/scripts/mirror_plugin_archive_contents.py index ab4dd64..dc0e503 100644 --- a/scripts/mirror_plugin_archive_contents.py +++ b/scripts/mirror_plugin_archive_contents.py @@ -49,6 +49,7 @@ import hashlib import logging import shutil +import urllib.request from pathlib import Path import rich.console @@ -68,6 +69,19 @@ stderr_console = rich.console.Console(stderr=True) +def fetch_plugin_archive_with_redirects(url: str) -> bytes: + try: + return fetch_plugin_archive(url) + except Exception as e: + response = getattr(e, "response", None) + if response is None or response.status_code not in {301, 302, 303, 307, 308}: + raise + + logger.info("following redirect for plugin archive: %s", url) + with urllib.request.urlopen(url) as response: + return response.read() + + def do_cache(json_path: Path, out_path: Path, no_cache: bool = False): repo = JSONFilePluginRepo.from_file(json_path) plugins = repo.get_plugins() @@ -104,7 +118,7 @@ def do_cache(json_path: Path, out_path: Path, no_cache: bool = False): assert location.url.startswith("https://") - zip_data = fetch_plugin_archive(location.url) + zip_data = fetch_plugin_archive_with_redirects(location.url) metadata_path, metadata = get_metadata_from_plugin_archive(zip_data, plugin.name) validate_metadata_in_plugin_archive(zip_data, metadata_path, metadata) From f27e9a0ace349f4b2d0f046a9ac6f6233c59f56b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:20:14 +0000 Subject: [PATCH 3/3] refine: harden redirect fallback for mirrored archives Agent-Logs-Url: https://github.com/HexRaysSA/plugin-repository/sessions/4e69d154-2431-429a-892a-9b09cc77a213 Co-authored-by: williballenthin <156560+williballenthin@users.noreply.github.com> --- scripts/mirror_plugin_archive_contents.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/scripts/mirror_plugin_archive_contents.py b/scripts/mirror_plugin_archive_contents.py index dc0e503..3961ba5 100644 --- a/scripts/mirror_plugin_archive_contents.py +++ b/scripts/mirror_plugin_archive_contents.py @@ -40,6 +40,7 @@ # /// script # requires-python = ">=3.13" # dependencies = [ +# "httpx", # "ida-hcli", # "rich", # ] @@ -52,6 +53,7 @@ import urllib.request from pathlib import Path +import httpx import rich.console import rich.progress from hcli.lib.ida.plugin import ( @@ -72,13 +74,19 @@ def fetch_plugin_archive_with_redirects(url: str) -> bytes: try: return fetch_plugin_archive(url) - except Exception as e: - response = getattr(e, "response", None) - if response is None or response.status_code not in {301, 302, 303, 307, 308}: + except httpx.HTTPStatusError as e: + if e.response.status_code not in {301, 302, 303, 307, 308}: raise - logger.info("following redirect for plugin archive: %s", url) - with urllib.request.urlopen(url) as response: + logger.info( + "retrying with urllib.request to follow redirect (%s) for plugin archive: %s", + e.response.status_code, + url, + ) + with urllib.request.urlopen(url, timeout=30) as response: + redirected_url = response.geturl() + if not redirected_url.startswith("https://"): + raise ValueError(f"redirected plugin archive URL is not HTTPS: {redirected_url}") return response.read()