How to Gaslight JavaScript Programmers
Bryan Gillespie · · Pragma Twice
TL;DR: Proxy
Say you are a JavaScript programmer and you want to use an object created by something you don't directly control. This happens often when making a call to a Web API or some other library you've pulled into your project.
The question: is there any way of knowing what the object can do at runtime without examining the source that created it? For example, try to figure out what the mysterious obj below can do:
At first glance, it seems like there is a single callable property - hello. But what if I told you that obj.goodbye() also works? Go ahead, try it.
But wait, how is that possible? goodbye doesn't show up in console.log. Nor does it appear in Object.keys(obj) or Reflect.ownKeys(obj) or for (const key in obj). I've also put obj onto globalThis in case you want to examine it in your browser's dev tools console. You won't be able to find any evidence of goodbye there either, I'm afraid.
Proxy
It turns out, JavaScript provides a method of "trapping" nearly any operation on an object - property access, assignment, enumeration, and even property deletion. How? Via a Proxy. Think of a Proxy as a "hook" or "middleware" that can be used to gaslight the caller into seeing the proxied object any way you want them to see it. When you wrap an object in a Proxy, you can do almost anything you imagine - hide properties like I did with goodbye above, invent new "virtual" properties, or even make operations on properties "cursed":
But... Why?
I discovered Proxy while working on Pragma Twice, a JavaScript puzzle adventure where every object in the game has an API. I wanted a way to hide certain API methods from the player until they had discovered them through gameplay. But I also wanted veteran players to be able to use them right from the beginning if they knew about them. I wanted Pragma Twice to be a bit like Tunic in that the main thing you "unlock" as you progress through the game is "knowledge". If only you knew the right secrets, you could take all sorts of shortcuts. Proxies turned out to be the perfect solution.
Proxies have many non-nefarious use cases as well. As a contrived example, maybe you are writing an application and want console.log to tee to both the console and a file. Simply replace the global console with a Proxy that forwards calls to both the original console and the file logger. Some reactive frameworks (such as Vue) use Proxies to automatically detect changes to objects and update the UI accordingly.
Discovering Hidden Properties
So, is there any way to discover object properties hidden by a Proxy without access to the source code? Possibly. In theory, you could use typeof obj[guess] in conjunction with a dictionary or brute force attack to try to infer property existence. Think you can do it? Try it out on the Pragma Twice web demo... if you can do it without using dev tools to look at the source, send me a note!
For completeness, here's the full source for the original hello/goodbye example at the beginning:
Happy gaslighting!