Parts of Lessons converted to React now in Preview

Super cool! The API design is really ergonomic. One thing that might be nice is if the global registration could be a dictionary keyed on the script version. That way, instead of initing a separate instance every time the script is included, your library could automatically just use a single instance for each used version that is shared among all scripts using that version.

Also, I’m not sure that your usage of unsafeWindow is secure. Your script does use grant none, so if the script is directly installed instead of included, there is no issue since the script is directly injected into the document in that case and window and unsafeWindow I think are the same object. However, if the script is included in another script that does not use grant none, then I think it may introduce a privilege escalation vulnerability that would allow any untrusted javascript running on the page to access the sandbox containing the userscript. (I’m not sure exactly how script managers handle inclusion in this case. If they just inject the grant none script directly into the page the same as if it were a standalone script, then there is again no issue and the usage of unsafeWindow could be replaced by window in your script. However, if an included script gets run in the same sandbox as the script including it, this would create a security issue.) Specifically, any untrusted script in the page could get access to the global prototype of object for the sandbox from the object that you set as a property of unsafewindow. Even if you clear the prototype of that object, I think there would still be an issue since you would actually need to recursively clear the prototypes of all objects that are descendants of it and I don’t think you can do that with function objects. Also, even if you were able to fix all of that, I’m not sure whether there are other dangerous properties. There are also security gotchas with even just reading properties of unsafeWindow. For this reason, I would recommend avoiding it entirely. As far as I know, the only secure way to expose a function that the page can call is to have the trusted privileged script insert an untrusted unprivileged script into the document which exposes the methods and then communicates back to the privileged script using event listeners or postMessage. (I.E., your trusted library in the sandbox has a function called myfun, you inject an untrusted script which exposes a function also called myfun which dispatches an event called call_myfun. The trusted library listens for this event and calls the actual implementation when it receives the event. You could alternatively implement this approach using postMessage, but postMessage has security gotchas of its own so I would recommend the mentioned events approach instead.)