You know that feeling when you're trying to run some code – maybe a plugin or a script from an unknown source – and you get that little tickle of worry? Like, what if it tries to snoop around your files? Or worse, connect to some dodgy server? I mean, we all want to be secure, right?
For ages, people have been saying, "just use a container!" And yeah, tools like Docker are brilliant. They're like these super tough, isolated boxes for your code. But sometimes, they feel a bit like bringing a tank to a snowball fight. They can be a bit heavy, a bit much to set up, especially if you just want to put a tiny, specific lock on something.
Well, turns out, there's a new kid on the block that caught my eye. It's called sandbox-runtime
, and it's been buzzing on GitHub – even though it's super new, it's already trending. It's an open-source
project, and it's all about sandboxing
your code in a really lightweight way. I've been playing with it for about 2 weeks now, and honestly, it's a game-changer for specific situations. Especially for us in webdev
, where we're often dealing with third-party tools or user-generated content.
What is this sandbox-runtime
thing, really?
Imagine you have a toddler who just loves to play with mud. You want them to have fun, but you don't want them tracking mud all over your nice clean carpet. So, you set up a little playpen in the garden. They can play, they can get messy, but they can't get out of that specific spot. That's pretty much what sandbox-runtime
does for your code.
It lets you put rules
on what a program can do, right at the OS level
. We're talking about things like which files it can read, which files it can write, and what network connections it's allowed to make. And the best part? No big, clunky container
needed. It's just a tiny tool that wraps around your existing programs.
I mean, think about it. What if some innocent-looking javascript
utility you pulled in for your frontend
build accidentally tried to phone home? Or, worse, an AI
model running some RAG
process decided to mess with your system files? We've all heard stories about a simple typo leading to a massive AWS outage
for some company. This kind of tool could be your first line of defence.
Okay, I'm In! What Do I Need?
This isn't rocket science, but there are a few bits and bobs you'll want to have ready. Don't worry, it's mostly standard webdev
stuff.
Prerequisites (the basics):
* A Linux System: This tool works its magic deep inside the operating system, so you'll need a Linux computer or environment. Ubuntu, Fedora, whatever your favourite
flavour is – it should work. I've been running it on a fresh Ubuntu 22.04 virtual machine without any trouble.
* Command Line Chops: You don't need to be a terminal wizard, but knowing your way around basic commands like cd
, ls
, and git clone
will make life easier.
* Node.js: Since we're in webdev
and the project uses TypeScript
, having Node.js installed is super helpful. I'm currently using Node 20.9.0
with npm 10.1.0
.
Tools You'll Need (the specific stuff):
* sandbox-runtime
: Obviously! We'll grab this directly from GitHub.
* git
: To download the project. Any fairly new version will do; I'm on git 2.34.1
.
* TypeScript
(optional but recommended): The project itself is written in TypeScript. If you're writing your own scripts to put in the sandbox, it's a great choice because it helps catch mistakes early. You can install it globally with npm install -g typescript
or just for your project.
Let's Get Our Hands Dirty: Step-by-Step
Alright, grab a coffee. We're going to set up a quick example. Honestly, this took me about 20 minutes to get working the first time, and that included me fumbling around for the right commands.
Step 1: Get sandbox-runtime
on Your Machine
First, you need to get the tool itself. Head over to its GitHub page, or just run these commands:
git clone https://github.com/your-username/sandbox-runtime.git # Replace with actual repo URL when available
cd sandbox-runtime
npm install
npm run build
Pro tip: That npm run build
command takes the TypeScript code and turns it into regular JavaScript that Node can actually run. If you're curious about TypeScript, I wrote about it in Root Drawings That Changed How I See Code – it made me think about how code should be separated in a whole new way.
Step 2: Write a "Naughty" Script
Let's create a simple Node.js script that tries to do some things we don't want it to do. Create a file called naughty.ts
(or naughty.js
if you skip TypeScript) in a new directory somewhere, like ~/my-sandbox-test
.
// naughty.ts
import * as fs from 'fs';
import * as http from 'http';
console.log('Trying to be naughty...');
// Try to read a sensitive system file
try {
const passwd = fs.readFileSync('/etc/passwd', 'utf8');
console.log('Accessed /etc/passwd! Oh no!', passwd.slice(0, 50) + '...');
} catch (error: any) {
console.log('Could not access /etc/passwd:', error.message);
}
// Try to make an external network request
http.get('http://example.com', (res) => {
console.log('Connected to example.com! Status:', res.statusCode);
res.resume();
}).on('error', (e) => {
console.log('Could not connect to example.com:', e.message);
});
// Try to write a file outside our directory
try {
fs.writeFileSync('/tmp/malicious.txt', 'I am here!', 'utf8');
console.log('Wrote to /tmp/malicious.txt!');
} catch (error: any) {
console.log('Could not write to /tmp/malicious.txt:', error.message);
}
console.log('Naughty script finished trying.');
Now, compile it if you're using TypeScript:
npx tsc naughty.ts
This will give you naughty.js
.
Step 3: Define Your Sandbox Rules
This is where the magic happens. In the same directory as naughty.js
, create a file called .sandbox.json
. This file tells sandbox-runtime
what your script is allowed to do.
Let's start super strict. We'll allow almost nothing.
// .sandbox.json
{
"command": ["node", "naughty.js"],
"filesystem": {
"allow-read": [],
"allow-write": []
},
"network": {
"allow-connect": [],
"allow-listen": []
},
"environment": {
"allow-env": []
}
}
See how allow-read
, allow-write
, allow-connect
, and allow-listen
are all empty? That means your script can't touch any files, make any network connections, or even see environment variables, except for the absolute minimum Node.js needs to even run.
Step 4: Run It Sandboxed and Watch It Fail (Safely!)
Now, from the sandbox-runtime
directory (where you cloned it in Step 1), you'll run your naughty.js
script inside the sandbox. Make sure you adjust the path to your my-sandbox-test
directory.
# From the sandbox-runtime directory
npm start -- /home/youruser/my-sandbox-test/.sandbox.json
You should see output like:
Trying to be naughty...
Could not access /etc/passwd: EACCES: permission denied, open '/etc/passwd'
Could not connect to example.com: getaddrinfo ENOTFOUND example.com
Could not write to /tmp/malicious.txt: EACCES: permission denied, open '/tmp/malicious.txt'
Naughty script finished trying.
Awesome! The sandbox totally stopped your script from doing anything it shouldn't. It couldn't read /etc/passwd
, couldn't connect to example.com
, and couldn't write to /tmp
. That's exactly what we wanted!
Step 5: Loosen the Reins (Just a Little)
What if your script needs to read a specific file, or make a network request to your own API? You can specify that in .sandbox.json
.
Let's say naughty.js
needed to read a configuration file called config.txt
in its own directory, and connect to localhost:3000
.
Modify your .sandbox.json
like this:
// .sandbox.json
{
"command": ["node", "naughty.js"],
"filesystem": {
"allow-read": ["./config.txt"], // Allow reading config.txt in the script's directory
"allow-write": []
},
"network": {
"allow-connect": ["127.0.0.1:3000"], // Allow connecting to localhost port 3000
"allow-listen": []
},
"environment": {
"allow-env": []
}
}
Now, if you ran a naughty.js
that did try to read config.txt
or connect to localhost:3000
, it would actually work (as long as config.txt
is there and something's listening on localhost:3000
). But anything else? Still blocked. See? You get super specific control!
Oops! Common Mistakes I Made (So You Don't Have To)
Believe me, I messed this up at first. Took me forever – about 3 hours – to figure out one particular issue.
filesystem.allow-read
or allow-write
. I kept hitting ENOENT
errors (that means "Error No Entry" or file not found) until I realised my relative paths were off, or I just forgot to list all the folders I needed. Remember, paths are either relative to where you run sandbox-runtime
from, or you can use full, absolute paths.localhost
when my computer decided to use ::1
(that's IPv6) but I'd only allowed 127.0.0.1
(which is IPv4). Be super specific! "127.0.0.1:3000"
is way safer than just "localhost"
.sudo
(or not needing it): sandbox-runtime
itself usually doesn't need sudo
to start the sandbox. But if the script inside the sandbox tries to do something special (like writing to /etc/
), it'll still fail even if sandbox-runtime
said it was okay. That's because the computer's own security rules step in. Think of it as an extra layer of defence, not a magic key to get root access.TypeScript
, you have to compile your .ts
files into .js
before Node can actually run them. I've definitely forgotten npx tsc my-script.ts
more times than I care to admit before trying to run node my-script.ts
.When Things Go Sideways: Troubleshooting
Sometimes, even with the best intentions, things don't work as expected. Here's what I usually check:
* Read the Error Messages: Seriously, they tell you so much. EACCES
almost always means you don't have permission – either from the sandbox or your computer itself. ENOTFOUND
usually means there's a problem finding a network address or the connection is blocked.
*Double-Check `.sandbox