The Dev Insights
The Dev Insights
My New Favourite Way to Keep Code Safe
Back to Blog
Web DevelopmentOctober 21, 202511 min read

My New Favourite Way to Keep Code Safe

Web DevelopmentOctober 21, 202511 min read

Ever felt like containers are overkill for keeping your code secure? There's a new open-source tool that's making waves, and it's super simple.

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:

bash
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.

typescript
// 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:

bash
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.

json
// .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.

bash
# From the sandbox-runtime directory
npm start -- /home/youruser/my-sandbox-test/.sandbox.json

You should see output like:

code
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:

json
// .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.

  • Paths, Paths, Paths: Honestly, the biggest headache for me was getting the paths wrong in 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.
  • Network Specifics: Forgetting the port number, or using 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".
  • Forgetting 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.
  • Not Building TypeScript: If you're using 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

    TOPICS

    Web Development

    Share This Article

    Related Articles

    Archives

    October 202546
    T

    The Dev Insights Team

    Author