├── 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 |
--------------------------------------------------------------------------------