use mlua::StdLib;
pub(crate) const CHUNK_LOADERS: [&str; 4] = ["load", "loadstring", "dofile", "loadfile"];
pub(crate) const UNSAFE_OS_FUNCTIONS: [&str; 7] = [
"execute",
"exit",
"getenv",
"remove",
"rename",
"tmpname",
"setlocale",
];
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum LanguageSurface {
Full,
#[default]
Restricted,
Minimal,
}
impl LanguageSurface {
#[must_use]
pub(crate) fn libraries(self) -> StdLib {
match self {
Self::Full => StdLib::ALL_SAFE,
Self::Restricted => {
StdLib::STRING
| StdLib::TABLE
| StdLib::MATH
| StdLib::UTF8
| StdLib::COROUTINE
| StdLib::OS
}
Self::Minimal => StdLib::STRING | StdLib::TABLE | StdLib::MATH | StdLib::UTF8,
}
}
#[must_use]
pub const fn withholds_unsafe_globals(self) -> bool {
!matches!(self, Self::Full)
}
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Full => "full",
Self::Restricted => "restricted",
Self::Minimal => "minimal",
}
}
}
impl core::fmt::Display for LanguageSurface {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.as_str())
}
}
#[cfg(test)]
mod tests {
use super::{CHUNK_LOADERS, LanguageSurface, UNSAFE_OS_FUNCTIONS};
use mlua::StdLib;
#[test]
fn restricted_is_the_default_surface() {
assert_eq!(LanguageSurface::default(), LanguageSurface::Restricted);
}
#[test]
fn full_loads_every_safe_library() {
assert_eq!(LanguageSurface::Full.libraries(), StdLib::ALL_SAFE);
}
#[test]
fn restricted_loads_coroutine_and_os_but_minimal_does_not() {
let restricted = LanguageSurface::Restricted.libraries();
assert!(restricted.contains(StdLib::COROUTINE));
assert!(restricted.contains(StdLib::OS));
let minimal = LanguageSurface::Minimal.libraries();
assert!(!minimal.contains(StdLib::COROUTINE));
assert!(!minimal.contains(StdLib::OS));
}
#[test]
fn every_surface_loads_the_pure_computation_libraries() {
for surface in [
LanguageSurface::Full,
LanguageSurface::Restricted,
LanguageSurface::Minimal,
] {
let libs = surface.libraries();
assert!(libs.contains(StdLib::STRING), "{surface}");
assert!(libs.contains(StdLib::TABLE), "{surface}");
assert!(libs.contains(StdLib::MATH), "{surface}");
assert!(libs.contains(StdLib::UTF8), "{surface}");
}
}
#[test]
fn no_surface_below_full_loads_io_or_package() {
for surface in [LanguageSurface::Restricted, LanguageSurface::Minimal] {
let libs = surface.libraries();
assert!(!libs.contains(StdLib::IO), "{surface}");
assert!(!libs.contains(StdLib::PACKAGE), "{surface}");
}
}
#[test]
fn only_full_keeps_the_unsafe_globals() {
assert!(!LanguageSurface::Full.withholds_unsafe_globals());
assert!(LanguageSurface::Restricted.withholds_unsafe_globals());
assert!(LanguageSurface::Minimal.withholds_unsafe_globals());
}
#[test]
fn each_surface_reports_a_distinct_name() {
assert_eq!(LanguageSurface::Full.to_string(), "full");
assert_eq!(LanguageSurface::Restricted.to_string(), "restricted");
assert_eq!(LanguageSurface::Minimal.to_string(), "minimal");
}
#[test]
fn the_withheld_lists_name_every_route_back_to_arbitrary_code() {
assert!(CHUNK_LOADERS.contains(&"load"));
assert!(CHUNK_LOADERS.contains(&"loadfile"));
assert!(UNSAFE_OS_FUNCTIONS.contains(&"execute"));
assert!(UNSAFE_OS_FUNCTIONS.contains(&"setlocale"));
}
}