Weblog

I try to share things that are interesting to me. This may include technology, personal projects, living in New York City; etc.


Link Blogging

May 30, 2025

Ref: “My approach to running a link blog” by Simon Willison

I wanted to start link blogging! I’ve been very inspired by Simon Willison the past few years—and once I’ve gotten past having Something Important to say, a link blog seems like a good way to lower the stakes of making & sharing something on my website.

This is the main tenant:

I try to add something extra. My goal with any link blog post is that if you read both my post and the source material you’ll have an enhanced experience over if you read just the source material itself.

I’ve backfilled a few entries (and will more-so soon), and installed RSS if that’s your jam. Let me know what you think!


Gamejam, SUB-SURFACE!

April 11, 2025

SUB-SURFACE

I helped make a game—SUB-SURFACE! A gamejam one, anyway—every year or so I end up doing a gamejam with a few friends.

Who worked on it:

Austin Breed (art & animation), Jeff Lee (programming & audio), me (programming), Saman Bemel-Benrud (a bit of everything), Tom Lubanovic (design)


Deno's sandbox is insufficient

May 17, 2023

Node.js packages, NPM post-install scripts horrify me.

So much of “traditional” security relies on if an application can get root, change your system etc… to say absolutely nothing about what’s sitting in your $HOME directory.

My SSH keys? My downloads? My private notes?

At any time—any of the bazillion packages NPM install could be malicious, and secretly exfiltrate your data. DYLD_INSERT_LIBRARIES something, put something in your .bashrc, whatever.

Another JavaScript runtime—Deno tries to solve this problem:

> deno run --allow-run runner.ts
error: Uncaught PermissionDenied: Requires env access to "HOME", run again with the --allow-env flag
const privateKey = await Deno.readFile(Deno.env.get("HOME") + "/.ssh/id_ed25519");

By default, access to most system I/O is denied. There are some I/O operations that are allowed in a limited capacity, even by default. These are described below.

To enable these operations, the user must explicitly grant permission to the Deno runtime. This is done by passing the --allow-read, --allow-write, --allow-net, --allow-env, and --allow-run flags to the deno command.

Digging deeper

As far as I could tell—Deno’s approach is only gated by an application-specific sandbox. The files to be read/denied are checked against an allowlist in their Rust code, if it’s there it’s allowed, otherwise it’s denied.

Deno makes the assumption its JavaScript engine, V8, and it’s application code is perfect—free of memory bugs. If this assumption is violated, then you can break the sandbox. Furthermore, if you allow calling a subcommand like ESBuild, you also break the sandbox.

This is inconsistent with other projects where V8 is used—Chromium’s “Sandbox” takes the assumption their application sandbox has already been escaped.

Assume sandboxed code is malicious code: For threat-modeling purposes, we consider the sandbox compromised (that is, running malicious code) once the execution path reaches past a few early calls in the main() function. In practice, it could happen as soon as the first external input is accepted, or right before the main loop is entered.

How they accomplish this is a command in macOS named sandbox-exec, a command that allows us at the OS level to restrict what a process can read, write, and access through the network.

The mechanism is not documented outside of Apple, is technically deprecated (man sandbox-exec), but even so is used by Chromium, Firefox, and Bazel. Profiles are defined using some sort of Scheme variant.

How these have been documented, then, is by analyzing ones internal to macOS—try picking an example in /System/Library/Sandbox/Profiles:

;; cat /System/Library/Sandbox/Profiles/bsd.sb
(version 1)
(debug deny)
(import "system.sb")

;; allow processes to traverse symlinks
(allow file-read-metadata)

(allow file-read-data file-write-data
  (regex
    ; Allow files accessed by system dylibs and frameworks
    #"/\.CFUserTextEncoding$"
    #"^/usr/share/nls/"
    #"^/usr/share/zoneinfo /var/db/timezone/zoneinfo/"
  ))

(allow ipc-posix-shm (ipc-posix-name "apple.shm.notification_center")) ; Libnotify

(allow signal (target self))

Neat! Then run it with sandbox-exec -f /System/Library/Sandbox/Profiles/bsd.sb node index.js, although don’t expect it to do much without modification.

Tying this together

Please let me sandbox Node.js. Please let me sandbox NPM. While Deno is a great start, their sandbox is insufficient relying only on application-level policy—true sandboxing needs OS-level sandboxing.

Deno needs OS-level sandboxing because they cannot guarantee the correctness of their system, something even Google admits for V8:

The key to security is understanding: we can only truly secure a system if we fully understand its behaviors with respect to the combination of all possible inputs in all possible states. For a codebase as large and diverse as Chromium, reasoning about the combined behavior of all its parts is nearly impossible.

On macOS, this can be accomplished using sandbox-exec.

I’d really love to see Deno ship with sandbox-exec support, and for the permissions Deno has developed to permeate Node.js/NPM writ large.


Serial side projects

February 22, 2022

It started small, about a year ago, in-between me trying to finish my undergrad degree.

I kept having an idea for a spatial desktop manager—not totally unlike a project called PaperWM, although I didn’t know it at the time. You can think of like when you search for titles in Netflix how the different rows retain their relative position to the cursor. Except, of course, with windows.

Cut to a few months later, I ended up combing through stacks of Linux documentation trying to find the best way to build this thing.

OK. I need to learn about Wayland and build my window manager with that. Surely C isn’t good enough (in fairness, it isn’t), which compiled language has the best wlroots bindings? Swift, cool.

Maybe it needs to be a distro?

Cut later—I now have a fully immutable Linux system decently far removed from my Ubuntu base. This little immutability stunt, based on OSTree, is pretty stable actually. There’s a lot of externalities that come with it, and of course I had to figure those out as well from the jump. I took optimistic notes.

On and on it went, me finding the ends of the Linux earth for prior art and inspiration, but my excitement for the original idea was waning.

The goal of a side-project

If it’s not clear speaking of it in the past-tense, I’m no longer working on this project.

Was it a failure? I don’t know. I’m not sure. All of these tasks, some I’m actively invested in and others zombies, all carry a guilt that they’re not shipped. “Always be shipping.”

Other than declaring side-project bankruptcy, what am I suppose to do? Make a GAANT chart months out for my free time?