use capsec::prelude::*;
fn read_scoped(
path: &str,
scope: &Attenuated<FsRead, DirScope>,
cap: &impl CapProvider<FsRead>,
) -> Result<String, CapSecError> {
scope.check(path)?;
capsec::fs::read_to_string(path, cap)
}
fn read_from_dir(
path: &str,
raw_cap: &impl CapProvider<FsRead>,
scope: &Attenuated<FsRead, DirScope>,
) -> Result<String, CapSecError> {
scope.check(path)?;
capsec::fs::read_to_string(path, raw_cap)
}
fn connect_scoped(
addr: &str,
scope: &Attenuated<NetConnect, HostScope>,
cap: &impl CapProvider<NetConnect>,
) -> Result<std::net::TcpStream, CapSecError> {
scope.check(addr)?;
capsec::net::tcp_connect(addr, cap)
}
#[capsec::main]
fn main(root: CapRoot) -> Result<(), Box<dyn std::error::Error>> {
let fs_cap = root.fs_read();
let net_cap = root.net_connect();
let fs_for_scope = fs_cap.clone();
let net_for_scope = net_cap.clone();
let fs_scope = fs_for_scope.attenuate(DirScope::new("/tmp")?);
let net_scope = net_for_scope.attenuate(HostScope::new(["api.example.com"]));
match read_scoped("/tmp/capsec-demo.txt", &fs_scope, &fs_cap) {
Ok(data) => println!("Read {} bytes from /tmp", data.len()),
Err(e) => println!("Expected: {e}"),
}
match read_scoped("/etc/hostname", &fs_scope, &fs_cap) {
Ok(_) => println!("This should not happen"),
Err(e) => println!("Blocked by scope (expected): {e}"),
}
match connect_scoped("evil.com:8080", &net_scope, &net_cap) {
Ok(_) => println!("This should not happen"),
Err(e) => println!("Blocked by scope (expected): {e}"),
}
match read_from_dir("/tmp/capsec-demo.txt", &fs_cap, &fs_scope) {
Ok(data) => println!("Compact pattern: {} bytes", data.len()),
Err(e) => println!("Expected: {e}"),
}
println!("Scoped capabilities demo complete.");
Ok(())
}