use crate::error::Result;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct SecurityPolicy {
pub allow_unsafe_code: bool,
pub require_signatures: bool,
pub min_trust_level: u8,
pub max_complexity: u32,
pub dangerous_permissions: Vec<String>,
pub restricted_apis: Vec<String>,
}
impl SecurityPolicy {
pub fn strict() -> Self {
Self {
allow_unsafe_code: false,
require_signatures: true,
min_trust_level: 8,
max_complexity: 10,
dangerous_permissions: vec![
"file_system_write".to_string(),
"file_system_delete".to_string(),
"network_access".to_string(),
"system_commands".to_string(),
"environment_variables".to_string(),
"process_spawn".to_string(),
],
restricted_apis: vec![
"std::process::Command".to_string(),
"std::fs::remove_file".to_string(),
"std::fs::remove_dir".to_string(),
"std::env::set_var".to_string(),
"libc::system".to_string(),
],
}
}
pub fn standard() -> Self {
Self {
allow_unsafe_code: false,
require_signatures: true,
min_trust_level: 5,
max_complexity: 20,
dangerous_permissions: vec![
"file_system_write".to_string(),
"network_access".to_string(),
"system_commands".to_string(),
],
restricted_apis: vec![
"std::process::Command".to_string(),
"std::fs::remove_file".to_string(),
],
}
}
pub fn permissive() -> Self {
Self {
allow_unsafe_code: true,
require_signatures: false,
min_trust_level: 0,
max_complexity: 100,
dangerous_permissions: vec!["system_commands".to_string()],
restricted_apis: vec![],
}
}
pub fn is_dangerous_permission(&self, permission: &Permission) -> bool {
match permission {
Permission::FileSystemWrite => true,
Permission::FileSystemDelete => true,
Permission::NetworkAccess => self
.dangerous_permissions
.contains(&"network_access".to_string()),
Permission::SystemCommands => true,
Permission::ProcessSpawn => true,
Permission::EnvironmentVariables => self
.dangerous_permissions
.contains(&"environment_variables".to_string()),
Permission::Custom(name) => self.dangerous_permissions.contains(name),
_ => false,
}
}
pub fn is_restricted_api(&self, api: &str) -> bool {
self.restricted_apis
.iter()
.any(|restricted| api.contains(restricted) || api.starts_with(restricted))
}
pub fn add_dangerous_permission(&mut self, permission: String) {
if !self.dangerous_permissions.contains(&permission) {
self.dangerous_permissions.push(permission);
}
}
pub fn remove_dangerous_permission(&mut self, permission: &str) {
self.dangerous_permissions.retain(|p| p != permission);
}
pub fn add_restricted_api(&mut self, api_pattern: String) {
if !self.restricted_apis.contains(&api_pattern) {
self.restricted_apis.push(api_pattern);
}
}
pub fn remove_restricted_api(&mut self, api_pattern: &str) {
self.restricted_apis.retain(|p| p != api_pattern);
}
pub fn validate(&self) -> Result<()> {
if self.min_trust_level > 10 {
return Err(crate::error::SklearsError::InvalidOperation(
"Trust level must be between 0 and 10".to_string(),
));
}
if self.max_complexity == 0 {
return Err(crate::error::SklearsError::InvalidOperation(
"Maximum complexity must be greater than 0".to_string(),
));
}
Ok(())
}
}
impl Default for SecurityPolicy {
fn default() -> Self {
Self::standard()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum Permission {
FileSystemRead,
FileSystemWrite,
FileSystemDelete,
NetworkAccess,
SystemCommands,
ProcessSpawn,
GpuAccess,
EnvironmentVariables,
SystemConfiguration,
Custom(String),
}
impl Permission {
pub fn description(&self) -> &'static str {
match self {
Permission::FileSystemRead => "Read access to file system",
Permission::FileSystemWrite => "Write access to file system",
Permission::FileSystemDelete => "Delete access to file system",
Permission::NetworkAccess => "Access to network resources",
Permission::SystemCommands => "Execute system commands",
Permission::ProcessSpawn => "Spawn new processes",
Permission::GpuAccess => "Access to GPU resources",
Permission::EnvironmentVariables => "Access to environment variables",
Permission::SystemConfiguration => "Access to system configuration",
Permission::Custom(_) => "Custom permission",
}
}
pub fn risk_level(&self) -> u8 {
match self {
Permission::FileSystemRead => 2,
Permission::GpuAccess => 2,
Permission::FileSystemWrite => 3,
Permission::NetworkAccess => 3,
Permission::EnvironmentVariables => 3,
Permission::FileSystemDelete => 4,
Permission::SystemConfiguration => 4,
Permission::ProcessSpawn => 5,
Permission::SystemCommands => 5,
Permission::Custom(_) => 3, }
}
pub fn requires_user_consent(&self) -> bool {
self.risk_level() >= 3
}
pub fn all_standard() -> Vec<Permission> {
vec![
Permission::FileSystemRead,
Permission::FileSystemWrite,
Permission::FileSystemDelete,
Permission::NetworkAccess,
Permission::SystemCommands,
Permission::ProcessSpawn,
Permission::GpuAccess,
Permission::EnvironmentVariables,
Permission::SystemConfiguration,
]
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct DigitalSignature {
pub algorithm: String,
pub signature: Vec<u8>,
pub public_key_fingerprint: String,
pub timestamp: std::time::SystemTime,
pub signer_certificate: Option<String>,
}
impl DigitalSignature {
pub fn new(algorithm: String, signature: Vec<u8>, public_key_fingerprint: String) -> Self {
Self {
algorithm,
signature,
public_key_fingerprint,
timestamp: std::time::SystemTime::now(),
signer_certificate: None,
}
}
pub fn is_algorithm_supported(&self) -> bool {
matches!(
self.algorithm.as_str(),
"RSA-SHA256" | "RSA-SHA512" | "ECDSA-P256" | "ECDSA-P384" | "Ed25519"
)
}
pub fn security_strength(&self) -> u32 {
match self.algorithm.as_str() {
"RSA-SHA256" => 112, "RSA-SHA512" => 112, "ECDSA-P256" => 128, "ECDSA-P384" => 192, "Ed25519" => 128, _ => 0, }
}
pub fn is_expired(&self, max_age_seconds: u64) -> bool {
if let Ok(elapsed) = self.timestamp.elapsed() {
elapsed.as_secs() > max_age_seconds
} else {
true }
}
}
#[derive(Debug, Clone)]
pub struct TrustStore {
trusted_keys: HashMap<String, PublicKeyInfo>,
trusted_certificates: HashMap<String, CertificateInfo>,
revoked_keys: Vec<String>,
}
impl TrustStore {
pub fn new() -> Self {
Self {
trusted_keys: HashMap::new(),
trusted_certificates: HashMap::new(),
revoked_keys: Vec::new(),
}
}
pub fn verify_signature(
&self,
_content_hash: &str,
signature: &DigitalSignature,
) -> Result<bool> {
if self
.revoked_keys
.contains(&signature.public_key_fingerprint)
{
return Ok(false);
}
if !signature.is_algorithm_supported() {
return Err(crate::error::SklearsError::InvalidOperation(format!(
"Unsupported signature algorithm: {}",
signature.algorithm
)));
}
if signature.is_expired(365 * 24 * 60 * 60) {
return Ok(false);
}
if let Some(key_info) = self.trusted_keys.get(&signature.public_key_fingerprint) {
if key_info.is_valid() {
Ok(true)
} else {
Ok(false)
}
} else {
Ok(false)
}
}
pub fn get_publisher_trust(&self, publisher: &PublisherInfo) -> u8 {
let mut trust_level = 0u8;
if publisher.verified {
trust_level += 3;
}
trust_level += (publisher.trust_score / 2).min(5);
if self.trusted_certificates.contains_key(&publisher.name) {
trust_level += 2;
}
trust_level.min(10)
}
pub fn add_trusted_key(&mut self, fingerprint: String, key_info: PublicKeyInfo) {
self.trusted_keys.insert(fingerprint, key_info);
}
pub fn remove_trusted_key(&mut self, fingerprint: &str) {
self.trusted_keys.remove(fingerprint);
}
pub fn revoke_key(&mut self, fingerprint: String) {
if !self.revoked_keys.contains(&fingerprint) {
self.revoked_keys.push(fingerprint.clone());
}
self.trusted_keys.remove(&fingerprint);
}
pub fn is_key_revoked(&self, fingerprint: &str) -> bool {
self.revoked_keys.contains(&fingerprint.to_string())
}
pub fn load_from_file(&mut self, _path: &str) -> Result<()> {
Ok(())
}
pub fn save_to_file(&self, _path: &str) -> Result<()> {
Ok(())
}
}
impl Default for TrustStore {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PublisherInfo {
pub name: String,
pub email: String,
pub website: Option<String>,
pub verified: bool,
pub trust_score: u8,
}
impl PublisherInfo {
pub fn new(name: String, email: String) -> Self {
Self {
name,
email,
website: None,
verified: false,
trust_score: 0,
}
}
pub fn is_complete(&self) -> bool {
!self.name.is_empty() && !self.email.is_empty() && self.email.contains('@')
}
}
#[derive(Debug, Clone)]
pub struct PublicKeyInfo {
pub algorithm: String,
pub key_size: u32,
pub added_timestamp: std::time::SystemTime,
pub expires_at: Option<std::time::SystemTime>,
pub owner: String,
}
impl PublicKeyInfo {
pub fn is_valid(&self) -> bool {
if let Some(expires_at) = self.expires_at {
std::time::SystemTime::now() < expires_at
} else {
true }
}
pub fn security_strength(&self) -> u32 {
match self.algorithm.as_str() {
"RSA" => {
if self.key_size >= 2048 {
112
} else if self.key_size >= 1024 {
80
} else {
0 }
}
"ECDSA" => {
if self.key_size >= 256 {
128
} else {
80
}
}
"Ed25519" => 128,
_ => 0,
}
}
}
#[derive(Debug, Clone)]
pub struct CertificateInfo {
pub subject: String,
pub issuer: String,
pub not_before: std::time::SystemTime,
pub not_after: std::time::SystemTime,
pub fingerprint: String,
}
impl CertificateInfo {
pub fn is_valid(&self) -> bool {
let now = std::time::SystemTime::now();
now >= self.not_before && now <= self.not_after
}
}
#[derive(Debug, Clone)]
pub struct PermissionSet {
pub name: String,
pub description: String,
pub permissions: Vec<Permission>,
}
impl PermissionSet {
pub fn new(name: String, description: String, permissions: Vec<Permission>) -> Self {
Self {
name,
description,
permissions,
}
}
pub fn file_system() -> Self {
Self::new(
"file_system".to_string(),
"Read and write access to the file system".to_string(),
vec![Permission::FileSystemRead, Permission::FileSystemWrite],
)
}
pub fn network() -> Self {
Self::new(
"network".to_string(),
"Access to network resources".to_string(),
vec![Permission::NetworkAccess],
)
}
pub fn system_admin() -> Self {
Self::new(
"system_admin".to_string(),
"Administrative access to system resources".to_string(),
vec![
Permission::SystemCommands,
Permission::ProcessSpawn,
Permission::EnvironmentVariables,
Permission::SystemConfiguration,
],
)
}
pub fn max_risk_level(&self) -> u8 {
self.permissions
.iter()
.map(|p| p.risk_level())
.max()
.unwrap_or(0)
}
pub fn requires_user_consent(&self) -> bool {
self.permissions.iter().any(|p| p.requires_user_consent())
}
}
#[allow(non_snake_case)]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_security_policy_levels() {
let strict = SecurityPolicy::strict();
assert!(strict.require_signatures);
assert!(!strict.allow_unsafe_code);
assert_eq!(strict.min_trust_level, 8);
let standard = SecurityPolicy::standard();
assert!(standard.require_signatures);
assert!(!standard.allow_unsafe_code);
assert_eq!(standard.min_trust_level, 5);
let permissive = SecurityPolicy::permissive();
assert!(!permissive.require_signatures);
assert!(permissive.allow_unsafe_code);
assert_eq!(permissive.min_trust_level, 0);
}
#[test]
fn test_permission_risk_levels() {
assert_eq!(Permission::FileSystemRead.risk_level(), 2);
assert_eq!(Permission::FileSystemWrite.risk_level(), 3);
assert_eq!(Permission::SystemCommands.risk_level(), 5);
assert_eq!(Permission::GpuAccess.risk_level(), 2);
}
#[test]
fn test_permission_consent_requirements() {
assert!(!Permission::FileSystemRead.requires_user_consent());
assert!(Permission::FileSystemWrite.requires_user_consent());
assert!(Permission::SystemCommands.requires_user_consent());
assert!(!Permission::GpuAccess.requires_user_consent());
}
#[test]
fn test_digital_signature_algorithms() {
let rsa_sig = DigitalSignature::new("RSA-SHA256".to_string(), vec![], "test".to_string());
assert!(rsa_sig.is_algorithm_supported());
assert_eq!(rsa_sig.security_strength(), 112);
let unknown_sig = DigitalSignature::new("UNKNOWN".to_string(), vec![], "test".to_string());
assert!(!unknown_sig.is_algorithm_supported());
assert_eq!(unknown_sig.security_strength(), 0);
}
#[test]
fn test_trust_store_key_management() {
let mut trust_store = TrustStore::new();
let key_info = PublicKeyInfo {
algorithm: "RSA".to_string(),
key_size: 2048,
added_timestamp: std::time::SystemTime::now(),
expires_at: None,
owner: "test@example.com".to_string(),
};
trust_store.add_trusted_key("fingerprint123".to_string(), key_info.clone());
assert!(!trust_store.is_key_revoked("fingerprint123"));
trust_store.revoke_key("fingerprint123".to_string());
assert!(trust_store.is_key_revoked("fingerprint123"));
}
#[test]
fn test_permission_sets() {
let fs_set = PermissionSet::file_system();
assert_eq!(fs_set.permissions.len(), 2);
assert!(fs_set.requires_user_consent());
assert_eq!(fs_set.max_risk_level(), 3);
let admin_set = PermissionSet::system_admin();
assert!(admin_set.permissions.len() > 2);
assert!(admin_set.requires_user_consent());
assert_eq!(admin_set.max_risk_level(), 5);
}
#[test]
fn test_publisher_info() {
let publisher =
PublisherInfo::new("Test Publisher".to_string(), "test@example.com".to_string());
assert!(publisher.is_complete());
let incomplete = PublisherInfo::new("".to_string(), "invalid-email".to_string());
assert!(!incomplete.is_complete());
}
}