Skip to content

RFC: Rustc lint plugin for ambient authority detection #43

@bordumb

Description

@bordumb

Problem

capsec currently detects ambient authority via AST-level pattern matching (cargo capsec audit). This misses cases where type information is needed — specifically, it cannot check whether a function calling std::fs::read() has a Has<FsRead> bound that makes the call authorized.

The goal: a lint that flags std::fs/std::net/std::env/std::process calls in functions that don't have Has<P> bounds, producing clippy-quality diagnostics with fix suggestions.

Desired UX

error: ambient authority: `std::fs::read` called without capability token
  --> src/data.rs:12:5
   |
12 |     std::fs::read(path)
   |     ^^^^^^^^^^^^^^^^^^^ this performs filesystem I/O
   |
   = help: add a capability parameter: `cap: &impl Has<FsRead>`
   = note: `#[deny(capsec::ambient_authority)]` on by default

Three options

Option A: Raw rustc_private lint plugin (nightly only)

Write a custom lint pass using rustc_private internals — the same approach clippy uses internally.

Pros:

  • Full HIR access — can inspect function signatures for Has<P> bounds
  • Can provide rich fix suggestions (#[rustfix]-compatible)
  • Can resolve types, check trait bounds, follow impl blocks
  • Maximum diagnostic quality

Cons:

  • Requires nightly Rustrustc_private is unstable
  • Breaks across Rust versions — internal APIs change without warning
  • Ongoing maintenance burden: must track rustc releases
  • Users must run via a custom driver binary (like cargo-clippy itself)

Effort: High initial, high ongoing maintenance

Option B: dylint framework (nightly, but managed)

Use dylint (Trail of Bits) — a framework for writing and distributing third-party rustc lints.

Pros:

  • Same HIR access as Option A (full type information)
  • dylint handles toolchain management and lint distribution
  • Lints are distributed as dynamic libraries — users install via cargo install cargo-dylint
  • Active maintainer (Trail of Bits), used by production projects
  • Can produce the same rich diagnostics as Option A

Cons:

  • Still requires nightly for rustc_private under the hood
  • Adds a dependency on the dylint ecosystem
  • Less control over the driver than a fully custom approach
  • dylint's own stability depends on Trail of Bits continuing to maintain it

Effort: Medium initial (dylint handles boilerplate), medium ongoing

Option C: clippy.toml + cargo capsec audit (stable Rust)

Use clippy's built-in disallowed-methods configuration to flag raw std::fs/std::net calls, combined with cargo capsec audit for deeper analysis.

Pros:

  • Works on stable Rust — no nightly required
  • Zero new code — clippy already supports disallowed-methods and disallowed-types
  • Users add a clippy.toml to their project (capsec could generate it)
  • Integrates with existing CI pipelines that already run cargo clippy

Cons:

  • No type information — cannot check for Has<P> bounds
  • Can only say "this method is disallowed" — no smart suggestions
  • Cannot distinguish authorized calls (function has Has<FsRead>) from unauthorized calls
  • Essentially a blocklist, not an authority analysis
  • The ambient-authority crate already documents this exact approach

Effort: Low initial, low ongoing

Recommendation

Option B (dylint) as the primary path, with Option C shipped immediately as a stable-Rust stopgap.

  • Ship a capsec clippy-config command that generates a clippy.toml with all ambient authority methods disallowed (Option C — available today, stable Rust)
  • Build a dylint lint crate (capsec-lint) that uses HIR to check for Has<P> bounds on functions calling ambient authority APIs (Option B — nightly, but production-quality diagnostics)
  • The dylint lint becomes the recommended path for teams that can use nightly; the clippy.toml approach serves teams that require stable

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions