pub mod builders;
mod encoder;
mod namedargument;
mod permission;
mod permissionset;
mod types;
pub use builders::*;
pub use encoder::*;
pub use namedargument::NamedArgument;
pub use permission::Permission;
pub use permissionset::PermissionSet;
pub use types::*;
#[cfg(test)]
mod tests {
use crate::{
metadata::security::{
encode_permission_set, ArgumentType, ArgumentValue, NamedArgument, Permission,
PermissionSet, PermissionSetBuilder, PermissionSetFormat, SecurityAction,
},
Result,
};
#[test]
fn test_round_trip_security_permission_unrestricted() -> Result<()> {
let original_permissions = vec![Permission {
class_name: "System.Security.Permissions.SecurityPermission".to_string(),
assembly_name: "mscorlib".to_string(),
named_arguments: vec![NamedArgument {
name: "Unrestricted".to_string(),
arg_type: ArgumentType::Boolean,
value: ArgumentValue::Boolean(true),
}],
}];
let permission_blob =
encode_permission_set(&original_permissions, PermissionSetFormat::BinaryLegacy)?;
let parsed_set = PermissionSet::new(&permission_blob)?;
assert_eq!(parsed_set.permissions().len(), 1);
assert!(parsed_set.is_unrestricted());
assert!(parsed_set.is_full_trust());
let permission = &parsed_set.permissions()[0];
assert_eq!(
permission.class_name,
"System.Security.Permissions.SecurityPermission"
);
assert_eq!(permission.named_arguments.len(), 1);
assert_eq!(permission.named_arguments[0].name, "Unrestricted");
if let ArgumentValue::Boolean(value) = &permission.named_arguments[0].value {
assert!(value);
} else {
panic!("Expected boolean value for Unrestricted");
}
Ok(())
}
#[test]
fn test_round_trip_file_io_permission() -> Result<()> {
let original_permissions = vec![Permission {
class_name: "System.Security.Permissions.FileIOPermission".to_string(),
assembly_name: "mscorlib".to_string(),
named_arguments: vec![
NamedArgument {
name: "Read".to_string(),
arg_type: ArgumentType::String,
value: ArgumentValue::String("C:\\Data;C:\\Config".to_string()),
},
NamedArgument {
name: "Write".to_string(),
arg_type: ArgumentType::String,
value: ArgumentValue::String("C:\\Logs;C:\\Output".to_string()),
},
],
}];
let permission_blob =
encode_permission_set(&original_permissions, PermissionSetFormat::BinaryLegacy)?;
let parsed_set = PermissionSet::new(&permission_blob)?;
assert_eq!(parsed_set.permissions().len(), 1);
assert!(parsed_set.has_file_io());
assert!(!parsed_set.is_full_trust());
let read_paths = parsed_set.get_all_file_read_paths();
let write_paths = parsed_set.get_all_file_write_paths();
assert_eq!(read_paths.len(), 1);
assert_eq!(read_paths[0], "C:\\Data;C:\\Config");
assert_eq!(write_paths.len(), 1);
assert_eq!(write_paths[0], "C:\\Logs;C:\\Output");
Ok(())
}
#[test]
fn test_round_trip_multiple_permissions() -> Result<()> {
let original_permissions = vec![
Permission {
class_name: "System.Security.Permissions.SecurityPermission".to_string(),
assembly_name: "mscorlib".to_string(),
named_arguments: vec![NamedArgument {
name: "Flags".to_string(),
arg_type: ArgumentType::String,
value: ArgumentValue::String("Execution, SkipVerification".to_string()),
}],
},
Permission {
class_name: "System.Security.Permissions.FileIOPermission".to_string(),
assembly_name: "mscorlib".to_string(),
named_arguments: vec![NamedArgument {
name: "Read".to_string(),
arg_type: ArgumentType::String,
value: ArgumentValue::String("C:\\temp".to_string()),
}],
},
Permission {
class_name: "System.Security.Permissions.RegistryPermission".to_string(),
assembly_name: "mscorlib".to_string(),
named_arguments: vec![NamedArgument {
name: "Read".to_string(),
arg_type: ArgumentType::String,
value: ArgumentValue::String("HKEY_LOCAL_MACHINE\\SOFTWARE".to_string()),
}],
},
];
let permission_blob =
encode_permission_set(&original_permissions, PermissionSetFormat::BinaryLegacy)?;
let parsed_set = PermissionSet::new(&permission_blob)?;
assert_eq!(parsed_set.permissions().len(), 3);
assert!(parsed_set.has_file_io());
assert!(parsed_set.has_registry());
assert!(!parsed_set.has_reflection());
let security_perm =
parsed_set.get_permission("System.Security.Permissions.SecurityPermission");
assert!(security_perm.is_some());
let fileio_perm = parsed_set.get_permission("System.Security.Permissions.FileIOPermission");
assert!(fileio_perm.is_some());
let registry_perm =
parsed_set.get_permission("System.Security.Permissions.RegistryPermission");
assert!(registry_perm.is_some());
Ok(())
}
#[test]
fn test_round_trip_builder_api() -> Result<()> {
let permission_blob = PermissionSetBuilder::new()
.add_security_permission()
.flags("Execution, Assertion")
.build()
.add_file_io_permission()
.read_paths(&["C:\\Data", "C:\\Config"])
.write_paths(&["C:\\Logs"])
.unrestricted(false)
.build()
.encode(PermissionSetFormat::BinaryLegacy)?;
let parsed_set = PermissionSet::new(&permission_blob)?;
assert_eq!(parsed_set.permissions().len(), 2);
assert!(parsed_set.has_file_io());
assert!(!parsed_set.is_full_trust());
let security_perm = parsed_set
.get_permission("System.Security.Permissions.SecurityPermission")
.unwrap();
assert_eq!(security_perm.named_arguments.len(), 1);
assert_eq!(security_perm.named_arguments[0].name, "Flags");
let fileio_perm = parsed_set
.get_permission("System.Security.Permissions.FileIOPermission")
.unwrap();
assert_eq!(fileio_perm.named_arguments.len(), 3);
Ok(())
}
#[test]
fn test_round_trip_xml_format() -> Result<()> {
let original_permissions = vec![Permission {
class_name: "System.Security.Permissions.SecurityPermission".to_string(),
assembly_name: "mscorlib".to_string(),
named_arguments: vec![
NamedArgument {
name: "Unrestricted".to_string(),
arg_type: ArgumentType::Boolean,
value: ArgumentValue::Boolean(true),
},
NamedArgument {
name: "Flags".to_string(),
arg_type: ArgumentType::String,
value: ArgumentValue::String("AllFlags".to_string()),
},
],
}];
let xml_blob = encode_permission_set(&original_permissions, PermissionSetFormat::Xml)?;
let xml_str = String::from_utf8(xml_blob.clone()).expect("Valid UTF-8");
assert!(xml_str.contains("<PermissionSet"));
assert!(xml_str.contains("System.Security.Permissions.SecurityPermission"));
assert!(xml_str.contains("Unrestricted=\"true\""));
assert!(xml_str.contains("Flags=\"AllFlags\""));
assert!(xml_str.contains("</PermissionSet>"));
let parsed_set = PermissionSet::new(&xml_blob)?;
assert_eq!(parsed_set.permissions().len(), 1);
let permission = &parsed_set.permissions()[0];
assert_eq!(
permission.class_name,
"System.Security.Permissions.SecurityPermission"
);
assert_eq!(permission.named_arguments.len(), 2);
Ok(())
}
#[test]
fn test_round_trip_empty_permission_set() -> Result<()> {
let empty_permissions = vec![];
let permission_blob =
encode_permission_set(&empty_permissions, PermissionSetFormat::BinaryLegacy)?;
let parsed_set = PermissionSet::new(&permission_blob)?;
assert_eq!(parsed_set.permissions().len(), 0);
assert!(!parsed_set.has_file_io());
assert!(!parsed_set.has_registry());
assert!(!parsed_set.is_full_trust());
Ok(())
}
#[test]
fn test_round_trip_integer_arguments() -> Result<()> {
let original_permissions = vec![Permission {
class_name: "System.Security.Permissions.SecurityPermission".to_string(),
assembly_name: "mscorlib".to_string(),
named_arguments: vec![
NamedArgument {
name: "Flags".to_string(),
arg_type: ArgumentType::Int32,
value: ArgumentValue::Int32(7), },
NamedArgument {
name: "Unrestricted".to_string(),
arg_type: ArgumentType::Boolean,
value: ArgumentValue::Boolean(false),
},
],
}];
let permission_blob =
encode_permission_set(&original_permissions, PermissionSetFormat::BinaryLegacy)?;
let parsed_set = PermissionSet::new(&permission_blob)?;
assert_eq!(parsed_set.permissions().len(), 1);
let permission = &parsed_set.permissions()[0];
assert_eq!(permission.named_arguments.len(), 2);
let flags_arg = permission
.named_arguments
.iter()
.find(|arg| arg.name == "Flags")
.expect("Should have Flags argument");
if let ArgumentValue::Int32(value) = &flags_arg.value {
assert_eq!(*value, 7);
} else {
panic!("Expected Int32 value for Flags");
}
Ok(())
}
#[test]
fn test_round_trip_special_characters() -> Result<()> {
let original_permissions = vec![Permission {
class_name: "System.Security.Permissions.FileIOPermission".to_string(),
assembly_name: "mscorlib".to_string(),
named_arguments: vec![NamedArgument {
name: "Read".to_string(),
arg_type: ArgumentType::String,
value: ArgumentValue::String("C:\\Program Files\\My App\\data.xml".to_string()),
}],
}];
let permission_blob =
encode_permission_set(&original_permissions, PermissionSetFormat::BinaryLegacy)?;
let parsed_set = PermissionSet::new(&permission_blob)?;
assert_eq!(parsed_set.permissions().len(), 1);
let permission = &parsed_set.permissions()[0];
assert_eq!(permission.named_arguments.len(), 1);
if let ArgumentValue::String(path) = &permission.named_arguments[0].value {
assert_eq!(path, "C:\\Program Files\\My App\\data.xml");
} else {
panic!("Expected string value for Read path");
}
Ok(())
}
#[test]
fn test_security_actions() {
let actions = vec![
SecurityAction::Demand,
SecurityAction::Assert,
SecurityAction::Deny,
SecurityAction::PermitOnly,
SecurityAction::LinkDemand,
SecurityAction::InheritanceDemand,
SecurityAction::RequestMinimum,
SecurityAction::RequestOptional,
SecurityAction::RequestRefuse,
SecurityAction::PrejitGrant,
SecurityAction::PrejitDeny,
SecurityAction::NonCasDemand,
SecurityAction::NonCasLinkDemand,
SecurityAction::NonCasInheritance,
];
for action in actions {
let action_value: u16 = action.into();
let converted_back = SecurityAction::from(action_value);
assert_eq!(converted_back, action);
}
}
#[test]
fn test_permission_analysis() -> Result<()> {
let permission_blob = PermissionSetBuilder::new()
.add_security_permission()
.flags("SkipVerification, ControlPolicy, ControlEvidence")
.build()
.add_file_io_permission()
.read_paths(&["C:\\Data"])
.write_paths(&["C:\\Logs"])
.build()
.encode(PermissionSetFormat::BinaryLegacy)?;
let parsed_set = PermissionSet::new(&permission_blob)?;
assert!(parsed_set.has_file_io());
assert!(!parsed_set.has_registry());
assert!(!parsed_set.has_reflection());
assert!(!parsed_set.has_environment());
assert!(parsed_set.is_full_trust());
let read_paths = parsed_set.get_all_file_read_paths();
let write_paths = parsed_set.get_all_file_write_paths();
assert_eq!(read_paths, vec!["C:\\Data"]);
assert_eq!(write_paths, vec!["C:\\Logs"]);
Ok(())
}
}