scripts: harden github_blob_to_raw against substring URL spoofing

CodeQL flagged scripts/notebook_to_python.py:33's
`if "github.com" in url and "/blob/" in url` as
py/incomplete-url-substring-sanitization: "github.com" can sit
anywhere in the URL, so an attacker-controlled URL like
https://attacker.example.com/github.com/blob/x would be rewritten
to a raw.githubusercontent.com URL and fetched as if it were a
real GitHub blob.

Switch to urllib.parse.urlparse and require parsed.netloc ==
"github.com" exactly, then rewrite via a proper urlunparse on the
parsed components (path is replaced with first /blob/ -> / only).
Query strings and fragments now round-trip correctly too, which
was an incidental bug in the old string-replace path.

Closes the high-severity CodeQL alert on PR head 08235625.
This commit is contained in:
Daniel Han 2026-05-08 02:43:47 +00:00
commit fa3840cf6d

View file

@ -29,11 +29,19 @@ def needs_fstring(cmd: str) -> bool:
def github_blob_to_raw(url: str) -> str:
"""Convert GitHub blob URL to raw URL."""
# https://github.com/user/repo/blob/branch/path -> https://raw.githubusercontent.com/user/repo/branch/path
if "github.com" in url and "/blob/" in url:
url = url.replace("github.com", "raw.githubusercontent.com")
url = url.replace("/blob/", "/")
return url
# https://github.com/user/repo/blob/branch/path
# -> https://raw.githubusercontent.com/user/repo/branch/path
# Compare the parsed host exactly (not as a substring) so a URL
# like https://attacker.example.com/github.com/blob/... does NOT
# get rewritten to a github raw URL. Closes CodeQL alert
# py/incomplete-url-substring-sanitization.
parsed = urllib.parse.urlparse(url)
if parsed.netloc != "github.com" or "/blob/" not in parsed.path:
return url
new_path = parsed.path.replace("/blob/", "/", 1)
return urllib.parse.urlunparse(
parsed._replace(netloc = "raw.githubusercontent.com", path = new_path)
)
def download_notebook(url: str) -> tuple[str, str]: