mirror of
https://github.com/github/spec-kit.git
synced 2026-07-03 12:28:06 +08:00
Validate post-redirect URL scheme before reading response body
This commit is contained in:
committed by
GitHub
parent
7344071b7e
commit
5d75366fd5
@@ -2637,6 +2637,14 @@ def preset_add(
|
||||
try:
|
||||
with urllib.request.urlopen(from_url, timeout=60) as response:
|
||||
final_url = response.geturl()
|
||||
# Re-validate scheme after any redirect (scheme-downgrade
|
||||
# guard). Check BEFORE reading the body so an insecure
|
||||
# redirect cannot cause us to fetch the payload.
|
||||
_fp = _urlparse(final_url)
|
||||
_fl = _fp.hostname in ("localhost", "127.0.0.1", "::1")
|
||||
if _fp.scheme != "https" and not (_fp.scheme == "http" and _fl):
|
||||
console.print(f"[red]Error:[/red] URL was redirected to a non-HTTPS URL: {final_url}")
|
||||
raise typer.Exit(1)
|
||||
content_type = response.headers.get("Content-Type", "")
|
||||
# Prefer the post-redirect URL for format detection;
|
||||
# fall back to the original URL only as a last hint.
|
||||
@@ -2648,13 +2656,6 @@ def preset_add(
|
||||
console.print(f"[red]Error:[/red] Failed to download: {e}")
|
||||
raise typer.Exit(1)
|
||||
|
||||
# Re-validate scheme after any redirect (scheme-downgrade guard).
|
||||
_fp = _urlparse(final_url)
|
||||
_fl = _fp.hostname in ("localhost", "127.0.0.1", "::1")
|
||||
if _fp.scheme != "https" and not (_fp.scheme == "http" and _fl):
|
||||
console.print(f"[red]Error:[/red] URL was redirected to a non-HTTPS URL: {final_url}")
|
||||
raise typer.Exit(1)
|
||||
|
||||
if not archive_fmt:
|
||||
console.print("[red]Error:[/red] Could not determine archive format from URL or Content-Type.")
|
||||
console.print("Ensure the URL points to a .zip or .tar.gz/.tgz file.")
|
||||
@@ -3671,19 +3672,20 @@ def extension_add(
|
||||
try:
|
||||
with urllib.request.urlopen(from_url, timeout=60) as response:
|
||||
final_url = response.geturl()
|
||||
# Re-validate scheme after any redirect (scheme-downgrade
|
||||
# guard). Check BEFORE reading the body so an insecure
|
||||
# redirect cannot cause us to fetch the payload.
|
||||
_fp = urlparse(final_url)
|
||||
_fl = _fp.hostname in ("localhost", "127.0.0.1", "::1")
|
||||
if _fp.scheme != "https" and not (_fp.scheme == "http" and _fl):
|
||||
console.print(f"[red]Error:[/red] URL was redirected to a non-HTTPS URL: {final_url}")
|
||||
raise typer.Exit(1)
|
||||
content_type = response.headers.get("Content-Type", "")
|
||||
archive_fmt = detect_archive_format(final_url, content_type)
|
||||
if not archive_fmt:
|
||||
archive_fmt = detect_archive_format(from_url)
|
||||
archive_data = response.read()
|
||||
|
||||
# Re-validate scheme after any redirect (scheme-downgrade guard).
|
||||
_fp = urlparse(final_url)
|
||||
_fl = _fp.hostname in ("localhost", "127.0.0.1", "::1")
|
||||
if _fp.scheme != "https" and not (_fp.scheme == "http" and _fl):
|
||||
console.print(f"[red]Error:[/red] URL was redirected to a non-HTTPS URL: {final_url}")
|
||||
raise typer.Exit(1)
|
||||
|
||||
if not archive_fmt:
|
||||
console.print("[red]Error:[/red] Could not determine archive format from URL or Content-Type.")
|
||||
console.print("Ensure the URL points to a .zip or .tar.gz/.tgz file.")
|
||||
|
||||
@@ -2165,6 +2165,22 @@ class ExtensionCatalog:
|
||||
try:
|
||||
with self._open_url(download_url, timeout=60) as response:
|
||||
final_url = response.geturl()
|
||||
# Re-validate scheme after any redirect to guard against
|
||||
# scheme-downgrade. Validate BEFORE reading the body so a
|
||||
# malicious redirect cannot cause us to fetch the payload
|
||||
# over an insecure scheme.
|
||||
_final_parsed = urlparse(final_url)
|
||||
_final_is_localhost = _final_parsed.hostname in (
|
||||
"localhost",
|
||||
"127.0.0.1",
|
||||
"::1",
|
||||
)
|
||||
if _final_parsed.scheme != "https" and not (
|
||||
_final_parsed.scheme == "http" and _final_is_localhost
|
||||
):
|
||||
raise ExtensionError(
|
||||
f"Extension download URL was redirected to a non-HTTPS URL: {final_url}"
|
||||
)
|
||||
content_type = response.headers.get("Content-Type", "")
|
||||
archive_fmt = detect_archive_format(final_url, content_type)
|
||||
if not archive_fmt:
|
||||
@@ -2176,16 +2192,6 @@ class ExtensionCatalog:
|
||||
except IOError as e:
|
||||
raise ExtensionError(f"Failed to read extension archive from {download_url}: {e}")
|
||||
|
||||
# Re-validate scheme after any redirect to guard against scheme-downgrade.
|
||||
_final_parsed = urlparse(final_url)
|
||||
_final_is_localhost = _final_parsed.hostname in ("localhost", "127.0.0.1", "::1")
|
||||
if _final_parsed.scheme != "https" and not (
|
||||
_final_parsed.scheme == "http" and _final_is_localhost
|
||||
):
|
||||
raise ExtensionError(
|
||||
f"Extension download URL was redirected to a non-HTTPS URL: {final_url}"
|
||||
)
|
||||
|
||||
# Choose file extension based on detected format.
|
||||
if not archive_fmt:
|
||||
raise ExtensionError(
|
||||
|
||||
@@ -2321,6 +2321,22 @@ class PresetCatalog:
|
||||
try:
|
||||
with self._open_url(download_url, timeout=60) as response:
|
||||
final_url = response.geturl()
|
||||
# Re-validate scheme after any redirect to guard against
|
||||
# scheme-downgrade. Validate BEFORE reading the body so a
|
||||
# malicious redirect cannot cause us to fetch the payload
|
||||
# over an insecure scheme.
|
||||
_final_parsed = urlparse(final_url)
|
||||
_final_is_localhost = _final_parsed.hostname in (
|
||||
"localhost",
|
||||
"127.0.0.1",
|
||||
"::1",
|
||||
)
|
||||
if _final_parsed.scheme != "https" and not (
|
||||
_final_parsed.scheme == "http" and _final_is_localhost
|
||||
):
|
||||
raise PresetError(
|
||||
f"Preset download URL was redirected to a non-HTTPS URL: {final_url}"
|
||||
)
|
||||
content_type = response.headers.get("Content-Type", "")
|
||||
archive_fmt = detect_archive_format(final_url, content_type)
|
||||
if not archive_fmt:
|
||||
@@ -2334,16 +2350,6 @@ class PresetCatalog:
|
||||
except IOError as e:
|
||||
raise PresetError(f"Failed to read preset archive from {download_url}: {e}")
|
||||
|
||||
# Re-validate scheme after any redirect to guard against scheme-downgrade.
|
||||
_final_parsed = urlparse(final_url)
|
||||
_final_is_localhost = _final_parsed.hostname in ("localhost", "127.0.0.1", "::1")
|
||||
if _final_parsed.scheme != "https" and not (
|
||||
_final_parsed.scheme == "http" and _final_is_localhost
|
||||
):
|
||||
raise PresetError(
|
||||
f"Preset download URL was redirected to a non-HTTPS URL: {final_url}"
|
||||
)
|
||||
|
||||
# Choose file extension based on detected format.
|
||||
if not archive_fmt:
|
||||
raise PresetError(
|
||||
|
||||
Reference in New Issue
Block a user