Capabilities taken literally are more of a network thing (it's how you prove you have access to a computer that doesn't trust you). On a language, you don't need the capabilities themselves.
This of course also depends on some related static guarantees that a function can't access ambient capabilities not passed directly in their arguments, but this is a much simpler static guarantee than effect typing and doesn't require specific analysis over normal parameter types.
You may be thinking of the term in a different context. In this context, they are a general security concept and definitely apply to more than the network, including languages:
https://en.wikipedia.org/wiki/Capability-based_security
http://habitatchronicles.com/2017/05/what-are-capabilities/ (this is a great article)
etc...
Why not? Sometimes I want that. Just today I was looking at some code and realized that it was being used by groups that shouldn't. The code needs to be there and is great for the correct users, but someone is going around our architecture and we didn't notice it.
so while I agree that language integration is really useful, I think you can get a lot out of appropriate support in the runtime, most notably the library loader.
Imagine these changes to a language, making it "capability safe":
- There is a `Network` object, and it's the only way to access the network. Likewise, there's a `Filesystem` object, and it's the only way to access the file system.
- Code cannot construct a `Network` or `Filesystem` object. Like, you just can't, there's no constructor.
- The `main()` function is passed a `Network` object and a `Filesystem` object.
Consider the consequences of this. The log4j vulnerability involved a logging library doing network access. In a capability safe language, this could only happen if you passed a `Network` object to the logger, either in its constructor or in one of its methods. So the vulnerability is still possible. But! Now you can't be surprised that your logger was accessing the network because you gave it the network. And a logger asking for network access is sufficiently sketchy that log4j almost certainly would have made network access optional for the few users that wanted that feature, which would have prevented the vulnerability for everyone else!
I talked about there being a single monolithic `Filesystem` object. That is what would be passed into `main()`, but you should also be able to make finer grained capabilities out of it. For example, you should be able to use the `Filesystem` object to construct an object that has read/write access to a directory and its subdirectories. So if a function takes one of these as an argument, you know its not mucking around outside of the directory you gave it.
Capability safety within a language is stronger than the sort of capability safety you can get at the process level or at the level of multiple machines. But we want those too!
tomcat also gave different privilege levels to different parts of the classpath
nice idea, but didn't seem to work in practice
The real power will come when you can mix them. In one direction, that looks like `main()` not getting passed a `Network` object because the ".jar" wasn't granted network access. In the other direction, that looks like the system C library you invoked failing to access the network because when you invoked it from Java you failed to provide a `Network` object, so it was automatically sandboxed to not be able to access the network.
Emily took the approach of restricting a subset of OCaml, which they were running on Windows machines. No idea how tough it would be to get it running on a modern version.
Also for OCaml, the MirageOS unikernel is neat - develop on Linux etc., then recompile configured as a virtual machine with only the drivers (network, filesystem) needed by that one app. - https://mirage.io/
it wasn't, it was done at the type and classloader level
have a read about SecurityManager
Yeah, this is not only an effects type system, it's a static effects type system.
void effect:Network my_function_to_connect_to_server() {
...
}
I mean a type signature like this one: void my_function_to_connect_to_server(Network network) {
...
}
Where there's absolutely nothing special about the Network type, except that (i) it has no constructor, and (ii) nothing in the Java language lets you talk to a network without it. All of this is expressible with Java's type system today.See how there's the Network right there on the type?
void my_function_to_connect_to_server(
Network network,
int port,
String server_name
)
It's not categorically different than the other arguments, and doesn't require an effect type system.At this point I'm not sure if you're misunderstanding what I'm proposing, or if you don't know what an effect system is. If you care to communicate, you'll need to say something more substantial. Walk me through what you're trying to say.
But that thing you wrote is a static effect system. Having a singleton that you need to carry with the type doesn't make it not part of the type system.
1 - Or actually use Resource as a class. But Java wouldn't do this.
> But that thing you wrote is a static effect system.
The thing I wrote is expressible in Java's type system as it is today. So you're saying that Java has a static effect system?
Theseus OS is a research project that created single address space kernel that loads dynamic libraries written in safe-only Rust as "untrusted entities". Compiled with a trusted compiler that forbids unsafe. If they have no access to unsafe, and you're not giving them functions to link to that would hand them these a-capability-if-you-squint objects, they're supposedly sandboxed.
It is insane to me that in 2025 there is no easy way for me to run a program that, say, "can't touch the filesystem or network". As you say, even a few simple, very coarse grained categories of capabilities would be sufficient for 95% of cases.
https://www.freedesktop.org/software/systemd/man/latest/syst...
https://www.freedesktop.org/software/systemd/man/latest/syst...
sure, the command line get bit verbose but nothing that an alias or small wrapper couldn't solve
the big problem is that modern operating systems have huge surface area and applications tend to expect all sorts of things, so figuring out what you need to allow is often non-trivial
Well, OpenBSD has pledge(2) and unveil(2), both of which are very easy to use.
https://man.openbsd.org/pledge.2 https://man.openbsd.org/unveil.2
Like, I get it for a few things. But it is a short path to wanting access to files I have created for other things. Pictures and videos being obvious files that I likely want access to from applications that didn't create them.
Similarly, it is a short hop from "I don't want any other application to be able to control my browser" to "except for this accessibility application, and this password management application, and ..." As we push more and more to happen in the browser, it should be little surprise that we need more and more things to interact with said browser.
Maybe if rsync were better designed exploits could be better contained; alas, there was a recent whoopsiedoodle—an error, as Dijkstra would call them—and rsync can read from and write to a lot of files, do internet things, execute whatever programs. A great gift to attackers.
It may help if the tool does one thing and one thing well (e.g. the unix model, as opposed to the "I can't believe it's not bloat!"™ model common elsewhere) as then you can restrict, say, ping to only what it needs to do, and if some dark patterner wants to shove ads, metrics, and tracking into ls(1) how about a big fat Greek "no" for those network requests. It may also help if the tool is designed (like, say, OpenSSH) to be well partitioned, and not (like, say, rsync) to need the entire unix kitchen.
Image libraries have had quite a few CVE or whoopsiedoodles over the years, so there could be good arguments made to not allow those portions of the code access to the network and filesystem. Or how about a big heap of slow and expensive formal verification… what's that, someone with crap security stole all your market share? Oh, well. Maybe some other decade.
A non-zero number of people feel that "active content" e.g. the modern web is one of the worst security missteps made in the last few decades. At least flash was gotten rid of. So many CVE.
P.S. web browsers have always sucked at text editing, so this was typed up in vi yielding a file for w3m to read. No, w3m can't do much of anything besides internet and access a few narrow bits of the filesystem. So, for me, web browsers are very much in the "don't want to access the filesystem" category. I can also see arguments for them not having (direct) access to the network, to avoid mixing the "parse the bodge that is HTML and pray there are no exploits" with the "has access to the network" bits of the code, but I've been too lazy to write that as a replacement for w3m.
But to answer your question there are, eg, tons of programming packages in any language that I want purely for their computational abilities, and I know this for certain when using them. In fact for the vast majority of GUI programs I use, or programming packages I use, I know exactly what kind of permissions they need, and yet I cannot easily restrict them in those ways.
Now, I think your point on restricting the libraries that are imported to a program makes a ton of sense. I'm not entirely clear where I would want the "breaker box" of what an application is allowed to do to be located, but it is crazy how much just importing some library will do in many programs.
More broadly, creating a good UI around granting capabilities is non-trivial. But that's a separate problem from simply not being able to make even the most basic kinds of restrictions that you want in most cases.
Mostly fair that it would be good if we could say "on site foo.com, request for any access to not-foo.whatever that happens." I can't remember the last time I saw the sheer number of third party network accesses that happens on far too many sites. It was sobering.
Quite a few apps fall into this category: single player games, photo editors, word editors, video players, pdf editors ...
It seems very reasonable to restrict these applications from accessing the internet.
Any sort of editing software, though, gets tough. That is precisely the are that I have had bad experiences in in the past. Would try to edit raw photos and export them to a place I could draw or publish with them. Using a shadow banned application is the only way I know on how to describe how that felt.
As a random example, see this one ( https://www.welivesecurity.com/2017/04/19/turn-light-give-pa... ) which is a banking trojan cosplaying as a flashlight widget.
Now there is a more or less sophisticated permission system which users then bypass by still accepting any prompt if you promise them anything shiny...
I actually am less against these ideas on the phone. Quite the contrary, I think I'm largely agreed that more efforts need to be done to let people control those.
I am also sadly skeptical that this works, there. I've seen my family that is all too eager to just click "ok" on whatever an app says it needs. :(
Unfortunately, the performance is what suffers, and morse law hasn't kept up such that vm based OS can be used by the regular laymen.
Why does “ping” need to have file system access?
(More likely path for now: start a new sandbox, run things in it, put result files in an "outbox", quit sandbox, consume files from outbox. Also not very convenient with current tools.)
I get wanting "safe" computers. I'm not clear that we can technically define what legally "safe" means, though. :(
Now, i grant, we can probably get further than I would spit ball based on some bad interactions in the past.
> I get wanting "safe" computers. I'm not clear that we can technically define what legally "safe" means, though. :(
You are currently using a web browser. When you go to ycombinator, the site cannot read the contents of your email in the next tab. This isn't a shadow ban you on your own machine, it's just a reasonable restriction.
Imagine you just installed a new web browser (or pdf reader, tax software, video game, ...). It should not be able to read and send all the pictures in your camera roll to a third party.
But I use my web browser to upload my photos to the cloud, so it absolutely should.
(I do somewhat agree with the general point, but I find it very funny that your very first example would break my workflow, and I do think that highlights the problem with trying to sandbox general-purpose programs)
If it needs more, there is always "access all photos" permission, and "access all files" too.. but this is explicit and requires user prompt. And the last part ia very important - if freshly installed browser requires full files access without explanation, this is likely for spyware, so uninstall it and leave bad review.
I'm open to the idea that some people are locked down such that they can't install things. And, that makes a lot of sense. You can have a relationship that is basically, "I trust them with access to data running this closed set of applications." Managing system configurations makes a ton of sense.
But, as soon as you have full trust of system management on a group, you start getting in odd worlds where you want to allow them to have full access, but want to stop unauthorized use. Which, we don't have a way to distinguish use from access for most data.
You _can_ fully vet apps, each and every one. Or you can choose a zero-trust approach and only vet the apps where it's necessary to extend trust.
Something dangerous like ffmpeg would be better if the codecs were running without access to files or the network, although you'd need a not fully sandboxed process to load the media in the first place.
Many things do need file access, but could work well with an already opened fd, rather than having to open things themselves (although forcing that results in terrible UX).
Of course, filesystem access gets tricky because of dynamic loading, but lets pretend away that for now.
Deno took a step in a good direction, but it was an opportunity to go much further and meaningfully address the problem, so I was a bit disappointed that it just controlled restrictions at the process level.
That is simply not true. There are many ways to do that, which have been answered already. SELinux. Seccommp profiles. AppArmor. Linux containers (whether that be OCI, bubblewrap, snap, app images, or systemd-run). Pledge and jails.
These are different concerns. One is software developers wanting to code into their programs upper limits to what imported dependencies can do. That is poorly supported and mostly not possible outside of research systems. The other is end users and system administrators setting limits on what resources running processes can access and what system calls they can make. That is widely supported with hundreds of ways to do it and the main reasons it is perceived as complicated is because software usually assumes it can do anything, doesn't tell you what it needs, and trying to figure it out as an end user is an endless game of playing whack-a-mole with broken installs.
Qubes OS exists for more than 10 years already. My daily driver, can't recommend it enough.
https://en.m.wikipedia.org/wiki/Security-Enhanced_Linux
To enable this at the programming level would require an enforcement mechanism at the level of a language VM or OS. It would require more overhead to enforce at that level, but the safety benefits within a language may be worth it.
Or even, for that matter, some sane capabilities for browser extensions.
Half jokes aside, I'd love to be able to have a decent mobile style permissions experience on browser extensions and desktop apps.
"These libraries can not access the network." No. "These libraries have not been given access to the network (and by default none are given access)."
From an implementation perspective, this is just passing in access rights as "local" resources instead of using "global" resources. For instance, it is self-evident that other code can not use your B-Tree local variable if you did not pass a reference to it to any called functions (assuming no arbitrary pointer casts). You just do the same with these "resources". It is just passing things to functions instead of relying on globals. The only difficulty is making these actions/resources "passable", which is trivial at the language-level, and "fine-grained/divisible" to avoid over-granting.
The neat thing about this particular problem is that you can do some really coarse things and get some immediate benefit. Capabilities in their original form, and perhaps their truest form, carry down the call stack, so that code can do things like "restrict everything that I call can only append to files in this specific subtree" in the most sophisticated implementations. But you could do something more coarse with libraries and just do things like "These libraries can not access the network", and get big wins on some simple assertions. If you're a library for turning jpegs into their pixels, you don't need network access, and with only a bit more work, you shouldn't even need filesystem access (get passed in files if you need them, but no ability to spontaneously create files).
This would not be a complete solution, or perhaps even an adequate solution, but it would be a great bang-for-the-buck solution, and a great way to step into that world and immediately get benefits without requiring the entire world to immediately rewrite everything to use granular capabilities everywhere.