diff --git a/src/github.rs b/src/github.rs index 2ae8adf..94015ba 100644 --- a/src/github.rs +++ b/src/github.rs @@ -154,6 +154,22 @@ fn github_get(client: &reqwest::Client, url: Url, token: Option<&str>) -> reqwes if let Some(token) = token { req.bearer_auth(token) } else { req } } +async fn ensure_github_success(resp: reqwest::Response, action: &str) -> Result { + if resp.status().is_success() { + return Ok(resp); + } + + let status = resp.status(); + let url = resp.url().clone(); + warn_on_rate_limit("GitHub", status, action); + + let mut body = resp.text().await.unwrap_or_default(); + if body.len() > 512 { + body.truncate(512); + } + anyhow::bail!("GitHub API request failed while {action}: HTTP {status} ({url}): {body}"); +} + async fn fetch_github_orgs( client: &reqwest::Client, api_base: &Url, @@ -165,11 +181,11 @@ async fn fetch_github_orgs( loop { let mut url = api_base.join("organizations").context("Failed to build GitHub orgs URL")?; url.query_pairs_mut().append_pair("per_page", "100").append_pair("page", &page.to_string()); - let resp = github_get(client, url, token).send().await?; - if !resp.status().is_success() { - warn_on_rate_limit("GitHub", resp.status(), "listing organizations"); - break; - } + let resp = ensure_github_success( + github_get(client, url, token).send().await?, + "listing organizations", + ) + .await?; let page_orgs: Vec = resp.json().await?; if page_orgs.is_empty() { break; @@ -200,11 +216,8 @@ async fn fetch_github_repos( .append_pair("type", repo_type) .append_pair("sort", "created") .append_pair("direction", "desc"); - let resp = github_get(client, url, token).send().await?; - if !resp.status().is_success() { - warn_on_rate_limit("GitHub", resp.status(), action); - break; - } + let resp = + ensure_github_success(github_get(client, url, token).send().await?, action).await?; let page_repos: Vec = resp.json().await?; if page_repos.is_empty() { break; @@ -239,11 +252,11 @@ pub async fn enumerate_contributor_repo_urls( .join(&format!("repos/{owner}/{repo}/contributors")) .context("Failed to build GitHub contributors URL")?; url.query_pairs_mut().append_pair("per_page", "100").append_pair("page", &page.to_string()); - let resp = github_get(&client, url, token.as_deref()).send().await?; - if !resp.status().is_success() { - warn_on_rate_limit("GitHub", resp.status(), "listing contributors"); - break; - } + let resp = ensure_github_success( + github_get(&client, url, token.as_deref()).send().await?, + "listing contributors", + ) + .await?; let contributors: Vec = resp.json().await?; if contributors.is_empty() { break; @@ -296,11 +309,11 @@ pub async fn enumerate_contributor_repo_urls( .append_pair("type", "all") .append_pair("sort", "updated") .append_pair("direction", "desc"); - let resp = github_get(&client, url, token.as_deref()).send().await?; - if !resp.status().is_success() { - warn_on_rate_limit("GitHub", resp.status(), "listing user repositories"); - break; - } + let resp = ensure_github_success( + github_get(&client, url, token.as_deref()).send().await?, + "listing user repositories", + ) + .await?; let repos: Vec = resp.json().await?; if repos.is_empty() { break; diff --git a/src/scanner/validation.rs b/src/scanner/validation.rs index dc2ecaf..c52ad8c 100644 --- a/src/scanner/validation.rs +++ b/src/scanner/validation.rs @@ -859,17 +859,7 @@ async fn validate_single( validation_retries: u32, max_body_len: usize, ) { - // Build key - let dep_vars_str = dep_vars - .get(om.rule.id()) - .map(|hm| { - let mut sorted: Vec<_> = hm.iter().collect(); - sorted.sort_by(|(k, _), (k2, _)| k.cmp(k2)); - sorted.into_iter().map(|(k, v)| format!("{}={}", k, v)).collect::>().join("|") - }) - .unwrap_or_default(); - let capture0 = om.captures.captures.get(0).map_or(String::new(), |c| c.raw_value().to_string()); - let cache_key = format!("{}|{}|{}", om.rule.name(), capture0, dep_vars_str); + let cache_key = build_cache_key(om, dep_vars); // Check cache first if let Some(cached) = cache.get(&cache_key) { om.validation_success = cached.is_valid;