Open rsp opened 6 years ago
By the way, commenting the "Defaults to read-only file system access and no network access" privileges, I would love it to default to no unrestricted file system access (even read-only!), so the incidents like a recent ESLint trojan from few days ago could be eliminated (see eslint-scope issue #39: Virus in eslint-scope? and article: ESLint: Postmortem for Malicious Packages Published on July 12th, 2018).
I think that a linter should not have access to any other part of the file system than the source code that it actually needs to lint. Ideally, I would love it to have access only to the files that an external program that runs the linter allows it to have access to - it's easy for a small number of files (e.g. linter run as linter file.js
needs read access to the file system but a linter run as linter < file.js
doesn't) but gets more complicated (without things like chroot) for larger file trees of course.
If you want safe and very fine-grained capability handling (where a capability is only usable by Javascript with a direct reference to it, and where you could safely have untrusted code within the same Javascript realm as code that handles capabilities), then you'll need Secure EcmaScript or Frozen Realms (a standard proposal inspired by SES). I don't think it would be a good idea for Deno to execute in SES-mode or a frozen realm by default since it's un-weblike, causes certain incompatibilities, and the user needs to take special care with their javascript in order to continue to stay safe (like by not passing mutable objects around).
A more coarse-grained system could be accomplished without any special Deno support once Realms (which start out with no capabilities) are standardized. Realms must have capabilities passed in from another Realm. The default Realm will start out with all of the capabilities (well, all that are specified by the command-line arguments; these capabilities would be exposed in a global object or a special import only available from the default Realm), and it can easily create new Realms and choose to pass capabilities into them. (It could even create an SES-like Frozen Realm and pass the capabilities into there, and then that Frozen Realm can safely contain both trusted and untrusted code.) The only things to be done are to push the Realms standard along, and to establish conventions of using it often.
@ry is this something to consider given current --allow-*
flags?
Few months after this issue was posted, the start of a new standardization effort of WASI (the WebAssembly system interface) was announced on Mozilla Hacks.
WASI is using a capability-based security model to give access to the system resources to WASM modules.
Since Deno supports WebAssembly, it may be useful in the future to support WASI as well, to expose some of the system resources handled by Deno to WASM modules, plus also it's useful to know how WASI solves some of the things that Deno is solving in a different way.
See:
The WASI design principles document is not published yet but available in a PR WebAssembly/WASI#192 - it explain the principles quite well, and this is pretty much what I meant when I originally posted this PR #378, though I didn't know about the idea of WASI back then:
WASI Design Principles
Capability-based security
WASI is built using capability-based security principles. Access to external resources is always represented by handles, which are special values that are unforgeable, meaning there's no way to coerce an arbitrary integer or other type of value into a handle. WASI is also aiming to have no ambient authorities, meaning that there should be no way to request a handle purely by providing a string or other user-controlled identifier providing the name of a resource. With these two properties, the only ways to obtain access to resources are to be explicitly given handles, or to perform operations on handles which return new handles.
Note that this is a different sense of "capability" than Linux capabilities or the withdrawn POSIX capabilities, which are per-process rather than per-resource.
The simplest representation of handles are values of reference type. References in wasm are inherently unforgeable, so they can represent handles directly.
[...] (more)
The module attributes proposal currently making its way through TC39 gives a syntactic space for us to pass capabilities at import sites. In #8, I list capabilities among the use cases for the feature.
The module attributes proposal currently making its way through TC39 gives a syntactic space for us to pass capabilities at import sites. In #8, I list capabilities among the use cases for the feature.
What was known as "module attributes proposal" later became the "import assertions proposal" which required that import assertions could not affect how a module was parsed or some such. This would've forbidden the here-proposed capability-based security model from using that syntactic space.
The proposal has recently been demoted back to Stage 2 because browsers could not align to the "assert only" requirement. This requirement will be relaxed and the proposal re-evaluated. It is quite possible that the syntactic space and spec definitions will open up for Deno to use in eg. permissions and capability passing.
I don't know if anyone here has considered using a capability-based security model in Deno but when I read this paragraph in the Readme I thought that such a model would be perfect for this sort of thing:
It is already based on message passing and a possibility to drop privileges may be needed in the future (e.g. a program runs with network access and wants to run a module but without giving it network access).
There have been some interesting operating systems based on capabilities (search for GNOSIS, KeyKOS, EROS, CapROS and Coyotos for good info) where some complexities were needed to model entire file system and user privileges with capabilities, which didn't map well to the traditional Unix permission model.
But here as I understand all we need is to make sure which privileged code an unprivileged code can run, and then let the OS do the rest of permission checking so most of the complexities that are inherent to designing a security model for an operating system are not relevant here.
We can start a program with certain capabilites (like the read-only file system access by default, plus some additional ones turned on by the command-line switches if needed) and those capabilities could be passed (or not passed, which is really most important here) to certain modules used by the main program. Those modules running as unprivileged code (V8) would pass the capabilities along the serialized messages to the privileged code (previously Golang, now Rust) and the privileged code would either allow or reject such a request to a system call or other funcionality that requires a certain set of capabilities.
Since Deno is already using message passing between unprivileged and privileged code, and since the idea to disallow certain permissions from the running code seems to be widely supported, then maybe a capability-based security model would work well here.
Some resources: