{"id":13724820,"url":"https://github.com/asvd/jailed","last_synced_at":"2025-05-16T01:06:58.823Z","repository":{"id":19940336,"uuid":"23206986","full_name":"asvd/jailed","owner":"asvd","description":"execute untrusted code with custom permissions","archived":false,"fork":false,"pushed_at":"2020-03-11T11:58:21.000Z","size":181,"stargazers_count":1015,"open_issues_count":37,"forks_count":69,"subscribers_count":21,"default_branch":"master","last_synced_at":"2025-05-08T12:31:51.412Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/asvd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-08-22T00:00:18.000Z","updated_at":"2025-04-30T08:52:38.000Z","dependencies_parsed_at":"2022-07-27T01:00:08.122Z","dependency_job_id":null,"html_url":"https://github.com/asvd/jailed","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asvd%2Fjailed","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asvd%2Fjailed/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asvd%2Fjailed/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asvd%2Fjailed/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/asvd","download_url":"https://codeload.github.com/asvd/jailed/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254448579,"owners_count":22072764,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T01:02:03.927Z","updated_at":"2025-05-16T01:06:53.791Z","avatar_url":"https://github.com/asvd.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"Jailed — flexible JS sandbox\n============================\n\nJailed is a small JavaScript library for running untrusted code in a\nsandbox. The library is written in vanilla-js and has no dependencies.\n\nWith Jailed you can:\n\n- Load an untrusted code into a secure sandbox;\n\n- Export a set of external functions into the sandbox.\n\nThe untrusted code may then interract with the main application by\ndirectly calling those functions, but the application owner decides\nwhich functions to export, and therefore what will be allowed for the\nuntrusted code to perform.\n\nThe code is executed as a *plugin*, a special instance running as a\nrestricted subprocess (in Node.js), or in a web-worker inside a\nsandboxed frame (in case of web-browser environment). The iframe is\ncreated locally, so that you don't need to host it on a separate\n(sub)domain.\n\n\nYou can use Jailed to:\n\n- Setup a safe environment for executing untrusted code, without a\n  need to create a sandboxed worker / subprocess manually;\n\n- Do that in an isomorphic way: the syntax is same both for Node.js\n  and web-browser, the code works unchanged;\n\n- Execute a code from a string or from a file;\n\n- Initiate and interrupt the execution anytime;\n\n- *[Demo](http://asvd.github.io/jailed/demos/web/console/)* safely\n   execute user-submitted code;\n\n- *[Demo](http://asvd.github.io/jailed/demos/web/banner/)* embed\n  3rd-party code and provide it the precise set of functions to\n  harmlessly operate on the part of your application;\n  \n- Export the particular set of application functions into the sandbox\n  (or in the opposite direction), and let those functions be invoked\n  from the other site (without a need for manual messaging) thus\n  building any custom API and set of permissions.\n\n\nFor instance:\n\n\n```js\nvar path = 'http://path.to/the/plugin.js';\n\n// exported methods, will be available to the plugin\nvar api = {\n    alert: alert\n};\n\nvar plugin = new jailed.Plugin(path, api);\n```\n\n\n*plugin.js:*\n\n```js\n// runs in a sandboxed worker, cannot access the main application,\n// with except for the explicitly exported alert() method\n\n// exported methods are stored in the application.remote object\napplication.remote.alert('Hello from the plugin!');\n```\n\n*(exporting the `alert()` method is not that good idea actually)*\n\nUnder the hood, an application may only communicate to a plugin\n(sandboxed worker / jailed subprocess) through a messaging mechanism,\nwhich is reused by Jailed in order to simulate the exporting of\nparticular functions. Each exported function is duplicated on the\nopposite site with a special wrapper method with the same name. Upon\nthe wrapper method is called, arguments are serialized, and the\ncorresponding message is sent, which leads to the actual function\ninvocation on the other site. If the executed function then issues a\ncallback, the responce message will be sent back and handled by the\nopposite site, which will, in turn, execute the actual callback\npreviously stored upon the initial wrapper method invocation. A\ncallback is in fact a short-term exported function and behaves in the\nsame way, particularly it may invoke a newer callback in reply.\n\n\n### Installation\n\nFor the web-browser environment — download and unpack the\n[distribution](https://github.com/asvd/jailed/releases/download/v0.3.1/jailed-0.3.1.tar.gz), or install it using [Bower](http://bower.io/):\n\n```sh\n$ bower install jailed\n```\n\nLoad the `jailed.js` in a preferrable way. That is an UMD module, thus\nfor instance it may simply be loaded as a plain JavaScript file using\nthe `\u003cscript\u003e` tag:\n\n```html\n\u003cscript src=\"jailed/jailed.js\"\u003e\u003c/script\u003e\n```\n\nFor Node.js — install Jailed with npm:\n\n```sh\n$ npm install jailed\n```\n\nand then in your code:\n\n```js\nvar jailed = require('jailed');\n```\n\nOptionally you may load the script from the\n[distribution](https://github.com/asvd/jailed/releases/download/v0.3.1/jailed-0.3.1.tar.gz):\n\n```js\nvar jailed = require('path/to/jailed.js');\n```\n\nAfter the module is loaded, the two plugin constructors are available:\n`jailed.Plugin` and `jailed.DynamicPlugin`.\n\n\n\n### Usage\n\nThe messaging mechanism reused beyond the remote method invocation\nintroduces some natural limitations for the exported functions and\ntheir usage (nevertheless the most common use-cases are still\nstraightforward):\n\n- Exported function arguments may only be either simple objects (which\n  are then serialized and sent within a message), or callbacks (which\n  are preserved and replaced with special identifiers before\n  sending). Custom object instance may not be used as an argument.\n\n- A callback can not be executed several times, it will be destroyed\n  upon the first invocation.\n\n- If several callbacks are provided, only one of them may be called.\n\n- Returned value of an exported function is ignored, result should be\n  provided to a callback instead.\n\n\n*In Node.js the\n [send()](http://nodejs.org/api/child_process.html#child_process_child_send_message_sendhandle)\n method of a child process object is used for transfering messages,\n which serializes an object into a JSON-string. In a web-browser\n environment, the messages are transfered via\n [postMessage()](https://developer.mozilla.org/en-US/docs/Web/API/Window.postMessage)\n method, which implements [the structured clone\n algorithm](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/The_structured_clone_algorithm)\n for the serialization. That algorithm is more capable than JSON (for\n instance, in a web-browser you may send a RegExp object, which is not\n possible in Node.js). [More details about structured clone algorithm\n and its comparsion to\n JSON](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/The_structured_clone_algorithm).*\n\nA plugin object may be created either from a string containing a\nsource code to be executed, or with a path to the script. To load a\nplugin code from a file, create the plugin using `jailed.Plugin`\nconstructor and provide the path:\n\n```js\nvar path = 'http://path.to/some/plugin.js';\n\n// set of methods to be exported into the plugin\nvar api = {\n    alert: alert\n}\n\nvar plugin = new jailed.Plugin(path, api);\n```\n\n\n*plugin.js:*\n\n```js\napplication.remote.alert('Hello from the plugin!');\n```\n\n\nCreating a plugin from a string containing a code is very similar,\nthis is performed using `jailed.DynamicPlugin` constructor:\n\n\n```js\nvar code = \"application.remote.alert('Hello from the plugin!');\";\n\nvar api = {\n    alert: alert\n}\n\nvar plugin = new jailed.DynamicPlugin(code, api);\n```\n\n\nThe second `api` argument provided to the `jailed.Plugin` and\n`jailed.DynamicPlugin` constructors is an interface object with a set\nof functions to be exported into the plugin. It is also possible to\nexport functions in the opposite direction — from a plugin to the main\napplication. It may be used for instance if a plugin provides a method\nto perform a calculation. In this case the second argument of a plugin\nconstructor may be omitted. To export some plugin functions, use\n`application.setInterface()` method in the plugin code:\n\n\n```js\n// create a plugin\nvar path = \"http://path.to/some/plugin.js\";\nvar plugin = new jailed.Plugin(path);\n\n// called after the plugin is loaded\nvar start = function() {\n    // exported method is available at this point\n    plugin.remote.square(2, reportResult);\n}\n\nvar reportResult = function(result) {\n    window.alert(\"Result is: \" + result);\n}\n\n// execute start() upon the plugin is loaded\nplugin.whenConnected(start);\n```\n\n*plugin.js:*\n\n```js\n// provides the method to square a number\nvar api = {\n    square: function(num, cb) {\n        // result reported to the callback\n        cb(num*num);\n    }\n}\n\n// exports the api to the application environment\napplication.setInterface(api);\n```\n\nIn this example the `whenConnected()` plugin method is used at the\napplication site: that method subscribes the given function to the\nplugin connection event, after which the functions exported by the\nplugin become accessible at the `remote` property of a plugin.\n\nThe `whenConnected()` method may be used as many times as needed and\nthus subscribe several handlers for a single connection event. For\nadditional convenience, it is also possible to set a connection\nhandler even after the plugin has already been connected — in this\ncase the handler is issued immediately (yet asynchronously).\n\nWhen a plugin code is executed, a set of functions exported by the\napplication is already prepared. But if one of those functions is\ninvoked, it will actually be called on the application site. If in\nthis case the code of that function will try to use a function\nexported by the plugin, it may not be prepared yet. To solve this, the\nsimilar `application.whenConnected()` method is available on the\nplugin site. The method works same as the one of the plugin object:\nthe subscribed handler function will be executed after the connection\nis initialized, and a set of functions exported by each site is\navailable on the opposite site.\n\n\nTherefore:\n\n- If you need to load a plugin and supply it with a set of exported\n  functions, simply provide those functions into the plugin\n  constructor, and then access those at `application.remote` property\n  on the plugin site — the exported functions are already prepared\n  when the plugin code is exectued.\n\n- If you need to load a plugin and use the functions it provides\n  through exporting, set up a handler using `plugin.whenConnected()`\n  method on the application site. After the event is fired, the\n  functions exported by the plugin are available at its `remote`\n  property of the plugin object;.\n\n- If both application and a plugin use the exported functions of each\n  other, *and* the communication is initiated by the plugin, you will\n  most likely need to use the `application.whenConnected()` method on\n  the plugin site before initiating the communication, in order to\n  make sure that the functions exported by the plugin are already\n  available to the application.\n\n\nTo disconnect a plugin, use the `disconnect()` method: it kills a\nworker / subprocess immediately without any chance for its code to\nreact.\n\nA plugin may also disconnect itself by calling the\n`application.disconnect()` method.\n\nIn addition to `whenConnected()` method, the plugin object also\nprovides similar `whenFailed()` and `whenDisconnected()` methods:\n\n- `whenFailed()` subscribes a handler function to the connection\n  failure event, which happens if there have been some error during\n  the plugin initialization, like a network problem or a syntax error\n  in the plugin initialization code.\n\n- `whenDisconnected()` subscribes a function to the disconnect event,\n  which happens if a plugin was disconnected by calling the\n  `disconnect()` method, or a plugin has disconnected itself by\n  calling `application.disconnect()`, or if a plugin failed to\n  initialize (along with the failure event mentioned above). After the\n  event is fired, the plugin is not usable anymore.\n\nJust like as for `whenConnected()` method, those two methods may also\nbe used several times or even after the event has actually been fired.\n\n\n### Compatibility\n\nJailed was tested and should work in Node.js, and in the following\nbrowsers:\n\n- Internet Explorer 10+, Edge\n- Firefox 26+\n- Opera 12+\n- Safari 6+\n- Chrome 10+\n\n\n### Security\n\nThis is how the sandbox is built:\n\n##### In a web-browser:\n\n- a [sandboxed\niframe](http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/)\nis created with its `sandbox` attribute only set to `\"allow-scripts\"`\n(to prevent the content of the frame from accessing anything of the\nmain application origin);\n\n- then a web-worker is started inside that frame;\n\n- finally the code is loaded by the worker and executed.\n\n*Note: when Jailed library is loaded from the local source (its path\n starts with `file://`), the `\"allow-same-origin\"` permission is added\n to the `sandbox` attribute of the iframe. Local installations are\n mostly used for testing, and without that permission it would not be\n possible to load the plugin code from a local file. This means that\n the plugin code has an access to the local filesystem, and to some\n origin-shared things like IndexedDB (though the main application page\n is still not accessible from the worker). Therefore if you need to\n safely execute untrusted code on a local system, reuse the Jailed\n library in Node.js.*\n\n\n##### In Node.js:\n\n*Warning: according to recent reports\n ([#33](https://github.com/asvd/jailed/issues/33)) this way of\n sandboxing is not secure any longer, the fix is being prepared...*\n\n- A Node.js subprocess is created by the Jailed library;\n\n- the subprocess (down)loads the file containing an untrusted code as\n  a string (or, in case of `DynamicPlugin`, simply uses the provided\n  string with code)\n\n- then `\"use strict\";` is appended to the head of that code (in order\n  to prevent breaking the sandbox using `arguments.callee.caller`);\n\n- finally the code is executed using `vm.runInNewContext()` method,\n  where the provided sandbox only exposes some basic methods like\n  `setTimeout()`, and the `application` object for messaging with the\n   application site.\n\n--\n\nfollow me on twitter: https://twitter.com/asvd0\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasvd%2Fjailed","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fasvd%2Fjailed","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasvd%2Fjailed/lists"}