use crate::contract::capability::{EvidenceSet, SupportVerdict};
use crate::contract::plan::BoundaryRequirement;
use crate::contract::report::BoundaryReportBody;
use crate::contract::support::{BackendProfile, RequirementKind};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum LoweringPhase {
NamespaceCreate,
FsSetup,
PrivilegeDrop,
FdHygiene,
PolicyInstall,
Launch,
Observe,
Teardown,
}
impl LoweringPhase {
#[must_use]
pub fn code(self) -> u8 {
match self {
Self::NamespaceCreate => 0,
Self::FsSetup => 1,
Self::PrivilegeDrop => 2,
Self::FdHygiene => 3,
Self::PolicyInstall => 4,
Self::Launch => 5,
Self::Observe => 6,
Self::Teardown => 7,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PrimitiveId(String);
impl PrimitiveId {
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for PrimitiveId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PrimitiveVersion(u32);
impl PrimitiveVersion {
#[must_use]
pub fn new(version: u32) -> Self {
Self(version)
}
#[must_use]
pub fn get(self) -> u32 {
self.0
}
}
impl std::fmt::Display for PrimitiveVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "v{}", self.0)
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Privilege(String);
impl Privilege {
pub fn new(name: impl Into<String>) -> Self {
Self(name.into())
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for Privilege {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
pub trait PrimitiveDecl {
fn id(&self) -> PrimitiveId;
fn version(&self) -> PrimitiveVersion;
fn covers(&self) -> &[RequirementKind];
fn classify(&self, req: &BoundaryRequirement, profile: &BackendProfile) -> SupportVerdict;
fn phase(&self) -> LoweringPhase;
fn prerequisites(&self) -> &[PrimitiveId];
fn conflicts(&self) -> &[PrimitiveId];
fn required_privileges(&self) -> &[Privilege];
fn witness(&self, observed: &BoundaryReportBody, out: &mut EvidenceSet);
}
#[must_use]
pub fn classify_via_primitives(
primitives: &[&dyn PrimitiveDecl],
req: &BoundaryRequirement,
profile: &BackendProfile,
) -> SupportVerdict {
let kind = RequirementKind::of(req);
let mut verdict: Option<SupportVerdict> = None;
for primitive in primitives {
if primitive.covers().contains(&kind) {
let next = primitive.classify(req, profile);
verdict = Some(match verdict {
Some(acc) => acc.meet(&next),
None => next,
});
}
}
verdict.unwrap_or_else(SupportVerdict::unsupported)
}
#[cfg(test)]
mod primitive_tests {
use super::{
classify_via_primitives, LoweringPhase, PrimitiveDecl, PrimitiveId, PrimitiveVersion,
Privilege,
};
use crate::contract::capability::{Enforcement, EvidenceClaim, EvidenceSet, SupportVerdict};
use crate::contract::host_control::HostControl;
use crate::contract::plan::BoundaryRequirement;
use crate::contract::support::{BackendProfile, RequirementKind};
use std::collections::BTreeMap;
struct FakePrim {
id: PrimitiveId,
version: PrimitiveVersion,
covers: Vec<RequirementKind>,
phase: LoweringPhase,
prereqs: Vec<PrimitiveId>,
conflicts: Vec<PrimitiveId>,
privileges: Vec<Privilege>,
verdict: SupportVerdict,
}
impl FakePrim {
fn new(id: &str, phase: LoweringPhase) -> Self {
Self {
id: PrimitiveId::new(id),
version: PrimitiveVersion::new(1),
covers: Vec::new(),
phase,
prereqs: Vec::new(),
conflicts: Vec::new(),
privileges: Vec::new(),
verdict: SupportVerdict::unsupported(),
}
}
fn covers(mut self, kinds: &[RequirementKind]) -> Self {
self.covers = kinds.to_vec();
self
}
fn verdict(mut self, verdict: SupportVerdict) -> Self {
self.verdict = verdict;
self
}
}
impl PrimitiveDecl for FakePrim {
fn id(&self) -> PrimitiveId {
self.id.clone()
}
fn version(&self) -> PrimitiveVersion {
self.version
}
fn covers(&self) -> &[RequirementKind] {
&self.covers
}
fn classify(&self, _req: &BoundaryRequirement, _p: &BackendProfile) -> SupportVerdict {
self.verdict.clone()
}
fn phase(&self) -> LoweringPhase {
self.phase
}
fn prerequisites(&self) -> &[PrimitiveId] {
&self.prereqs
}
fn conflicts(&self) -> &[PrimitiveId] {
&self.conflicts
}
fn required_privileges(&self) -> &[Privilege] {
&self.privileges
}
fn witness(
&self,
_observed: &crate::contract::report::BoundaryReportBody,
_out: &mut EvidenceSet,
) {
}
}
fn launch_req() -> BoundaryRequirement {
BoundaryRequirement::HostControl(HostControl::LaunchWorkload)
}
fn empty_profile() -> BackendProfile {
BackendProfile::from_ceiling(BTreeMap::new())
}
#[test]
fn classify_meets_covering_primitives_and_fails_closed_when_uncovered() {
let profile = empty_profile();
let req = launch_req();
let bystander = FakePrim::new("other", LoweringPhase::Launch); assert_eq!(
classify_via_primitives(&[&bystander], &req, &profile).enforcement,
Enforcement::Unsupported,
);
let strong = FakePrim::new("strong", LoweringPhase::Launch)
.covers(&[RequirementKind::LaunchWorkload])
.verdict(SupportVerdict::new(
Enforcement::Enforced,
[EvidenceClaim::TerminalOutcome].into_iter().collect(),
));
let weak = FakePrim::new("weak", LoweringPhase::Launch)
.covers(&[RequirementKind::LaunchWorkload])
.verdict(SupportVerdict::new(
Enforcement::Mediated,
[
EvidenceClaim::TerminalOutcome,
EvidenceClaim::CapturedStreams,
]
.into_iter()
.collect(),
));
let met = classify_via_primitives(&[&strong, &weak], &req, &profile);
assert_eq!(
met.enforcement,
Enforcement::Mediated,
"enforcement floored"
);
assert!(met.evidence.contains(EvidenceClaim::TerminalOutcome));
assert!(
!met.evidence.contains(EvidenceClaim::CapturedStreams),
"evidence intersected: a claim only one primitive makes is dropped"
);
let met_rev = classify_via_primitives(&[&weak, &strong], &req, &profile);
assert_eq!(met, met_rev);
}
}