use crate::sandbox::grant_set::GrantSet;
use crate::sandbox::language_surface::LanguageSurface;
use crate::sandbox::resource_limits::{InstructionLimit, MemoryLimit, ResourceLimits};
const CONFINED_MEMORY: MemoryLimit = MemoryLimit::mebibytes(64);
const CONFINED_INSTRUCTIONS: InstructionLimit = InstructionLimit::count(100_000_000);
const PURE_MEMORY: MemoryLimit = MemoryLimit::mebibytes(16);
const PURE_INSTRUCTIONS: InstructionLimit = InstructionLimit::count(10_000_000);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Policy {
language: LanguageSurface,
grants: GrantSet,
limits: ResourceLimits,
}
impl Policy {
#[must_use]
pub const fn trusted() -> Self {
Self {
language: LanguageSurface::Full,
grants: GrantSet::unrestricted(),
limits: ResourceLimits::none(),
}
}
#[must_use]
pub const fn confined() -> Self {
Self {
language: LanguageSurface::Restricted,
grants: GrantSet::declared(),
limits: ResourceLimits::new(Some(CONFINED_MEMORY), Some(CONFINED_INSTRUCTIONS)),
}
}
#[must_use]
pub const fn pure() -> Self {
Self {
language: LanguageSurface::Minimal,
grants: GrantSet::declared(),
limits: ResourceLimits::new(Some(PURE_MEMORY), Some(PURE_INSTRUCTIONS)),
}
}
#[must_use]
pub const fn with_language(mut self, language: LanguageSurface) -> Self {
self.language = language;
self
}
#[must_use]
pub fn with_grants(mut self, grants: GrantSet) -> Self {
self.grants = grants;
self
}
#[must_use]
pub const fn with_limits(mut self, limits: ResourceLimits) -> Self {
self.limits = limits;
self
}
#[must_use]
pub const fn language(&self) -> LanguageSurface {
self.language
}
#[must_use]
pub const fn grants(&self) -> &GrantSet {
&self.grants
}
#[must_use]
pub const fn limits(&self) -> &ResourceLimits {
&self.limits
}
}
impl Default for Policy {
fn default() -> Self {
Self::confined()
}
}
#[cfg(test)]
mod tests {
use super::Policy;
use crate::sandbox::grant_set::GrantSet;
use crate::sandbox::language_surface::LanguageSurface;
use crate::sandbox::resource_limits::{MemoryLimit, ResourceLimits};
#[test]
fn confined_is_the_default_policy() {
assert_eq!(Policy::default(), Policy::confined());
}
#[test]
fn trusted_waives_the_surface_the_grants_and_the_ceilings() {
let policy = Policy::trusted();
assert_eq!(policy.language(), LanguageSurface::Full);
assert!(policy.grants().is_unrestricted());
assert_eq!(*policy.limits(), ResourceLimits::none());
}
#[test]
fn confined_restricts_the_surface_and_imposes_both_ceilings() {
let policy = Policy::confined();
assert_eq!(policy.language(), LanguageSurface::Restricted);
assert!(policy.grants().is_empty());
assert!(policy.limits().memory().is_some());
assert!(policy.limits().instructions().is_some());
}
#[test]
fn pure_is_tighter_than_confined_on_every_axis_that_can_be() {
let pure = Policy::pure();
let confined = Policy::confined();
assert_eq!(pure.language(), LanguageSurface::Minimal);
assert!(pure.limits().memory() < confined.limits().memory());
assert!(pure.limits().instructions() < confined.limits().instructions());
}
#[test]
fn each_wither_replaces_one_axis_and_leaves_the_others() {
let policy = Policy::confined().with_language(LanguageSurface::Minimal);
assert_eq!(policy.language(), LanguageSurface::Minimal);
assert_eq!(policy.limits(), Policy::confined().limits());
let policy = Policy::confined().with_grants(GrantSet::unrestricted());
assert!(policy.grants().is_unrestricted());
assert_eq!(policy.language(), LanguageSurface::Restricted);
let policy = Policy::confined().with_limits(ResourceLimits::none());
assert_eq!(*policy.limits(), ResourceLimits::none());
assert_eq!(policy.language(), LanguageSurface::Restricted);
}
#[test]
fn a_policy_can_be_tightened_beyond_any_preset() {
let policy = Policy::pure()
.with_limits(ResourceLimits::none().with_memory(Some(MemoryLimit::bytes(1))));
assert_eq!(policy.limits().memory().map(MemoryLimit::get), Some(1));
}
}