forked from hyoo-ru/mam_mol
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sandbox.ts
169 lines (114 loc) · 4.27 KB
/
sandbox.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
namespace $ {
export class $mol_func_sandbox {
static blacklist = new Set([
( function() {} ).constructor ,
( async function() {} ).constructor ,
( function*() {} ).constructor ,
eval ,
setTimeout ,
setInterval ,
])
static whitelist = new WeakSet()
static _make : ( contexts : Object[] )=> ( code : string )=> ()=> any
static get make() {
if( this._make ) return this._make
const frame = $mol_dom_context.document.createElement( 'iframe' )
frame.style.display = 'none'
$mol_dom_context.document.body.appendChild( frame )
const win = frame.contentWindow as any as typeof globalThis
const SafeFunc = win.Function
const SafeJSON = win.JSON
win.eval( `
var AsyncFunction = AsyncFunction || ( async function() {} ).constructor
var GeneratorFunction = GeneratorFunction || ( function*() {} ).constructor
var AsyncGeneratorFunction = AsyncGeneratorFunction || ( async function*() {} ).constructor
Object.defineProperty( Function.prototype , 'constructor' , { value : undefined } )
Object.defineProperty( AsyncFunction.prototype , 'constructor' , { value : undefined } )
Object.defineProperty( GeneratorFunction.prototype , 'constructor' , { value : undefined } )
Object.defineProperty( AsyncGeneratorFunction.prototype , 'constructor' , { value : undefined } )
for( const Class of [
String , Number , BigInt , Boolean , Array , Object , Promise , Symbol , RegExp ,
Error , RangeError , ReferenceError , SyntaxError , TypeError ,
Function , AsyncFunction , GeneratorFunction , AsyncGeneratorFunction
] ) {
Object.freeze( Class )
Object.freeze( Class.prototype )
}
for( const key of Object.getOwnPropertyNames( window ) ) delete window[ key ]
` )
// Stop event-loop and break all async operations
$mol_dom_context.document.body.removeChild( frame )
let context_default = {}
function clean( obj : object ) {
for( let name of Object.getOwnPropertyNames( obj ) ) {
context_default[ name ] = undefined
}
const proto = Object.getPrototypeOf( obj )
if( proto ) clean( proto )
}
clean( win )
const is_primitive = ( val : any )=> Object( val ) !== val
const safe_value = ( val : any ) : any => {
if( is_primitive( val ) ) return val
if( this.blacklist.has( val ) ) return undefined
if( this.whitelist.has( val ) ) return val
const str = JSON.stringify( val )
if( !str ) return str
val = SafeJSON.parse( str )
this.whitelist.add( val )
return val
}
const safe_derived = ( val : any ) : any => {
if( is_primitive( val ) ) return val
const proxy = new Proxy( val , {
get( val , field : any ) {
if( field === 'valueOf' ) return safe_derived( val[field] )
if( field === 'toString' ) return safe_derived( val[field] )
return safe_value( val[field] )
},
set() { return false },
defineProperty() { return false },
deleteProperty() { return false },
preventExtensions() { return false },
apply( val , host , args ) {
return safe_value( val.call( host , ... args ) )
},
construct( val , args ) {
return safe_value( new val( ... args ) )
},
})
this.whitelist.add( proxy )
return proxy
}
return this._make = ( ( ... contexts : Object[] )=> {
const context_merged = {}
for( let context of contexts ) {
for( let name of Object.getOwnPropertyNames( context ) ) {
context_merged[ name ] = safe_derived( context[ name ] )
}
}
const vars = Object.keys( context_merged )
const values = vars.map( name => context_merged[ name ] )
return ( code : string )=> {
const func = new SafeFunc( ... vars , '"use strict";' + code )
.bind( null , ... values )
return ()=> {
const val = func()
if( is_primitive( val ) ) return val
this.whitelist.add( val )
return val
}
}
} ).bind( null , context_default )
}
constructor( ... contexts : Object[] ) {
this.contexts = contexts
}
contexts : Object[]
_eval : ( ( code : string )=> ()=> any ) | undefined
get eval() {
if( this._eval ) return this._eval
return this._eval = $mol_func_sandbox.make( ... this.contexts as [Object[]] )
}
}
}