use mockforge_plugin_core::*;
use mockforge_plugin_loader::*;
use std::fs;
use tempfile::TempDir;
#[cfg(test)]
mod tests {
use super::*;
fn create_malicious_wasm() -> Vec<u8> {
vec![
0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, ]
}
fn create_plugin_with_excessive_permissions() -> PluginManifest {
let id = PluginId::new("excessive-plugin");
let version = PluginVersion::new(1, 0, 0);
let author = PluginAuthor::new("Malicious Author");
let info = PluginInfo::new(
id,
version,
"Excessive Permissions Plugin",
"Plugin requesting excessive permissions",
author,
);
let mut manifest = PluginManifest::new(info);
manifest.capabilities = vec![
"network:http".to_string(),
"filesystem:read".to_string(),
"filesystem:write".to_string(),
];
manifest
}
#[tokio::test]
async fn test_malicious_wasm_detection() {
let temp_dir = TempDir::new().unwrap();
let manifest_path = temp_dir.path().join("plugin.yaml");
let wasm_path = temp_dir.path().join("plugin.wasm");
let manifest = create_test_plugin_manifest();
let yaml_content = serde_yaml::to_string(&manifest).unwrap();
fs::write(&manifest_path, yaml_content).unwrap();
let malicious_wasm = create_malicious_wasm();
fs::write(&wasm_path, malicious_wasm).unwrap();
let config = PluginLoaderConfig::default();
let plugin_loader = PluginLoader::new(config);
let result = plugin_loader.validate_plugin(temp_dir.path()).await;
let _ = result; }
#[tokio::test]
async fn test_excessive_permission_requests() {
let temp_dir = TempDir::new().unwrap();
let manifest_path = temp_dir.path().join("plugin.yaml");
let wasm_path = temp_dir.path().join("plugin.wasm");
let manifest = create_plugin_with_excessive_permissions();
let yaml_content = serde_yaml::to_string(&manifest).unwrap();
fs::write(&manifest_path, yaml_content).unwrap();
let wasm_bytes = vec![
0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, ];
fs::write(&wasm_path, wasm_bytes).unwrap();
let config = PluginLoaderConfig {
skip_wasm_validation: true,
..Default::default()
};
let plugin_loader = PluginLoader::new(config);
let result = plugin_loader.validate_plugin(temp_dir.path()).await;
assert!(
result.is_ok(),
"Manifest validation should pass even with excessive permissions"
);
let validated_manifest = result.unwrap();
assert!(validated_manifest.capabilities.contains(&"network:http".to_string()));
assert!(validated_manifest.capabilities.contains(&"filesystem:read".to_string()));
assert!(validated_manifest.capabilities.contains(&"filesystem:write".to_string()));
}
#[tokio::test]
async fn test_network_access_restriction() {
let config = PluginLoaderConfig::default();
let validator = PluginValidator::new(config);
let network_capabilities = vec!["network:http".to_string(), "template".to_string()];
let result = validator.validate_capabilities(&network_capabilities);
assert!(result.is_ok(), "Network capabilities should pass validation");
let parsed = PluginCapabilities::from_strings(&network_capabilities);
assert!(parsed.network.allow_http, "Network HTTP permission should be enabled");
assert!(
parsed.custom.contains_key("template"),
"Template capability should be in custom"
);
}
#[tokio::test]
async fn test_filesystem_access_restriction() {
let config = PluginLoaderConfig::default();
let validator = PluginValidator::new(config);
let filesystem_capabilities = vec![
"filesystem:read".to_string(),
"filesystem:write".to_string(),
"template".to_string(),
];
let result = validator.validate_capabilities(&filesystem_capabilities);
assert!(result.is_ok(), "Filesystem capabilities should pass validation");
let parsed = PluginCapabilities::from_strings(&filesystem_capabilities);
assert!(
parsed.filesystem.read_paths.contains(&"*".to_string()),
"Filesystem read permission should be enabled"
);
assert!(
parsed.filesystem.write_paths.contains(&"*".to_string()),
"Filesystem write permission should be enabled"
);
assert!(
parsed.custom.contains_key("template"),
"Template capability should be in custom"
);
}
#[tokio::test]
async fn test_resource_limit_enforcement() {
let config = PluginLoaderConfig::default();
let validator = PluginValidator::new(config);
let resource_capabilities = vec![
"resource:memory=100MB".to_string(),
"resource:cpu=5s".to_string(),
"template".to_string(),
];
let result = validator.validate_capabilities(&resource_capabilities);
assert!(result.is_ok(), "Resource limit capabilities should pass validation");
let parsed = PluginCapabilities::from_strings(&resource_capabilities);
assert!(
parsed.custom.contains_key("resource:memory=100MB"),
"Memory resource limit should be in custom"
);
assert!(
parsed.custom.contains_key("resource:cpu=5s"),
"CPU resource limit should be in custom"
);
assert!(
parsed.custom.contains_key("template"),
"Template capability should be in custom"
);
}
#[tokio::test]
async fn test_plugin_isolation() {
let config = PluginLoaderConfig::default();
let plugin_loader = PluginLoader::new(config);
let plugin1_id = PluginId::new("plugin1".to_string());
let plugin2_id = PluginId::new("plugin2".to_string());
let plugin1 = plugin_loader.get_plugin(&plugin1_id).await;
let plugin2 = plugin_loader.get_plugin(&plugin2_id).await;
assert!(plugin1.is_none()); assert!(plugin2.is_none()); }
#[tokio::test]
async fn test_input_validation() {
let valid_id = PluginId::new("valid-plugin-id".to_string());
assert_eq!(valid_id.0, "valid-plugin-id");
let valid_id2 = PluginId::new("plugin_with_underscores".to_string());
assert_eq!(valid_id2.0, "plugin_with_underscores");
let dangerous_id = PluginId::new("../../etc/passwd".to_string());
assert_eq!(dangerous_id.0, "../../etc/passwd"); }
#[tokio::test]
async fn test_dependency_security() {
let dependency_id = PluginId::new("safe-dependency");
let dependency_version = PluginVersion::new(1, 0, 0);
assert_eq!(dependency_id.as_str(), "safe-dependency");
assert_eq!(dependency_version.to_string(), "1.0.0");
let malicious_dep_id = PluginId::new("../../../malicious");
let malicious_dep_version = PluginVersion::new(0, 0, 0);
assert_eq!(malicious_dep_id.as_str(), "../../../malicious");
assert_eq!(malicious_dep_version.to_string(), "0.0.0");
}
#[tokio::test]
async fn test_wasm_module_validation() {
let temp_dir = TempDir::new().unwrap();
let manifest_path = temp_dir.path().join("plugin.yaml");
let wasm_path = temp_dir.path().join("plugin.wasm");
let manifest = create_test_plugin_manifest();
let yaml_content = serde_yaml::to_string(&manifest).unwrap();
fs::write(&manifest_path, yaml_content).unwrap();
let valid_wasm = vec![
0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, ];
fs::write(&wasm_path, valid_wasm).unwrap();
let config = PluginLoaderConfig::default();
let plugin_loader = PluginLoader::new(config);
let result = plugin_loader.validate_plugin(temp_dir.path()).await;
let _ = result;
let invalid_wasm = vec![0x00, 0x61, 0x73]; fs::write(&wasm_path, invalid_wasm).unwrap();
let result = plugin_loader.validate_plugin(temp_dir.path()).await;
let _ = result; }
#[tokio::test]
async fn test_capability_enforcement() {
let mut restricted_plugin = create_test_plugin_manifest();
restricted_plugin.capabilities = vec!["template".to_string()];
let mut permissive_plugin = create_test_plugin_manifest();
permissive_plugin.capabilities = vec![
"template".to_string(),
"network:http".to_string(),
"filesystem:read".to_string(),
"filesystem:write".to_string(),
];
assert!(restricted_plugin.capabilities.contains(&"template".to_string()));
assert!(!restricted_plugin.capabilities.contains(&"network:http".to_string()));
assert!(!restricted_plugin.capabilities.contains(&"filesystem:read".to_string()));
assert!(permissive_plugin.capabilities.contains(&"template".to_string()));
assert!(permissive_plugin.capabilities.contains(&"network:http".to_string()));
assert!(permissive_plugin.capabilities.contains(&"filesystem:read".to_string()));
assert!(permissive_plugin.capabilities.contains(&"filesystem:write".to_string()));
let config = PluginLoaderConfig::default();
let validator = PluginValidator::new(config);
let restricted_result = validator.validate_capabilities(&restricted_plugin.capabilities);
assert!(restricted_result.is_ok(), "Restricted capabilities should pass validation");
let permissive_result = validator.validate_capabilities(&permissive_plugin.capabilities);
assert!(permissive_result.is_ok(), "Permissive capabilities should pass validation");
}
#[tokio::test]
async fn test_plugin_execution_isolation() {
let config = PluginLoaderConfig::default();
let _loader = PluginLoader::new(config);
let plugin_id1 = PluginId::new("test-plugin-1");
let version1 = PluginVersion::new(1, 0, 0);
let context1 = PluginContext::new(plugin_id1, version1)
.with_custom("method", serde_json::json!("GET"))
.with_custom("path", serde_json::json!("/api/test1"));
let plugin_id2 = PluginId::new("test-plugin-2");
let version2 = PluginVersion::new(2, 0, 0);
let context2 = PluginContext::new(plugin_id2, version2)
.with_custom("method", serde_json::json!("POST"))
.with_custom("path", serde_json::json!("/api/test2"))
.with_custom("body", serde_json::json!({"data": "test"}));
assert_ne!(context1.plugin_id, context2.plugin_id);
assert_ne!(context1.version, context2.version);
assert_ne!(context1.custom.get("method"), context2.custom.get("method"));
assert_ne!(context1.custom.get("path"), context2.custom.get("path"));
}
fn create_test_plugin_manifest() -> PluginManifest {
let id = PluginId::new("test-plugin");
let version = PluginVersion::new(1, 0, 0);
let author = PluginAuthor::with_email("Test Author", "test@example.com");
let info = PluginInfo::new(id, version, "Test Plugin", "A test plugin", author);
PluginManifest::new(info).with_capability("template")
}
}