├── css └── styles.css ├── images └── .DS_Store ├── index.html └── js └── nice-script.js /css/styles.css: -------------------------------------------------------------------------------- 1 | textarea { 2 | width:600px; 3 | height: 300px; 4 | } -------------------------------------------------------------------------------- /images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PortSwigger/nice-script/9d979ebcd1c6b727333ea2d368fe90d268f94e76/images/.DS_Store -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | NiceScript sandbox 7 | 8 | 9 | 10 |

11 |
12 | 34 |

35 | 36 |

37 |
38 | 39 |

40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /js/nice-script.js: -------------------------------------------------------------------------------- 1 | var NiceScript = function(){ 2 | function run(code) { 3 | try { 4 | if(checkSyntax(code)) { 5 | var handler = { 6 | get(obj, prop) { 7 | return Reflect.has(obj, prop.toString() + '__$') ? 8 | obj[prop.toString() + '__$'] : undefined; 9 | }, 10 | set(obj, prop, value) { 11 | Reflect.set(obj, prop+'__$', value); 12 | }, 13 | has(obj,prop) { 14 | return obj && Reflect.has(obj, prop.toString()+'__$'); 15 | } 16 | }; 17 | var catchAllHandler = { 18 | get(obj, prop){ 19 | return Reflect.get(obj,prop); 20 | }, 21 | set() { }, 22 | has() { 23 | return true 24 | } 25 | }; 26 | 27 | const _console = {}; 28 | 29 | (function createMockConsole(_console){ 30 | const allowConsoleProps = ["debug", "error", "info", "log", "warn", 31 | "dir", "dirxml", "table", "trace", "group", "groupCollapsed", 32 | "groupEnd", "clear", "count", "countReset", "assert", "profile", 33 | "profileEnd", "time", "timeLog", "timeEnd", "timeStamp"]; 34 | 35 | for(const property of allowConsoleProps){ 36 | _console[property] = function(){ 37 | if (arguments.length > 1 && typeof arguments[0] === 'string') { 38 | arguments[0] = arguments[0].replace(/%/g, '%%') 39 | } 40 | return console[property](...arguments); 41 | } 42 | } 43 | })(_console); 44 | 45 | var allowList = { 46 | __proto__: null, 47 | console__$:_console, 48 | alert__$: function(){ 49 | alert("Sandboxed alert:"+arguments[0]); 50 | }, 51 | String__$: String, 52 | Number__$: Number, 53 | Array__$: Array, 54 | Symbol__$: Symbol, 55 | Math__$: Math, 56 | RegExp__$: RegExp, 57 | Object__$: Object, 58 | eval__$: function(code){ 59 | return NiceScript.run("return "+code); 60 | } 61 | }; 62 | if(!Object.isFrozen(String.prototype)) { 63 | Function.prototype.constructor = null; 64 | Object.freeze(console); 65 | Object.freeze(_console); 66 | Object.freeze(Object); 67 | Object.freeze(String); 68 | Object.freeze(Number); 69 | Object.freeze(Array); 70 | Object.freeze(Symbol); 71 | Object.freeze(Math); 72 | Object.freeze(Function); 73 | Object.freeze(RegExp); 74 | Object.freeze(BigInt); 75 | Object.freeze(Promise); 76 | Object.freeze(console); 77 | Object.freeze(BigInt.prototype); 78 | Object.freeze(Object.prototype); 79 | Object.freeze(String.prototype); 80 | Object.freeze(Number.prototype); 81 | Object.freeze(Array.prototype); 82 | Object.freeze(Symbol.prototype); 83 | Object.freeze(Math.prototype); 84 | Object.freeze(Function.prototype); 85 | Object.freeze(RegExp.prototype); 86 | Object.freeze(Promise.prototype); 87 | Object.defineProperty((async function () {}).constructor.prototype, 'constructor', { 88 | value: null, 89 | configurable: false, 90 | writable: false 91 | }); 92 | Object.defineProperty((async function* () {}).constructor.prototype, 'constructor', { 93 | value: null, 94 | configurable: false, 95 | writable: false 96 | }); 97 | Object.defineProperty((function* () {}).constructor.prototype, 'constructor', { 98 | value: null, 99 | configurable: false, 100 | writable: false 101 | }); 102 | 103 | Object.freeze((async function () {}).__proto__); 104 | Object.freeze((async function* () {}).__proto__); 105 | Object.freeze((function* () {}).__proto__); 106 | Object.freeze((function* () {}).__proto__.prototype); 107 | Object.freeze((async function* () {}).__proto__.prototype); 108 | } 109 | var proxy = new Proxy(allowList, handler); 110 | var catchAllProxy = new Proxy({__proto__:null, proxy:proxy, globalThis:new Proxy(allowList, handler), window:new Proxy(allowList, handler)}, catchAllHandler); 111 | var output = Function('proxy', 'catchAllProxy', ` 112 | with(catchAllProxy) { 113 | with(proxy) { 114 | return (function(){ 115 | "use strict"; 116 | ${code}; 117 | })(); 118 | } 119 | } 120 | `)(proxy, catchAllProxy); 121 | return output; 122 | } 123 | } catch(e) { 124 | throw e; 125 | } 126 | } 127 | function checkSyntax(code) { 128 | Function(code); 129 | if(/\bimport\s*(?:[(]|\/[*]|\/\/|)/.test(code)) { 130 | throw new Error("Dynamic imports are blocked"); 131 | } 132 | return true; 133 | } 134 | return {run: run}; 135 | }(); 136 | --------------------------------------------------------------------------------