zoi_resolver/
mini_resolve.rs1use std::collections::HashMap;
7
8use anyhow::{Result, anyhow};
9use colored::Colorize;
10use serde::{Deserialize, Serialize};
11pub use zoi_core::types::MiniVulnerability;
12
13fn default_revision() -> String {
15 "1".to_string()
16}
17
18#[derive(Debug, Serialize, Deserialize, Clone)]
24pub struct MiniPackageIndex {
25 pub repo: String,
27 pub repo_type: String,
29 pub version: String,
31 #[serde(default = "default_revision")]
33 pub revision: String,
34 pub description: String,
36 #[serde(default, deserialize_with = "deserialize_sub_packages")]
38 pub sub_packages: Option<Vec<String>>,
39 pub vuln: Option<Vec<MiniVulnerability>>
41}
42
43fn deserialize_sub_packages<'de, D>(
46 deserializer: D
47) -> Result<Option<Vec<String>>, D::Error>
48where
49 D: serde::Deserializer<'de>
50{
51 let v: serde_json::Value = serde::Deserialize::deserialize(deserializer)?;
52 if v.is_array() {
53 Ok(serde_json::from_value(v).ok())
54 } else {
55 Ok(None)
56 }
57}
58
59#[derive(Debug, Serialize, Deserialize, Clone)]
61pub struct MiniRegistryIndex {
62 pub packages: HashMap<String, MiniPackageIndex>
64}
65
66fn set_registry_info() -> Result<(String, String)> {
73 let reg = zoi_core::builtin::registry::get_set()?.ok_or_else(|| {
74 anyhow!(
75 "No set registry is defined in the built-in registries. Please \
76 reinstall Zoi."
77 )
78 })?;
79 Ok((reg.git, reg.branch))
80}
81
82pub fn fetch_registry_index() -> Result<MiniRegistryIndex> {
91 let (git, branch) = set_registry_info()?;
92 let url = zoi_purl::construct_raw_url(&git, &branch, "packages.json")?;
93 let client = zoi_core::utils::get_http_client()?;
94 let response = client.get(url).send()?;
95 if !response.status().is_success() {
96 return Err(anyhow!(
97 "Failed to fetch packages.json from registry: {}",
98 response.status()
99 ));
100 }
101 let index: MiniRegistryIndex = response.json()?;
102 Ok(index)
103}
104
105pub fn fetch_registry_config() -> Result<zoi_core::types::RepoConfig> {
111 let (git, branch) = set_registry_info()?;
112 let url = zoi_purl::construct_raw_url(&git, &branch, "repo.yaml")?;
113 let client = zoi_core::utils::get_http_client()?;
114 let response = client.get(url).send()?;
115 if !response.status().is_success() {
116 return Err(anyhow!(
117 "Failed to fetch repo.yaml from registry: {}",
118 response.status()
119 ));
120 }
121 let content = response.text()?;
122 let config: zoi_core::types::RepoConfig = serde_yaml::from_str(&content)?;
123 Ok(config)
124}
125
126pub fn get_package_lua_url(repo: &str, name: &str) -> String {
129 let file_path = if repo.is_empty() {
130 format!("{name}/{name}.pkg.lua")
131 } else {
132 format!("{repo}/{name}/{name}.pkg.lua")
133 };
134 let (git, branch) = set_registry_info().unwrap_or_else(|_| {
135 (
136 "https://gitlab.com/zillowe/zillwen/zusty/zoidberg".to_string(),
137 "main".to_string()
138 )
139 });
140 zoi_purl::construct_raw_url(&git, &branch, &file_path).unwrap_or(file_path)
141}
142
143pub fn check_vulnerabilities(
153 pkg_name: &str,
154 pkg_index: &MiniPackageIndex,
155 version: &str
156) -> Result<bool> {
157 let Some(vulns) = &pkg_index.vuln else {
158 return Ok(true);
159 };
160
161 let target_version =
162 semver::Version::parse(version.trim_start_matches('v'))
163 .map_err(|e| anyhow!("Failed to parse version {version}: {e}"))?;
164
165 let mut affected = Vec::new();
166
167 for vuln in vulns {
168 if let Ok(req) = semver::VersionReq::parse(&vuln.affected_range)
169 && req.matches(&target_version)
170 {
171 affected.push(vuln);
172 }
173 }
174
175 if affected.is_empty() {
176 return Ok(true);
177 }
178
179 println!("\n{}", "SECURITY WARNING".red().bold());
180 for vuln in affected {
181 println!(
182 "Package {} v{} is known to be vulnerable:",
183 pkg_name.cyan().bold(),
184 version.red()
185 );
186 println!(
187 "[{}] {} (Severity: {})",
188 vuln.id.dimmed(),
189 vuln.summary,
190 vuln.severity.to_uppercase()
191 );
192 if let Some(fixed) = &vuln.fixed_in {
193 println!("Fixed in version: {}", fixed.green());
194 }
195 println!();
196 }
197
198 Ok(zoi_core::utils::ask_for_confirmation(
199 "Do you want to continue with the installation anyway?",
200 false
201 ))
202}