fix: handle tags containing / in GitHub release asset URL resolution (#3767)

The tag extraction in resolve_github_release_asset_api_url split the
URL path on / and assumed the tag was a single segment at index 4.
Tags containing literal / (e.g. feature/v1) would be split across
multiple segments, causing the tag to be truncated to only the first
part and the asset name to include leftover tag segments.

Fix by reconstructing the tag as all segments between 'download' and
the final asset segment: tag = '/'.join(parts[4:-1]), asset = parts[-1].
This commit is contained in:
Quratulain-bilal
2026-07-29 01:45:38 +05:00
committed by GitHub
parent 98b2551ade
commit 56b1839fba
2 changed files with 28 additions and 2 deletions

View File

@@ -159,8 +159,9 @@ def resolve_github_release_asset_api_url(
if len(parts) < 6 or parts[2:4] != ["releases", "download"]:
return None
owner, repo, tag = parts[0], parts[1], parts[4]
asset_name = "/".join(parts[5:])
owner, repo = parts[0], parts[1]
tag = "/".join(parts[4:-1])
asset_name = parts[-1]
encoded_tag = quote(tag, safe="")
release_url = f"{api_base}/repos/{owner}/{repo}/releases/tags/{encoded_tag}"

View File

@@ -379,6 +379,31 @@ class TestResolveGitHubReleaseAssetApiUrl:
assert result == "https://api.github.com/repos/org/repo/releases/assets/99"
assert captured == ["https://api.github.com/repos/org/repo/releases/tags/v1.0"]
def test_tag_with_literal_slash_in_path(self):
"""A tag containing a literal '/' (e.g. feature/v1.0.0) splits across
multiple URL path segments. The implementation must join all segments
between 'download/' and the asset name to reconstruct the full tag."""
captured_urls = []
asset_url = "https://api.github.com/repos/org/repo/releases/assets/77"
@contextmanager
def capturing_open(url, timeout=None, extra_headers=None):
captured_urls.append(url)
resp = MagicMock()
resp.read.side_effect = io.BytesIO(json.dumps({
"assets": [{"name": "asset.zip", "url": asset_url}]
}).encode()).read
yield resp
result = resolve_github_release_asset_api_url(
"https://github.com/org/repo/releases/download/feature/v1.0.0/asset.zip",
capturing_open,
)
assert result == asset_url
# Tag must be the full "feature/v1.0.0", not just "v1.0.0"
assert len(captured_urls) == 1
assert "releases/tags/feature%2Fv1.0.0" in captured_urls[0]
class TestGitHubRedirectAuth:
"""Tests for GitHub-owned redirect auth handling."""