pub trait FacetTerm: Copy + Eq + Sized + 'static {
fn all() -> &'static [Self];
fn as_str(self) -> &'static str;
fn from_term(s: &str) -> Option<Self>;
}
macro_rules! ordinal_term {
(
$(#[$meta:meta])*
$name:ident { $first:ident => $fs:literal $(, $rest:ident => $rs:literal)* $(,)? }
) => {
$(#[$meta])*
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum $name {
#[default]
$first,
$($rest),*
}
impl FacetTerm for $name {
fn all() -> &'static [Self] { &[Self::$first $(, Self::$rest)*] }
fn as_str(self) -> &'static str {
match self { Self::$first => $fs $(, Self::$rest => $rs)* }
}
fn from_term(s: &str) -> Option<Self> {
match s { $fs => Some(Self::$first), $($rs => Some(Self::$rest),)* _ => None }
}
}
};
}
macro_rules! categorical_term {
(
$(#[$meta:meta])*
$name:ident { $first:ident => $fs:literal $(, $rest:ident => $rs:literal)* $(,)? }
) => {
$(#[$meta])*
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub enum $name {
#[default]
$first,
$($rest),*
}
impl FacetTerm for $name {
fn all() -> &'static [Self] { &[Self::$first $(, Self::$rest)*] }
fn as_str(self) -> &'static str {
match self { Self::$first => $fs $(, Self::$rest => $rs)* }
}
fn from_term(s: &str) -> Option<Self> {
match s { $fs => Some(Self::$first), $($rs => Some(Self::$rest),)* _ => None }
}
}
};
}
categorical_term! {
Operation {
Observe => "observe",
Create => "create",
Mutate => "mutate",
Destroy => "destroy",
Execute => "execute",
Communicate => "communicate",
Configure => "configure", Authorize => "authorize", Control => "control", }
}
ordinal_term! {
LocalLocus {
Process => "process",
Temp => "temp",
SandboxScope => "sandbox-scope",
Worktree => "worktree",
Adjacent => "adjacent", WorktreeTrusted => "worktree-trusted", User => "user", Machine => "machine", SystemIntegrity => "system-integrity", Device => "device", Kernel => "kernel", }
}
ordinal_term! {
RemoteReach {
None => "none",
Fixed => "fixed",
Arbitrary => "arbitrary",
}
}
categorical_term! {
RemoteBinding {
Na => "n/a", Pinned => "pinned", Ambient => "ambient",
}
}
ordinal_term! {
Provenance {
Na => "n/a", Established => "established", Literal => "literal", Opaque => "opaque", }
}
ordinal_term! {
Scale {
Single => "single",
Bounded => "bounded", Unbounded => "unbounded", }
}
ordinal_term! {
RetrievalGranularity {
Metadata => "metadata", Record => "record", BulkContent => "bulk-content", }
}
ordinal_term! {
Authority {
User => "user",
Elevated => "elevated", Root => "root",
OtherUser => "other-user", }
}
ordinal_term! {
Isolation {
None => "none",
View => "view", Namespace => "namespace",
Userns => "userns",
Vm => "vm",
Ocap => "ocap",
}
}
ordinal_term! {
Reversibility {
None => "none", Trivial => "trivial", Recoverable => "recoverable", Effortful => "effortful", Irreversible => "irreversible",
}
}
ordinal_term! {
PersistenceLevel {
Transient => "transient",
Data => "data",
Reconfiguring => "reconfiguring", Installing => "installing", }
}
ordinal_term! {
TriggerEscape {
Immediate => "immediate", Detached => "detached", Recurring => "recurring", Boot => "boot", }
}
categorical_term! {
TriggerKind {
None => "none", Clock => "clock", Event => "event", }
}
ordinal_term! {
DisclosureAudience {
None => "none",
LocalProcess => "local-process", LocalPersistent => "local-persistent", TrustedRemote => "trusted-remote",
SharedRemote => "shared-remote",
Public => "public",
}
}
ordinal_term! {
SecretLevel {
None => "none",
UsesAmbient => "uses-ambient",
Reads => "reads",
Writes => "writes",
Transmits => "transmits",
}
}
categorical_term! {
Channel {
None => "none",
Filesystem => "filesystem",
StdoutToModel => "stdout-to-model",
Network => "network",
Clipboard => "clipboard", Ipc => "ipc",
CredentialStore => "credential-store", CrossProcess => "cross-process", Unknown => "unknown",
}
}
categorical_term! {
Principal {
Own => "own",
Cross => "cross",
}
}
ordinal_term! {
NetDirection {
None => "none",
Loopback => "loopback",
Outbound => "outbound",
InboundListen => "inbound-listen",
}
}
ordinal_term! {
NetDestination {
Na => "n/a",
Fixed => "fixed",
Arbitrary => "arbitrary",
}
}
ordinal_term! {
NetPayload {
None => "none",
Fetches => "fetches",
SendsHostData => "sends-host-data",
}
}
ordinal_term! {
ExecutionTrust {
None => "none",
SelfCode => "self",
CallerInline => "caller-inline",
CallerFile => "caller-file",
AmbientConfig => "ambient-config", NetworkSourced => "network-sourced",
}
}
categorical_term! {
SupplySource {
UnverifiedUrl => "unverified-url",
PublicRegistry => "public-registry",
SignedRepo => "signed-repo",
PrivateRegistry => "private-registry",
Vendored => "vendored",
}
}
ordinal_term! {
Pinning {
Floating => "floating",
Version => "version",
HashVerified => "hash-verified",
Digest => "digest",
}
}
categorical_term! {
ExecSurface {
None => "none",
InstallHook => "install-hook", BuildScript => "build-script", CallTime => "call-time", RunArtifact => "run-artifact", }
}
ordinal_term! {
Cost {
None => "none",
LocalResource => "local-resource",
Metered => "metered", Quota => "quota",
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Locus {
pub local: LocalLocus,
pub remote: RemoteReach,
pub binding: RemoteBinding,
pub provenance: Provenance,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Trigger {
pub escape: TriggerEscape,
pub kind: TriggerKind,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Persistence {
pub level: PersistenceLevel,
pub trigger: Trigger,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Disclosure {
pub audience: DisclosureAudience,
pub channel: Channel,
pub principal: Principal,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Secret {
pub level: SecretLevel,
pub channel: Channel,
pub principal: Principal,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Network {
pub direction: NetDirection,
pub destination: NetDestination,
pub payload: NetPayload,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct SupplyChain {
pub source: SupplySource,
pub pinning: Pinning,
pub exec_surface: ExecSurface,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Execution {
pub trust: ExecutionTrust,
pub supply_chain: Option<SupplyChain>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Capability {
pub operation: Operation,
pub locus: Locus,
pub scale: Scale,
pub retrieval: RetrievalGranularity,
pub authority: Authority,
pub isolation: Isolation,
pub reversibility: Reversibility,
pub persistence: Persistence,
pub disclosure: Disclosure,
pub secret: Secret,
pub network: Network,
pub execution: Execution,
pub cost: Cost,
pub because: String,
}
impl Capability {
pub fn new(operation: Operation) -> Self {
Self { operation, ..Self::default() }
}
pub fn worst(because: impl Into<String>) -> Self {
Self {
operation: Operation::Execute,
locus: Locus {
local: LocalLocus::Kernel,
remote: RemoteReach::Arbitrary,
binding: RemoteBinding::Ambient,
provenance: Provenance::Opaque,
},
scale: Scale::Unbounded,
retrieval: RetrievalGranularity::BulkContent,
authority: Authority::OtherUser,
isolation: Isolation::None,
reversibility: Reversibility::Irreversible,
persistence: Persistence {
level: PersistenceLevel::Installing,
trigger: Trigger { escape: TriggerEscape::Boot, kind: TriggerKind::None },
},
disclosure: Disclosure {
audience: DisclosureAudience::Public,
channel: Channel::Unknown,
principal: Principal::Cross,
},
secret: Secret {
level: SecretLevel::Transmits,
channel: Channel::Unknown,
principal: Principal::Cross,
},
network: Network {
direction: NetDirection::InboundListen,
destination: NetDestination::Arbitrary,
payload: NetPayload::SendsHostData,
},
execution: Execution { trust: ExecutionTrust::NetworkSourced, supply_chain: None },
cost: Cost::Quota,
because: because.into(),
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Profile {
pub capabilities: Vec<Capability>,
}
impl Profile {
pub fn of(capabilities: Vec<Capability>) -> Self {
Self { capabilities }
}
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_term_strings_roundtrip<T: FacetTerm + std::fmt::Debug>() {
for &term in T::all() {
assert_eq!(
T::from_term(term.as_str()),
Some(term),
"term {:?} did not round-trip through {:?}",
term,
term.as_str(),
);
}
for (i, &a) in T::all().iter().enumerate() {
for &b in &T::all()[i + 1..] {
assert_ne!(a.as_str(), b.as_str(), "two variants share a TOML spelling");
}
}
assert_eq!(T::from_term("definitely-not-a-term"), None);
}
fn assert_zero_is_minimum<T: FacetTerm + Ord + Default + std::fmt::Debug>() {
let zero = T::all()[0];
assert_eq!(T::default(), zero, "Default must be the zero term (first variant)");
for &term in T::all() {
assert!(zero <= term, "zero term {zero:?} is not <= {term:?}");
}
}
#[test]
fn every_term_roundtrips_and_is_uniquely_spelled() {
assert_term_strings_roundtrip::<Operation>();
assert_term_strings_roundtrip::<LocalLocus>();
assert_term_strings_roundtrip::<RemoteReach>();
assert_term_strings_roundtrip::<RemoteBinding>();
assert_term_strings_roundtrip::<Provenance>();
assert_term_strings_roundtrip::<Scale>();
assert_term_strings_roundtrip::<RetrievalGranularity>();
assert_term_strings_roundtrip::<Authority>();
assert_term_strings_roundtrip::<Isolation>();
assert_term_strings_roundtrip::<Reversibility>();
assert_term_strings_roundtrip::<PersistenceLevel>();
assert_term_strings_roundtrip::<TriggerEscape>();
assert_term_strings_roundtrip::<TriggerKind>();
assert_term_strings_roundtrip::<DisclosureAudience>();
assert_term_strings_roundtrip::<SecretLevel>();
assert_term_strings_roundtrip::<Channel>();
assert_term_strings_roundtrip::<Principal>();
assert_term_strings_roundtrip::<NetDirection>();
assert_term_strings_roundtrip::<NetDestination>();
assert_term_strings_roundtrip::<NetPayload>();
assert_term_strings_roundtrip::<ExecutionTrust>();
assert_term_strings_roundtrip::<SupplySource>();
assert_term_strings_roundtrip::<Pinning>();
assert_term_strings_roundtrip::<ExecSurface>();
assert_term_strings_roundtrip::<Cost>();
}
#[test]
fn ordinal_zero_terms_are_the_minimum() {
assert_zero_is_minimum::<LocalLocus>();
assert_zero_is_minimum::<RemoteReach>();
assert_zero_is_minimum::<Provenance>();
assert_zero_is_minimum::<Scale>();
assert_zero_is_minimum::<RetrievalGranularity>();
assert_zero_is_minimum::<Authority>();
assert_zero_is_minimum::<Isolation>();
assert_zero_is_minimum::<Reversibility>();
assert_zero_is_minimum::<PersistenceLevel>();
assert_zero_is_minimum::<TriggerEscape>();
assert_zero_is_minimum::<DisclosureAudience>();
assert_zero_is_minimum::<SecretLevel>();
assert_zero_is_minimum::<NetDirection>();
assert_zero_is_minimum::<NetDestination>();
assert_zero_is_minimum::<NetPayload>();
assert_zero_is_minimum::<ExecutionTrust>();
assert_zero_is_minimum::<Pinning>();
assert_zero_is_minimum::<Cost>();
}
#[test]
fn ordinal_ladders_match_the_spec() {
assert!(LocalLocus::Process < LocalLocus::Worktree);
assert!(LocalLocus::Worktree < LocalLocus::Adjacent);
assert!(LocalLocus::Adjacent < LocalLocus::WorktreeTrusted);
assert!(LocalLocus::WorktreeTrusted < LocalLocus::User);
assert!(LocalLocus::Worktree < LocalLocus::Machine);
assert!(LocalLocus::Machine < LocalLocus::SystemIntegrity);
assert!(LocalLocus::SystemIntegrity < LocalLocus::Device);
assert!(LocalLocus::Device < LocalLocus::Kernel);
assert!(Scale::Single < Scale::Bounded && Scale::Bounded < Scale::Unbounded);
assert!(RetrievalGranularity::Metadata < RetrievalGranularity::Record);
assert!(RetrievalGranularity::Record < RetrievalGranularity::BulkContent);
assert!(Authority::User < Authority::Root && Authority::Root < Authority::OtherUser);
assert!(Reversibility::Recoverable < Reversibility::Irreversible);
assert!(PersistenceLevel::Data < PersistenceLevel::Installing);
assert!(TriggerEscape::Immediate < TriggerEscape::Boot);
assert!(DisclosureAudience::LocalProcess < DisclosureAudience::Public);
assert!(Provenance::Na < Provenance::Established);
assert!(Provenance::Established < Provenance::Literal);
assert!(Provenance::Literal < Provenance::Opaque);
assert!(SecretLevel::Reads < SecretLevel::Transmits);
assert!(ExecutionTrust::SelfCode < ExecutionTrust::NetworkSourced);
assert!(Pinning::Floating < Pinning::HashVerified);
}
#[test]
fn capability_new_leaves_all_other_facets_at_zero() {
let cap = Capability::new(Operation::Destroy);
assert_eq!(cap.operation, Operation::Destroy);
assert_eq!(cap.locus, Locus::default());
assert_eq!(cap.locus.local, LocalLocus::Process);
assert_eq!(cap.scale, Scale::Single);
assert_eq!(cap.retrieval, RetrievalGranularity::Metadata);
assert_eq!(cap.authority, Authority::User);
assert_eq!(cap.reversibility, Reversibility::None);
assert_eq!(cap.secret.level, SecretLevel::None);
assert_eq!(cap.disclosure.audience, DisclosureAudience::None);
assert_eq!(cap.network.direction, NetDirection::None);
assert_eq!(cap.execution.trust, ExecutionTrust::None);
assert!(cap.execution.supply_chain.is_none());
assert_eq!(cap.cost, Cost::None);
assert!(cap.because.is_empty());
}
#[test]
fn default_capability_is_a_zero_observe() {
assert_eq!(Capability::default().operation, Operation::Observe);
assert_eq!(Capability::default(), Capability::new(Operation::Observe));
}
}