Skip to content

Security: auths-dev/capsec

Security

SECURITY.md

Security Model

capsec enforces I/O permissions at compile time via a zero-cost capability type system. This document describes what it protects against, what it doesn't, and where the boundaries are.

The security boundary: Cap::new() is pub(crate)

The only way to construct a Cap<P> in safe Rust is through CapRoot::grant(). The constructor Cap::new() is pub(crate) — external crates cannot call it.

// This is the entire security gate:
pub(crate) fn new() -> Self { ... }

Every capability in a capsec program traces back to a CapRoot::grant() call. CapRoot is a singleton — root() panics if called twice, ensuring a single point of authority.

Has<P> is intentionally open

The Has<P> trait is open for external implementation. This is by design — the #[capsec::context] macro generates impl Has<P> on user-defined structs to enable capability threading through call stacks.

This is safe because Has<P> requires returning a Cap<P>:

pub trait Has<P: Permission> {
    fn cap_ref(&self) -> Cap<P>;
}

Any implementation must produce a Cap<P>. Since Cap::new() is pub(crate), the only way to satisfy this in safe Rust is to hold a real Cap<P> obtained from CapRoot::grant().

Enforcement: type-witnessed proofs

All capsec-std and capsec-tokio wrapper functions extract a typed proof before executing I/O:

pub fn read(path: impl AsRef<Path>, cap: &impl Has<FsRead>) -> Result<Vec<u8>, CapSecError> {
    let _proof: Cap<FsRead> = cap.cap_ref();  // proof extracted here
    Ok(std::fs::read(path)?)                   // I/O happens here
}

The concrete type annotation Cap<FsRead> is not cosmetic. It forces the compiler to resolve the return type of cap_ref() and prevents dead-code elimination. If a Has<P> implementation diverges (panics, loops, calls process::exit), the divergence fires at the _proof line — the I/O on the next line never executes.

The adversarial test suite (capsec-tests/tests/type_system.rs, section L) proves this with a PanicForge impl that panics in cap_ref() — the test confirms the panic fires before std::fs::read runs.

What capsec does NOT protect against

unsafe code

transmute, MaybeUninit, and ptr::read can forge a Cap<P> because Cap is a zero-sized type. All three attacks are documented and tested in capsec-tests/tests/type_system.rs (section C). This is expected — capsec's threat model is safe Rust only.

Direct std / tokio calls

A function can always call std::fs::read() or tokio::fs::read() directly without a capability token. The Rust compiler won't stop it — capsec wrappers are opt-in replacements, not mandatory.

This is where cargo capsec audit comes in. The audit tool statically scans source code for ambient authority calls and reports them. Functions annotated with #[capsec::deny(all)] that contain I/O calls are promoted to critical risk.

FFI and inline assembly

extern blocks and inline asm! interact with the OS directly. The audit tool flags extern blocks but cannot analyze what foreign code does.

Proc-macro-generated code

Code generated by procedural macros is invisible to the static scanner. This is inherent to syntax-level tooling.

Audit tool scope

cargo capsec audit is an AST-level heuristic scanner, not a data flow analyzer. It:

  • Detects qualified calls (std::fs::read, TcpStream::connect, Command::new)
  • Expands import aliases (use std::fs::read as load; load(...))
  • Handles glob imports (use std::fs::*; read(...))
  • Uses contextual method matching (.output() only flags when Command::new is in the same function)
  • Flags extern blocks as FFI findings

Known blind spots (documented in capsec-tests/tests/audit_evasion.rs):

  • Function pointers and closures that hide the call target
  • include!() directives that pull in code from other files
  • Module re-exports across crate boundaries
  • Dependency re-exports (a crate wrapping std::fs under a different name)
  • libc / nix crate calls (not in the pattern registry)
  • cfg-conditional code that may or may not compile

Reporting vulnerabilities

If you discover a security issue in capsec, please email the maintainers directly rather than opening a public issue. Contact: open an issue with the security label.

For issues that are not security-sensitive (e.g., audit tool false negatives, documentation gaps), please open a regular GitHub issue.

There aren’t any published security advisories