Skip to main content

On Sale: GamesAssetsToolsTabletopComics
Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

To Sandbox, or Not?

A topic by Internet Janitor created Feb 25, 2024 Views: 823 Replies: 13
Viewing posts 1 to 8
Developer

Decker comes in two flavors: web-decker (which runs in web browsers) and native-decker (which is a native executable for MacOS, Windows, Linux, BSD, etc).

Since Decker's initial release, I've tried to maintain parity between these implementations wherever possible. As a result, they both limit themselves to fairly strict sandboxing constraints inherited from web browsers. For example, Decker can open files on your local computer, but only through a manual file-picker dialog. A nice side effect of this approach is that it upholds a principle of informed consent: Decks cannot do nasty or invasive things like rummage through your documents or phone home with telemetry without the user's awareness.

I have heard a number of Decker users (and, more tenuously, prospective users) state that they would be interested in using Decker for a wider range of things if they could write programs that are less tightly sandboxed. Filesystem I/O and traversal and network access are frequent asks. If a user installs Python and runs a script on their computer, it's completely normal for that script to be able to monkey around at will with the user's files, and web-decker already provides a robust means of isolating decks you don't entirely trust. Perhaps the cautious approach is simply inconvenient?

I would very much like to hear your opinions on this matter!

  • Should native-decker have APIs for doing potentially hazardous or invasive things to the local computer, and if so what is the minimum functionality Decker should expose?
  • Should native-decker prompt for informed consent when using these APIs, or is this approach needlessly cautious and annoying?
  • What, specifically, would you like to build if you had access to the things you describe?

Considering how many people nowadays install software with `curl ... | bash` and consider that normal, I wouldn't trust those who say "your regular apps can already do anything" as if that's an excuse. It's not. It's often very problematic, just not an easy problem to solve in a general way without also making all your software toy-like. Android tried... yet somehow the Play Store is infamously full of malware, while F-Droid only avoids that because each and every app there is audited by a human being.

As a compromise, the stand-alone lilt interpreter could be given free reign if it doesn't have such already, since in principle you have to know what you're doing to use it at all. And Decker itself could have a flag to disable the sandbox, if it's not too much work.

Developer(+1)

Lilt does have free reign, since this is the normal expectation for a command-line scripting language interpreter.

For Decker itself, everything potentially hazardous would definitely be packaged in an interface (sort of like rtext, sys, or app) so that decks can easily test for its presence or absence. Beyond this, there are several possibilities:

  • Add a checkbox to the Decker menu that can enable or disable access at any time. Applications can whine at the user to check the box, but are powerless to modify the setting themselves. This is pretty annoying for users.
  • Provide a function that must be called to enable access, which would prompt a user once (per deck-opened) to manually confirm. Still annoying, but less so than the above.
  • Offer users esoteric rituals to opt into hazards: for example, creating a "lock-file" with a specific name relative to the decker executable and/or providing Decker with a command-line flag at startup. This has extremely low discoverability and so would be used only by experts, but would pose minimal annoyance to said experts. The major downside is that this would naturally have granularity for an entire work session, rather than scoping access to a specific deck.
  • Instead, offer users similar esoteric rituals to opt-out, instead of opt-in.
  • Offer a compile-time flag for including the hazardous interface. Similar to the above, but higher barriers to entry. A number of fancy IO features would be a lot easier to offer if I only had to do so for POSIX operating systems, and this would be a convenient way to avoid dealing with any of this stuff on Windows.
  • Offer a compile-time flag for excluding the hazardous interface. A bare minimum affordance to the privacy/security conscious desktop user.

Yep, there's no good solution. It sucks no matter what.

I feel like none of these solutions are mutually exclusive, necessarily. Like, for the default provided builds, you could have a checkbox in the menu to enable the functionality AND have an esoteric way to default it on AND have the ability to make custom builds that enable it. So then the regular Decker user has the ability to turn on the functionality but only by doing so intentionally, and then if someone wants to use these features in a more neatly packaged up type project, it's on them to ship a custom build and then for the end user it's no different as a potential threat vector to downloading any other random software online. I think this kind of mix could make things workable

(+1)

As the user on Lobste.rs who prompted this, my take is that Decker is one or more of a number of things, and all of those are awesome and equally valid, and I hope my comment wasn’t considered criticism or a complaint. I don’t think it can be ALL of these things, though.

Is it:

A programming language and framework for building apps?

A cross platform web/desktop tool with full compatibility?

A set of intentional restrictions for creativity?

A framework that guarantees the safety of user data against all malicious actors?

Just a fun toy that you are building because it is awesome and fun?

For me, adding the ability to make HTTP requests would multiply the utility of Decker tenfold, but I don’t think you can do that and guarantee that nobody will ever create a malicious deck. On the other hand, essentially every other attack vector is already available to a wannabe attacker. 
On the third hand, my hope to use Decker to write a baseball scoreboard might be missing the point as much as hoping to write a AAA game in Pico-8 would be.

It seems like a hard design problem because for some roles that Decker is trying to fill, it makes sense to be a standalone tool, but for others (especially as a prototyping tool), there's a benefit in connecting to the outside world.

Often when I'm building something, I think Decker would make a good piece of scaffolding. It feels like it ought to be easy to hook decker up to a live SQLite database and make a little frontend, or to design a contraption that could become a user interface component in a web page (outside of a deck).

Decker provides this really amazing development experience, but the price is that you have to be willing to make something that looks like a deck. Most of the time I'm making web pages that I want to look a certain way, and there's no clear path to go from one to the other. Once in a while, though, I have something that would work fine as a deck... And then the question is whether decker would let me make that thing.

I think having some reference docs that showed how to extend Decker would let power users do whatever they want, and then you could wait and see what people actually build.

Developer(+1)

Lots of useful feedback in here so far.

I've decided to begin with an extremely conservative strategy: the bleeding-edge source revision of Decker now includes an interface named "danger", which is normally not accessible. If you tweak the makefile to define the symbol "DANGER_ZONE" and build native-Decker from scratch,  you can use this interface to do most of the things Lilt could do previously:

  • raw file I/O
  • filesystem traversal
  • access environment variables
  • and (if you're on a Unixy operating system) invoke shell commands(!)

Obviously there's a whole host of possible foot-guns, sharp corners, inconsistent cross-platform behaviors, etc, but also it's possible to do a few pretty neat things like grab live data from the internet:


For the time being, I intend to leave The Danger Zone disabled in official binary releases; if you can build Decker from source, I think it's a reasonable assumption that you understand the associated risks. Depending on how people use this functionality, I'll consider offering some other mechanism(s) for opting into danger.

Thoughts?

(+1)

My first thought is that I’m going to lose a lot of weekends doing very cool stuff with this. Thank you!

Developer

Rad! Feel free to post anything you work on!

I actually think Decker might have an interesting interface opportunity here.

The filesystem access you described through a manual file-picker is an example of a security pattern called a powerbox - some less-trusted application code is able to interact with a more privileged object, which can selectively share its access to important things like the filesystem. This same pattern could be extended to the use of other potentially sensitive resources like network access, though. For example, you could potentially have a network-picker akin to a file-picker, but instead of picking a particular file from the filesystem, the user is able to pick a particular network resource (a URL, say). The network-picker in this case could be a part of Decker that does have unfettered access to your network interfaces, but not necessarily every deck you load would have the same authority.

What a good UX for something like this might look like is up in the air; it's a pattern that's really only frequently used in file- and media-pickers, so there's a lot of unexplored territory. (The media picker in recent versions of Android and iOS is basically a powerbox as well, for example.) The only place I've seen a network picker done before is in Sandstorm; it was a bit awkward there because most apps running on it were not Sandstorm-native, and were built assuming they had full network access. But that's why I think this pattern might be interesting in Decker, which isn't trying to support a bunch of existing software that wasn't written with Decker in mind.

(+4)

I'll comment from the perspective of a casual user and someone who enjoys collecting and viewing people's decks locally on my machine. I imagine there are others like me in the community.

As someone who enjoys other people's decks:

  • I would want to be made aware of if a deck had the ability to do something hazardous
  • I'd want to give explicit permission to allow decks I've loaded to be able to access those "danger" features

As someone who enjoys making decks:

  • Downloading, sharing, remixing, and reusing contraptions is what makes the Decker ecosystem so great. 
  • I'm sure there are many users who use contraptions and modules without knowing how they work. 
  • As a creator I want to know I'm not accidently adding hazardous elements to my decks. Especially if I plan to share the deck with others.

I hope that's helpful feedback. Thanks for taking the time to consider some of these thorny problems.

Developer(+1)

As a followup to the Native-Decker incarnation of the "danger" interface, I've created a counterpart for Web-Decker which allows Lil and JS to interoperate, opening the door for bodging in access to all sorts of web APIs that are normally not accessible via Lil.

danger.js["f=>f(f(47))" (on twice x do x,x end)]
# (47,47,47,47)

Just like the native "danger" interface, this feature is strictly opt-in and available to those who build Decker from source. Using this will severely limit portability of your decks, and offer many footguns for breaking the Decker environment. It is, however, significantly lower risk than the native "danger zone", since it's still answerable to the sandboxing mechanisms of a web browser. It was always possible to do this sort of thing by manually editing .html Decker exports, but this mechanism makes it possible to write those sorts of "plugins" against a stable internal interface.

Docs are updated here, for reference.

(+1)

Amazing!