diff --git a/crates/squabble-cli/src/zero_job.rs b/crates/squabble-cli/src/zero_job.rs index a36ea3b..a1e568e 100644 --- a/crates/squabble-cli/src/zero_job.rs +++ b/crates/squabble-cli/src/zero_job.rs @@ -352,8 +352,9 @@ impl GhRuns { let jobs_path = format!("repos/{slug}/actions/runs/{}/jobs?per_page=1", run.id); if let Ok(jobs) = transport.get(&jobs_path) { if (200..300).contains(&jobs.status) { - // Absence of a count is recorded as `None`, not as zero. - run.jobs = parse_job_count(&jobs.body).ok(); + let count = parse_job_count(&jobs.body) + .map_err(|err| format!("invalid job evidence for `{jobs_path}`: {err}"))?; + run.jobs = Some(count); } } } diff --git a/crates/squabble-fight/src/startup.rs b/crates/squabble-fight/src/startup.rs index 91d899b..9311fcc 100644 --- a/crates/squabble-fight/src/startup.rs +++ b/crates/squabble-fight/src/startup.rs @@ -227,13 +227,21 @@ pub fn parse_startup_runs(body: &str) -> Result, String> { /// # Errors /// /// Returns an error for invalid JSON or when `total_count` is absent or is not -/// an unsigned integer. Zero is a valid count. +/// an unsigned integer, or when a present `jobs` field is not an array or has +/// more entries than `total_count`. Zero is a valid count. pub fn parse_job_count(body: &str) -> Result { let v: serde_json::Value = serde_json::from_str(body) .map_err(|e| format!("could not parse actions/jobs response: {e}"))?; - v.get("total_count") + let count = v.get("total_count") .and_then(serde_json::Value::as_u64) - .ok_or_else(|| "actions/jobs response has no `total_count`".to_string()) + .ok_or_else(|| "actions/jobs response has no `total_count`".to_string())?; + if let Some(jobs) = v.get("jobs") { + let jobs = jobs.as_array().ok_or("actions/jobs response has invalid `jobs`")?; + if jobs.len() as u64 > count { + return Err("actions/jobs response lists more jobs than its total_count".to_string()); + } + } + Ok(count) } #[cfg(test)] @@ -510,6 +518,7 @@ jobs: parse_job_count("{\"total_count\":0,\"jobs\":[]}").unwrap(), 0 ); + assert!(parse_job_count(r#"{"total_count":0,"jobs":[{"id":1}]}"#).is_err()); assert!(parse_job_count("{}").is_err()); assert!(parse_job_count("[]").is_err()); }