Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 90 additions & 6 deletions src/github.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,15 +369,28 @@ impl fmt::Display for TokenSource {
}

/// Normalize a URL hostname to the canonical host used for token lookups.
/// Maps "api.github.com" and "*.githubusercontent.com" to "github.com".
/// Maps "api.github.com" and supported "*.githubusercontent.com" hosts to "github.com".
fn canonical_host(host: Option<&str>) -> Option<&str> {
match host {
Some("api.github.com") => Some("github.com"),
Some(h) if h.ends_with(".githubusercontent.com") => Some("github.com"),
Some(h) if is_githubusercontent_auth_host(h) => Some("github.com"),
other => other,
}
}

pub fn is_githubusercontent_auth_host(host: &str) -> bool {
host.ends_with(".githubusercontent.com") && !is_github_release_asset_host(host)
}

fn is_github_release_asset_host(host: &str) -> bool {
matches!(
host,
"objects.githubusercontent.com"
| "objects-origin.githubusercontent.com"
| "release-assets.githubusercontent.com"
)
}

/// Resolve the GitHub token for the given hostname, returning the token and its source.
///
/// Priority:
Expand All @@ -390,10 +403,13 @@ fn canonical_host(host: Option<&str>) -> Option<&str> {
pub fn resolve_token(host: &str) -> Option<(String, TokenSource)> {
let settings = Settings::get();

let is_ghcom = host == "github.com"
|| host == "api.github.com"
|| host.ends_with(".githubusercontent.com");
let lookup_host = if host == "api.github.com" || host.ends_with(".githubusercontent.com") {
if is_github_release_asset_host(host) {
return None;
}

let is_ghcom =
host == "github.com" || host == "api.github.com" || is_githubusercontent_auth_host(host);
let lookup_host = if host == "api.github.com" || is_githubusercontent_auth_host(host) {
"github.com"
} else {
host
Expand Down Expand Up @@ -568,6 +584,39 @@ struct GhHostEntry {
mod tests {
use super::*;

static TEST_ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());

fn with_github_token<F, R>(test_fn: F) -> R
where
F: FnOnce() -> R,
{
let _guard = TEST_ENV_LOCK.lock().unwrap();
let orig_mise = std::env::var("MISE_GITHUB_TOKEN").ok();
let orig_api = std::env::var("GITHUB_API_TOKEN").ok();
let orig_gh = std::env::var("GITHUB_TOKEN").ok();

env::remove_var("MISE_GITHUB_TOKEN");
env::remove_var("GITHUB_API_TOKEN");
env::set_var("GITHUB_TOKEN", "ghp_test");

let result = test_fn();

match orig_mise {
Some(v) => env::set_var("MISE_GITHUB_TOKEN", v),
None => env::remove_var("MISE_GITHUB_TOKEN"),
}
match orig_api {
Some(v) => env::set_var("GITHUB_API_TOKEN", v),
None => env::remove_var("GITHUB_API_TOKEN"),
}
match orig_gh {
Some(v) => env::set_var("GITHUB_TOKEN", v),
None => env::remove_var("GITHUB_TOKEN"),
}

result
}
Comment thread
risu729 marked this conversation as resolved.

#[test]
fn test_parse_github_tokens() {
let toml = r#"
Expand Down Expand Up @@ -604,6 +653,41 @@ something_else = "value"
assert!(result.is_empty());
}

#[test]
fn test_githubusercontent_auth_hosts_exclude_release_assets() {
assert!(is_githubusercontent_auth_host("raw.githubusercontent.com"));
assert!(!is_githubusercontent_auth_host(
"objects.githubusercontent.com"
));
assert!(!is_githubusercontent_auth_host(
"objects-origin.githubusercontent.com"
));
assert!(!is_githubusercontent_auth_host(
"release-assets.githubusercontent.com"
));
}

#[test]
fn test_release_asset_hosts_do_not_use_github_token() {
with_github_token(|| {
for host in [
"objects.githubusercontent.com",
"objects-origin.githubusercontent.com",
"release-assets.githubusercontent.com",
] {
let headers =
get_headers(format!("https://{host}/github-production-release-asset"));
assert!(
!headers.contains_key(reqwest::header::AUTHORIZATION),
"{host} should not use GitHub auth"
);
}

let headers = get_headers("https://raw.githubusercontent.com/owner/repo/main/file.txt");
assert!(headers.contains_key(reqwest::header::AUTHORIZATION));
});
}

fn make_release(tag: &str) -> GithubRelease {
GithubRelease {
tag_name: tag.to_string(),
Expand Down
2 changes: 1 addition & 1 deletion src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ fn host_auth_headers(url: &Url) -> HeaderMap {

let is_github = host == "api.github.com"
|| host == "github.com"
|| host.ends_with(".githubusercontent.com")
|| crate::github::is_githubusercontent_auth_host(host)
|| crate::github::is_gh_host(host);
if is_github {
return crate::github::get_headers(url.as_str());
Expand Down
Loading