Currently allows you to specify allowed resources during the package installation in a way very similar to what you've outlined [1].
The sandbox itself lives here [2] and can be integrated into other projects.
1. https://github.com/phylum-dev/cli/blob/main/extensions/npm/P...
[0]: https://deno.land/[email protected]/getting_started/permissions
Good luck with the project. I hope it delivers, because I’d love to use it. Signed up for the mailing list.
Deno is a really cool project, imo.
Though, like scopes, I think many times packages would need broad access, but maybe not?
I suspect you may run into similar sandbox escapes once things are complicated enough. So it seems like a good idea if they can be made bug free, but good luck with that?
But, yes, we've tried this idea many times now, and it never held up for long.
1. `autobox` (to be renamed lol) [0]. It's basically a Rust interpreter that performs taint and effect analysis, reporting on both, allowing you to use that information to generate sandboxes. ie: "autobox sees you used the string '~/.config' to read a file, and that is all the IO performed, so that is all the IO you get".
2. I'm working on a container based `cargo` with `riff` built in that aims to work for the vast majority of projects and sandbox your build with a defined threat model.
The goal is to be able to basically `alias cargo=cargo-sandboxed` and have the same experience but with a restricted container environment + better auditing of things happening in the container.
3. I previously built a POC of a `Sandbox.toml` and `Sandbox.lock` with a policy language that allowed you to specify a policy for a given build step. Unfortunately, I couldn't decide on how I wanted it to work in terms of "do I generate a single sandbox for the entire build, or do I run each build stage in its own sandbox" - there are tradeoffs for both.
Here's a lil snippet:
[build-permissions.file-system]
// All paths are relative to the project directory unless they start with `/`
"../" = {permissions = ["read"]}
// "$target" being a special path
"$target" = {permissions = ["read", "write"]}
// Source this path from the environment at build time, `optional` means it's
// ok if it isn't available
"$env::PROTOC_PATH" = {permissions = ["read", "execute"], optional=true}
// Default protobuf installation paths, via regex
"^(/usr)?/bin/protoc" = {permissions = ["read", "execute"], regex=true}
Once I'm done with (2) though I think I'll tackle (3).`autobox` is fun but I think it may be impractical without more language level support and no matter what I'd end up having to implement it in the compiler at some point, which means it would be unusable without nightly or a fork.
I'm going to try to wrap up an autobox POC that handles branching and loops, publish it, and see if someone who does more compilery things is willing to pick it up. As for (2) and (3) I believe I can build practical implementations for both.
(a) Discourage any future use of ">=" in version dependencies. Specify an exact version. That way a future compromised version doesn't get pulled
(b) Every build system needs better ways of having multiple versions of a same dependency coexist. I should be able to have one of my project's dependencies depend on "numpy==1.15" and another dependency depend on "numpy==1.16" and they should be able to coexist in the SAME environment and "see" exactly the numpy versions they requested.
For python we should think about how to support something like this in the future:
import numpy==1.15
and have it just work.That way if a hacker compromises PyPI and releases a malicious numpy 1.19 it won't get pulled in accidentally.
Here's a bit of a joke I made before that might be an interesting starting point, though since it uses virtualenv behind the hood it doesn't have a way for multiple versions of one package to exist. I don't think it's impossible to do though with some additional work.
https://github.com/dheera/magicimport.py
Sample code:
from magicimport import magicimport
tornado = magicimport("tornado", version = "4.5")foo() becomes v1_15_4_foo() automatically
Now I see what people at one respectable, big project were thinking when they allowed 7 different versions of OpenSSL to be statically linked to the same executable…
Seriously, this idea may save you from some not very interesting work, but it will create the need of much bigger amount of work which while potentially interesting is not very productive. You are toying with exponential growth here, like a chain reaction - like a bomb.
It does exactly that (although on a per-process basis).
I don't think this kind of permission system can be retrofitted into an existing language without direct OS support, and probably not at the library level (you'd need something like per-page permissions which would get hairy real fast).
You've described OpenBSD in general. I recommend a deeper dive - it's fantastically refreshing, how simple yet functional an OS can be.
I often use the FreeBSD Handbook as an example of first-party documentation done right, and that's only possible because of deliberately limited "churn for churn's sake". The kinds of regular code rot and attrition that Linux suffers just does not take place on BSD systems because if you contribute something new you're expected to make your thing mesh with the ecosystem, they don't tolerate the "I'm gonna churn everyone's environments because my version is 10% better than the existing one" that tends to take place on Linux.
As an example, the amount of init systems that Ubuntu has gone through over the last 20 years is completely insane by BSD standards. They've gone from sysvinit to upstart to systemd. You don't have people ripping out the graphics and audio subsystems and doing total rewrites, either. It's nuts that a lot of Linux people don't even realize that it doesn't have to be that way - that's not "just how maintenance is supposed to work", that's a bunch of incredibly bad behavior on the parts of distros and Poettering specifically that has become highly normalized and overlooked. Maintenance shouldn't be breaking things like that. "we never break userland" doesn't have to be a suicide pact either - BSD has actually maintained a much more stable ABI than the linux kernel without crystallizing a bunch of shitty bug-compatibility stuff like Linux is doing either. It's all insanely stable and competent and professional by linux standards.
If I was developing a product for true long-term support, with the minimum possible engineering effort devoted to fighting churn, BSD would be at the top of the list.
I'm and the average Joe around me, totally far from Netflix's task of packing bytes from disk to network. Simple 2vCPU VPS serving 4GBit without being saturated on system resource level is quite often much more than enough. Extra note - it's not even using kTLS.
Moreover, even for Netflix, noting they know FreeBSD in and out, do you think/have info on using FreeBSD as base OS beyond distribution level - running applications/services in particular?
I've quickly checked on their repos like https://github.com/Netflix/conductor and it smells like they use containers/Docker, which doesn't work on FreeBSD => I'm in very much doubts it's OS of choice for them.
If you read "rolling stones gather no moss" to mean "keep moving or risk becoming obsolete", choose Linux. If you understand it as "change for change's sake prevents the achievement of mastery", go with a BSD.
But it gets better. If you build Python in the Cosmopolitan Libc repository:
git clone https://github.com/jart/cosmopolitan
cd cosmopolitan
build/bootstrap/make.com -j8 o//third_party/python/python.com
Then you can use cosmo.pledge() directly from Python. $ o//third_party/python/python.com
Python 3.6.14+ (Actually Portable Python) [GCC 9.2.0] on cosmo
Type "help", "copyright", "credits" or "license" for more information.
>>: import cosmo, socket
>>: cosmo.pledge('stdio rpath wpath tty', None)
>>: print('hi')
hi
>>: socket.socket()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/zip/.python/socket.py", line 144, in __init__
_socket.socket.__init__(self, family, type, proto, fileno)
PermissionError: [Errno 1] EPERM/1/Operation not permitted
Since we didn't put "inet" in the pledge, you can now be certain your PyPi deps aren't spying on you or uploading your bitcoin wallet to the cloud. You can even use os.fork() to rapidly put each dependency in its own process, then call cosmo.pledge() afterwards to grant each component of your app its own maximally restrictive policy.Cosmopolitan Python also ports OpenBSD's unveil() system call to Linux too. For example, to disallow all file system access, just call cosmo.unveil(None, None). You need a very recent version of Linux though. For instance, I use unveil() in production on GCE but I had to apt install linux-image-5.18.0-0.deb11.4-cloud-amd64 in order for Landlock LSM to be available to use unveil().
That is, you can protect your own program from doing network stuff because of incorrect input, but you can't use it to sandbox another program.
See this thread: https://marc.info/?t=162367803300003&r=1&w=2 and this mail about sandboxing: https://marc.info/?l=openbsd-tech&m=162367954705721&w=2
If you own a package at version 1.X.X and you want to add a permission requirement you have to bump the version to 2.0.0. If you also allow people to opt in to a less strict "auto allow all the currently required permissions for these dependencies" mode, they would at least know for sure nothing can touch anything new unless they explicitly bump the major version.
If you're extra concerned about security you can explicitly specify them so it's really obvious when a major version bump adds new ones, but it removes some of the friction.
Anyway I'll leave this from 2013 here:
> After having work during 3 years on a pysandbox project to sandbox untrusted code, I now reached a point where I am convinced that pysandbox is broken by design. Different developers tried to convinced me before that pysandbox design is unsafe, but I had to experience it myself to be convineced.
> It would also be nice to help developers looking for a sandbox for their application. Please tell me if you know sandbox projects for Python so I can redirect users of pysandbox to a safer solution. I already know PyPy sandbox.
-- https://mail.python.org/pipermail/python-dev/2013-November/1...
const apollo = require('apollo-client', {fetch});
const stringutils = require('stringutils');
That way, you can pass in at a very fine-grained level any object you want to, including things like `fetch` and `fs`. But then, you could just as easily pass in a custom `loggedFetch` module like so, that wraps `fetch` and logs all network requests made: const apollo = require('apollo-client', {fetch: loggedFetch});
This is the object-capability security model, and as a sibling commenter pointed out, LavaMoat basically does this for JavaScript dependencies. (This is also basically just lexical scope, by the way, if your language is strict enough - though most unfortunately aren't.)Also- tangential but I really wish people would stop using require and use ESM whenever possible. I cringe when I see `require` just as much as when I see `var` being used in 2022, but I’m not certain there’s never a good reason not to use ESM.
var bar = require("bar", {baz: loggingBaz})
var foo = require("foo", {bar})
This is true even if you never otherwise use bar.This is certainly how peer dependencies work in nix flakes, for instance, where you can say something like
bar = //....
bar.inputs.baz.follows = loggingBaz;
foo = //....
foo.inputs.bar.follows = bar;
Since nix flakes is a package/dependency manager, it generates a lockfile that you could inspect too see what all the dependencies are, and make sure you got it right (oh, I still have two bar's, I must have forgotten to override some other dep). I suppose any compiler that involves a linking stage would in principle be able to generate some comparable output at the language level.(Apologies for using require and var, but I'm convinced the functional syntax is semantically clearer in this context.)
A if a language wants to deal in library's security it should strive to make static analysis possible. Eg: the language guarantees that network and filesystem calls can only be done with a single function, statically so I can audit that leftpad indeed doesn't make network calls .
In the end they became yet another attack vector, and now everyone should use OS security services instead.
Example of defining project dependencies:
It would probably require the language to limit monkey-patching core primitives (such as Object.prototype in javascript), and it would be more cumbersome for the developer to define the permissions it gives to each dependency. These required permissions could be listed on the package site (eg npm or PyPI) and the developer would just copy paste the permissions when adding the dependency. But if you upgrade a dependency version and it now requires a permission that seems suspicious (eg "stringutils" needing "filesystem"), it would prompt the developer to stop and investigate, or if it seems justified add the permission to "access" list.