Rust PoC (cargo, packages.d/*.toml config) that checks astral-sh/uv's GitHub Atom feed for a new release, fetches the matching asset via the GitHub API, and verifies it. Confirmed live against the real repo: uv actually ships GitHub build-provenance attestations (sigstore bundle) on every release, so it's a tier-2 package, not the tier-4 same-origin-sha256 guessed in the original spec draft. Verified via `gh attestation verify` rather than reimplementing sigstore in Rust. State persists across runs so a second run correctly reports "up to date." Also folds the finding back into SPEC.md: updates the uv example to tier 2, derives tier from verification method instead of storing both (avoids a tier/method mismatch that would mean nothing), marks the packages.d/ layout question resolved, and updates Architecture/Status to say what's actually implemented vs. still sketch (build/publish/review queue, same-origin-sha256 against a live repo, scheduling, non-GitHub sources, minisign are all still open). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A2FEut5tVMNjeVjqhgVZbr
93 lines
2.9 KiB
Rust
93 lines
2.9 KiB
Rust
use crate::config::Verification;
|
|
use crate::fetcher;
|
|
use anyhow::{Context, Result};
|
|
use sha2::{Digest, Sha256};
|
|
use std::path::Path;
|
|
use std::process::Command;
|
|
|
|
pub struct VerificationResult {
|
|
pub tier: u8,
|
|
pub passed: bool,
|
|
pub justification: String,
|
|
}
|
|
|
|
/// Runs the verification method declared for a package against a
|
|
/// downloaded artifact. See SPEC.md > Verification trust tiers for what
|
|
/// each tier does and does not prove.
|
|
pub fn verify(
|
|
client: &reqwest::blocking::Client,
|
|
verification: &Verification,
|
|
repo: &str,
|
|
tag: &str,
|
|
artifact_path: &Path,
|
|
dest_dir: &Path,
|
|
) -> Result<VerificationResult> {
|
|
match verification {
|
|
Verification::SameOriginSha256 {
|
|
checksum_asset_pattern,
|
|
} => {
|
|
let checksum_path = fetcher::download_asset(
|
|
client,
|
|
repo,
|
|
tag,
|
|
checksum_asset_pattern,
|
|
dest_dir,
|
|
)?;
|
|
let checksum_text = std::fs::read_to_string(&checksum_path)?;
|
|
let expected = checksum_text
|
|
.split_whitespace()
|
|
.next()
|
|
.context("empty checksum file")?
|
|
.to_lowercase();
|
|
|
|
let data = std::fs::read(artifact_path)?;
|
|
let actual = sha256_hex(&data);
|
|
let passed = actual == expected;
|
|
|
|
Ok(VerificationResult {
|
|
tier: verification.tier(),
|
|
passed,
|
|
justification: if passed {
|
|
"same-origin sha256 matched — proves transport integrity only, \
|
|
not authorship (see tier 4 in SPEC.md)"
|
|
.into()
|
|
} else {
|
|
format!("sha256 mismatch: expected {expected}, got {actual}")
|
|
},
|
|
})
|
|
}
|
|
Verification::GithubAttestation => {
|
|
let output = Command::new("gh")
|
|
.args([
|
|
"attestation",
|
|
"verify",
|
|
&artifact_path.to_string_lossy(),
|
|
"-R",
|
|
repo,
|
|
])
|
|
.output()
|
|
.context("running `gh attestation verify` (is `gh` installed and authenticated?)")?;
|
|
|
|
let passed = output.status.success();
|
|
Ok(VerificationResult {
|
|
tier: verification.tier(),
|
|
passed,
|
|
justification: if passed {
|
|
"GitHub build-provenance attestation verified via `gh attestation verify`"
|
|
.into()
|
|
} else {
|
|
format!(
|
|
"gh attestation verify failed: {}",
|
|
String::from_utf8_lossy(&output.stderr).trim()
|
|
)
|
|
},
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
fn sha256_hex(data: &[u8]) -> String {
|
|
let mut hasher = Sha256::new();
|
|
hasher.update(data);
|
|
hex::encode(hasher.finalize())
|
|
}
|