pub(super) fn validate_addon(
addon: &crate::core::context_package::content::AddonContent,
errors: &mut Vec<String>,
) {
use crate::core::context_package::content::{
MAX_ADDON_MODULE_BYTES, MAX_ADDON_MODULES, MAX_ADDON_TOTAL_BYTES,
};
if addon.manifest_toml.trim().is_empty() {
errors.push("kind=addon payload has an empty manifest_toml".into());
return;
}
let manifest = match crate::core::context_package::addon_manifest::parse(&addon.manifest_toml) {
Ok(m) => m,
Err(e) => {
errors.push(e);
return;
}
};
if addon.modules.is_empty() && manifest.mcp.is_none() {
errors.push(
"kind=addon payload declares neither a WASM module nor an [mcp] server — \
installing it would have no effect"
.into(),
);
return;
}
if addon.modules.len() > MAX_ADDON_MODULES {
errors.push(format!(
"addon payload has {} modules (cap: {MAX_ADDON_MODULES})",
addon.modules.len()
));
return;
}
let mut seen = std::collections::HashSet::new();
let mut total: usize = 0;
for blob in &addon.modules {
if let Err(e) = super::validate_document_path(&blob.path) {
errors.push(e);
continue;
}
if !std::path::Path::new(&blob.path)
.extension()
.is_some_and(|e| e.eq_ignore_ascii_case("wasm"))
{
errors.push(format!(
"addon module `{}` must be a `.wasm` file",
blob.path
));
continue;
}
if !seen.insert(blob.path.as_str()) {
errors.push(format!("duplicate addon module `{}`", blob.path));
continue;
}
if blob.sha256.len() != 64 || !blob.sha256.chars().all(|c| c.is_ascii_hexdigit()) {
errors.push(format!(
"`{}`: sha256 must be a 64-char hex string",
blob.path
));
continue;
}
match blob.decode_verified() {
Ok(plain) => {
if plain.len() > MAX_ADDON_MODULE_BYTES {
errors.push(format!(
"addon module `{}` is {} bytes (cap: {MAX_ADDON_MODULE_BYTES})",
blob.path,
plain.len()
));
}
if !plain.starts_with(b"\0asm") {
errors.push(format!(
"addon module `{}` is not a WebAssembly module (bad magic)",
blob.path
));
}
total += plain.len();
}
Err(e) => errors.push(e),
}
}
if total > MAX_ADDON_TOTAL_BYTES {
errors.push(format!(
"addon payload decodes to {total} bytes (cap: {MAX_ADDON_TOTAL_BYTES})"
));
}
}