Skip to main content

capsec_core/
permission.rs

1//! The sealed [`Permission`] trait and all built-in permission types.
2//!
3//! Permissions are zero-sized marker types that encode what kind of I/O a
4//! capability token grants. They form a sealed hierarchy — external crates
5//! cannot define new permissions, ensuring the set is auditable.
6//!
7//! # Built-in permissions
8//!
9//! | Type | Category | What it grants |
10//! |------|----------|----------------|
11//! | [`FsRead`] | Filesystem | Read files, list directories, check metadata |
12//! | [`FsWrite`] | Filesystem | Write, create, delete files and directories |
13//! | [`FsAll`] | Filesystem | All filesystem operations (subsumes `FsRead` + `FsWrite`) |
14//! | [`NetConnect`] | Network | Open outbound TCP/UDP connections |
15//! | [`NetBind`] | Network | Bind listeners and sockets to local ports |
16//! | [`NetAll`] | Network | All network operations (subsumes `NetConnect` + `NetBind`) |
17//! | [`EnvRead`] | Environment | Read environment variables |
18//! | [`EnvWrite`] | Environment | Modify or remove environment variables |
19//! | [`Spawn`] | Process | Execute subprocesses |
20//! | [`Ambient`] | Everything | Full ambient authority — the "god token" |
21//!
22//! # Tuples
23//!
24//! Two permissions can be bundled via a tuple: `(FsRead, NetConnect)` is itself
25//! a `Permission`, and `Cap<(FsRead, NetConnect)>` satisfies both `Has<FsRead>`
26//! and `Has<NetConnect>`. All 2-tuple combinations of built-in permissions are
27//! supported.
28//!
29//! # Subsumption
30//!
31//! Some permissions imply others. [`FsAll`] subsumes both [`FsRead`] and [`FsWrite`],
32//! meaning a `Cap<FsAll>` can be used anywhere a `Cap<FsRead>` is required.
33//! [`Ambient`] subsumes everything.
34
35/// Marker trait for all capability permissions. Sealed to prevent external implementation.
36///
37/// Every permission type is a zero-sized struct that implements this trait.
38/// The sealed pattern ensures that only the permissions defined in this crate
39/// can be used as capability tokens — external crates cannot forge new permissions.
40pub trait Permission: sealed::Sealed + 'static {}
41
42//  Filesystem
43
44/// Permission to read files, list directories, and check metadata.
45pub struct FsRead;
46
47/// Permission to write, create, rename, and delete files and directories.
48pub struct FsWrite;
49
50/// Permission for all filesystem operations. Subsumes [`FsRead`] and [`FsWrite`].
51pub struct FsAll;
52
53//  Network
54
55/// Permission to open outbound TCP and UDP connections.
56pub struct NetConnect;
57
58/// Permission to bind TCP listeners and UDP sockets to local ports.
59pub struct NetBind;
60
61/// Permission for all network operations. Subsumes [`NetConnect`] and [`NetBind`].
62pub struct NetAll;
63
64//  Environment
65
66/// Permission to read environment variables.
67pub struct EnvRead;
68
69/// Permission to modify or remove environment variables.
70pub struct EnvWrite;
71
72//  Process
73
74/// Permission to spawn and execute subprocesses via `std::process::Command`.
75pub struct Spawn;
76
77//  Ambient
78
79/// Full ambient authority — grants every permission.
80///
81/// This is the "god token." A `Cap<Ambient>` satisfies any `Has<P>` bound.
82/// Use sparingly and only at the capability root.
83pub struct Ambient;
84
85//  Permission impls
86
87impl Permission for FsRead {}
88impl Permission for FsWrite {}
89impl Permission for FsAll {}
90impl Permission for NetConnect {}
91impl Permission for NetBind {}
92impl Permission for NetAll {}
93impl Permission for EnvRead {}
94impl Permission for EnvWrite {}
95impl Permission for Spawn {}
96impl Permission for Ambient {}
97
98//  Tuple permissions
99
100impl<A: Permission, B: Permission> Permission for (A, B) {}
101
102//  Subsumption
103
104/// Indicates that `Self` implies permission `P`.
105///
106/// When `Super: Subsumes<Sub>`, a `Cap<Super>` can satisfy `Has<Sub>`.
107/// For example, `FsAll: Subsumes<FsRead>` means `Cap<FsAll>` works
108/// anywhere `Has<FsRead>` is required.
109pub trait Subsumes<P: Permission>: Permission {}
110
111impl Subsumes<FsRead> for FsAll {}
112impl Subsumes<FsWrite> for FsAll {}
113impl Subsumes<NetConnect> for NetAll {}
114impl Subsumes<NetBind> for NetAll {}
115impl<P: Permission> Subsumes<P> for Ambient {}
116
117//  Sealed
118
119mod sealed {
120    pub trait Sealed {}
121    impl Sealed for super::FsRead {}
122    impl Sealed for super::FsWrite {}
123    impl Sealed for super::FsAll {}
124    impl Sealed for super::NetConnect {}
125    impl Sealed for super::NetBind {}
126    impl Sealed for super::NetAll {}
127    impl Sealed for super::EnvRead {}
128    impl Sealed for super::EnvWrite {}
129    impl Sealed for super::Spawn {}
130    impl Sealed for super::Ambient {}
131    impl<A: Sealed, B: Sealed> Sealed for (A, B) {}
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137    use std::mem::size_of;
138
139    #[test]
140    fn all_permissions_are_zst() {
141        assert_eq!(size_of::<FsRead>(), 0);
142        assert_eq!(size_of::<FsWrite>(), 0);
143        assert_eq!(size_of::<FsAll>(), 0);
144        assert_eq!(size_of::<NetConnect>(), 0);
145        assert_eq!(size_of::<NetBind>(), 0);
146        assert_eq!(size_of::<NetAll>(), 0);
147        assert_eq!(size_of::<EnvRead>(), 0);
148        assert_eq!(size_of::<EnvWrite>(), 0);
149        assert_eq!(size_of::<Spawn>(), 0);
150        assert_eq!(size_of::<Ambient>(), 0);
151    }
152
153    // Compile-time proof that subsumption relationships hold:
154    fn _assert_subsumes<Super: Subsumes<Sub>, Sub: Permission>() {}
155
156    #[test]
157    fn tuple_permission_is_zst() {
158        assert_eq!(size_of::<(FsRead, NetConnect)>(), 0);
159    }
160
161    #[test]
162    fn subsumption_relationships() {
163        _assert_subsumes::<FsAll, FsRead>();
164        _assert_subsumes::<FsAll, FsWrite>();
165        _assert_subsumes::<NetAll, NetConnect>();
166        _assert_subsumes::<NetAll, NetBind>();
167        _assert_subsumes::<Ambient, FsRead>();
168        _assert_subsumes::<Ambient, NetConnect>();
169        _assert_subsumes::<Ambient, Spawn>();
170    }
171}