← Back to context

Comment by zdragnar

9 hours ago

What is a capability in terms of programming language design? It sounds more like the sort of thing that would belong at the standard library level, where builtin APIs are guarded by flags.

Deno has something vaguely built in with permissions flags, and old school Blackberry (at least in the J2ME days) had permissions settings for almost everything that an app could do, but again, those are all external to the language design itself.

If you want a fun read that might educate via entertaining I would read Satan Comes to Dinner by Douglas Crockford https://www.crockford.com/ec/dining.html

I wrote a bunch more, but I have a habit to verbosity when capabilities come in which I should attempt to combat, and have deleted it. Crockford says these things better than I can anyhow.

In this context, a capability is something that allows the code, or the transitive closure of the code that it may call, to access some particular function, to put it very briefly. So you could have a single function that, if accessed in one manner, is permitted to read from the directory /tmp/blahblah, but accessed in another manner, is permitted to read from the directory /home/zdragnar/.config/myprogram, and it is guaranteed by the language and runtime that the function will never do anything else on the file system. Or, even more importantly, it can be guaranteed that "from this code, nothing, no matter how the code is arranged, can access the file system at all".

This has massive overlap with a lot of things, like capabilities as implemented by Linux, effects systems, monadic data types as a not-really-very-good capabilities system (Haskellers have been playing with this for years and nobody really loves this approach, many practical problems beyond the scope of this message that would affect any language that tries that approach), dependently-typed programming, and so forth.

It is not a flag, though; flags can't handle that "transitive environment" aspect. It is also granular on the level of the programming language. This would allow you to do things like have your program be given access to a given part of the mobile file system using the mobile OS' permissions, but you could know beyond a shadow of a doubt that the image library you are using can not at any point access the file system, no matter what changes the author makes to it, because you can just look at the capabilities given to the image library and see that file system access is not among them. This is where real opportunity is over the next few years, in my opinion, because supply chain attacks are going to continue to get worse. A neat aspect of this approach is that it makes huge swathes of the ecosystem unattractive targets by statically ensuring that they can't sneak anything in to something that doesn't need file system or network access, so hackers won't even attack those libraries. Thus the ecosystem can concentrate on monitoring just the high-touch libraries that need to access high-risk resources.

(I should make it clear that the image parsing libraries can be passed a file; what I am saying is that they can't spontaneously originate arbitrary file system access in a system like this. Really what they would get is probably a "stream" and they would be forbidden from poking into the stream to see what it is made of, at which point, if some other code handed it a file presumably it meant to do that, but it does not give the image library any ability to do anything else with the filesystem.)

Moreover, if such a benefit was available, that would tend to have people squeeze down those dependencies as much as possible too, e.g., the aforementioned image library. You don't need file system access to parse images, that's just some convenience functions easily worked around that are provided because why not? The number of things that truly need direct high-risk access can actually be surprisingly small, and often, the application can also easily scope the permissions down quite tightly so the HTTP request library is limited in what it can hit, etc.

We actually have some semi-decent stabs at capabilities at the OS level; we can quibble with them but they are there. But inside an OS process, broadly speaking, anything can do anything in the vast majority of programming languages. The only way to be sure that the string concatenation function doesn't start crawling your file system looking for crypto keys is to examine the code, most languages have no ability to tell it that it can't. There are exceptions, like the aforementioned Haskell, that have at least some ability to do this, but this is an HN post, not a complete guide to a major topic. Really this is more about loading the reader up with keywords they can hit Google or an AI with.

The term is overloaded, too; Pony has something it calls "capabilities" but it really resembles more a sort of response to Rust's borrow checker, and if there is a way to lift it into this style of capabilities coherently it isn't clear to me. And even if you did, the entire rest of the ecosystem wouldn't support it, which is one of the reasons why this has to be a new language. You can't bodge this on to the side of an existing language.

(Plus, IMHO, there are some other ideas this may shake loose. Programming languages seem to be in a rut right now. My crack in my previous message about sum types and such isn't really about those things but the way almost every language going by is just a respelling of previous languages, churning over some other iteration of "The Perfect 2015 Language" that is already covered by any number of existing projects. I don't know that there's a lot of room there anymore. We need something big. Once you try something big the design will inevitably lead to other interesting things nobody else is trying either. Capabilities is one distinct possibility... like I said, if you dig in to the history you will discover there are entire huge segments of the capabilities space that haven't even been tried. If nothing else, if you are a PL nerd, I guarantee it'll be fun to explore those spaces that almost nobody has covered. No criticism intended to those who have, who have done a good job. It just hasn't been enough people and enough exploration to truly map the space.)

  • I've read this a few times, and it still sounds like you're trying to re-invent an operating system. You describe things like "access the file system" but what even is that? In Linux, if you write to a "file" in /proc or /sys, you can trigger all sorts of side effects, including cause a kernel panic.

    I can imagine all sorts of ways to "color" functions by what capabilities they use, and thus statically analyzing the program to assert what it does, but again, all of the core capabilities would be provided by the standard library / builtin functions, so it seems to not add much to the actual design of the language beyond ensuring all control flow, redirection and reflection can be statically analyzed.

    • >You describe things like "access the file system" but what even is that? In Linux, if you write to a "file" in /proc or /sys, you can trigger all sorts of side effects, including cause a kernel panic.

      well, you can could of course go ahead and read some of the literature on capabilities based security, but you don't seem interested in that, so I guess I will give a go.

      Here I will give a not necessarily great description of capability based security where the capabilities are handled on the OS level, this is my poorly remembered version of a document read about 20 years ago.

      In Windows if you, as an attacker, are somehow able to do a complicated attack on any program that a user runs and it succeeds you can make that program do anything that the user has access to do. For example make it write to the local filesystem. For a large number of programs writing to the local filesystem does not make any sense and they were not written to do that, but now that the program has been corrupted by your attack it will write to the filesystem.

      But let us suppose our program, Solitaire, was written with a capabilities based language then in the manifest for the program when we installed it it would not give access to the file system. The capabilities system can in fact be very explicit, and you could easily have a program be explicitly limited to only those abilities that are needed to run your solitaire game, but instead of going on at length on this and the abilities it confers to do interesting things with programs because you have actually enhanced security I will just focus on the filesystem.

      When the attacker attacks Solitaire that the user is running and corrupts it, it no longer has just the security model of old Windows to worry about (this program can do anything the user can, like write to the user's contacts list, send emails as the user, format the hard drive, whatever) but also the security model, enforced at the OS level, of what kind of thing the program can do. Since Solitaire was written with the limited number of capabilities needed for doing a solitaire type game it can't do any of those things the user can unless, as well as attacking the solitaire game and gaining control of that, you are also able to attack the OS level capabilities administrator and get it to give your pwned executable - Solitaire - the capabilities you explicitly want it to have.

      This is one example of how capabilities based security differs from Access based capabilities where if your solitaire game is allowed to if you write to a "file" in /proc or /sys, you can trigger all sorts of side effects, including cause a kernel panic. (here I have switched from solitaire in Windows to a similar game in Linux but I assume you can see the same princiles hold true)

      I earlier mentioned this document Satan Comes to Dinner by Douglas Crockford https://www.crockford.com/ec/dining.html, it shows some other examples of how a capability based security model can be used to defend against trust based attacks in shared resource systems etc.

      This is a more computer science based description than the one I gave and more abstract than my example of someone attacking Solitaire in order to write to a file somewhere, but in being more abstract also clearly applicable to many other forms of threats than my very specific (and thus unlikely) example is. Also it is by Douglas Crockford and quite a bit more entertaining than what I wrote.