
By Adam Harvey, Senior Software Engineer, Rust Foundation
In 2025, Alpha-Omega funded work by the Rust Foundation to implement an experimental tool to generate Capslock data from Rust projects. This blog post captures where we’re at now!
What is a Capslock, anyway?
Capslock is a Google-originated project to analyse and track which capabilities are used by a particular software package. A capability, as stated in the Capslock repo, refers to the permissions a package uses via its direct and transitive dependencies. In other words, a capability refers to privileged actions such as accessing the filesystem or making a network request. For example, reading from /etc/passwd or writing to /tmp would be a capability permission a package may have.
Until now, Capslock has only supported Go.
Walter Pearce and I have been working on a Rust implementation, while simultaneously Sergej Dechand has been working on a Java implementation. In parallel, we’ve also been working with John Dethridge, Nicky Ringland, and others at Google to start developing a common schema that can be used to describe capabilities across language ecosystems.
Rust experimentation
To keep the problem space focused, for now we’ve concentrated on per-binary analysis for Rust, rather than breaking results out on a per-crate basis. In other words, we are looking and reporting around entire Rust programs — which may be made up of multiple crates — instead of looking at each crate individually.
We’ve developed a tool called cargo-capslock to perform both static and runtime capability analysis on Rust binaries. Currently in an experimental state, cargo-capslock is allowing us to explore the capability analysis space, which will inform continued work and improvements to the tool, such as the development of the aforementioned common schema for downstream Capslock tooling.
Static analysis
The static analyser — seen here analysing ripgrep — uses Cargo to perform a normal build of the binary being analysed, but with an extra rustc flag to generate LLVM bitcode that can be analysed using forks of the llvm-ir and llvm-ir-analysis crates that were previously extended to support the Rust Foundation’s painter project.
Unsurprisingly, since ripgrep searches over files, we see that the file capability is required.
We also see one of the current shortcomings of the current tool: because the static analysis requires mapping capabilities based on the call graph, and we don’t have visibility below the last Rust frame into libc (calling into libc, a C library, crosses an FFI boundary; beyond that rustc cannot make real determinations about what is underneath), we essentially have to have a mapping of Rust functions to capabilities. For the purposes of this experiment, this mapping has been automatically generated by instrumenting the Rust compiler and standard library test suites and examining the syscalls issued by each function, but this doesn’t presently have the fidelity required to distinguish between ioctl syscalls that read and modify actual system state, versus those that — in this case — simply check if the stdout is a TTY.
A production version of this tool is likely to need human review of all the functions provided in Rust’s core, alloc, and std libraries, along with commonly used crates that also make syscalls, such as libc and nix.
Runtime analysis
The runtime analyser runs the given binary, tracing it using ptrace, and maps syscalls directly to capabilities.
There are pros and cons to this approach compared to static analysis. The main pro is that the exact behaviour of the binary is analysed, which avoids potential false positives on overloaded standard library calls. The main con is that you need to actually have a binary that represents the functionality of the program or service you want to analyse, and you need to trust the code enough to be willing to run it at all. At present, cargo capslock doesn’t sandbox the execution, although we also have experimental code at the Rust Foundation for crate analysis that uses the same underlying library in conjunction with bubblewrap to provide a level of sandboxing, so it’s likely that we’ll integrate that into the eventual production tool.
Summary of Static and Runtime Analysis
This table shows a quick summary of the differences between static and runtime analysis as it pertains to various features.
| Feature | Static Analysis (LLVM Bitcode) | Runtime Analysis (ptrace) |
| Accuracy | High coverage of all possible paths. | Real-world behavior; no false positives. |
| Safety | Perfectly safe; no code is executed. | Requires running the binary (needs a sandbox). |
| Complexity | High (requires LLVM IR mapping). | Low (direct syscall monitoring). |
| Blind Spots | Below the Rust frame (e.g., libc). | Only sees code paths that are actually triggered. |
The future
This project has validated that Capslock data can be generated from Rust projects through both static and runtime analysis.
There are no significant technical impediments to providing more fine-grained capability data, nor performing this analysis on a per-crate basis, but there is still significant work ahead to improve the fidelity of the data produced before this project will be ready for production use. Similarly, we look forward to working with the rest of the Capslock ecosystem to begin to build the tooling required for widespread adoption of capability-based analysis and sandboxing.
In the longer term, we are also excited about using this data for other purposes — for example, taking the call graphs observed in the LLVM bitcode and matching stacks against known CVEs to provide execution-flow-specific reporting of vulnerability exposures.
Finally, little of this work is truly Rust-specific. Any LLVM-based toolchain can produce the LLVM bitcode used for the static analysis. The syscall tracing approach used by the runtime analyser can be applied to any binary, and function-level data can be provided if symbols are available. This opens up opportunities to provide baseline Capslock functionality for other languages with (relatively) minimal effort.
We will continue to develop cargo-capslock. In the meantime, feel free to study the tool yourself at its GitHub repo and, if you have any feedback or suggestions, please file an issue there.
Author bio
Adam Harvey is a security-focused software developer at the Rust Foundation working on ecosystem security, especially around improving crate supply chain security, and is also a member of the crates.io team.
When he’s not finding poorly written cryptocurrency wallet exfiltrators, he plays cricket, kayaks, speaks Spanish extremely badly, throws tennis balls for his golden retriever, and tries to convince people that his Australian accent is actually flawless Canadian.