@@ -9,6 +9,13 @@ jsep.addUnaryOp('typeof');
9
9
jsep . addLiteral ( 'null' , null ) ;
10
10
jsep . addLiteral ( 'undefined' , undefined ) ;
11
11
12
+ const BLOCKED_PROTO_PROPERTIES = new Set ( [
13
+ 'constructor' ,
14
+ '__proto__' ,
15
+ '__defineGetter__' ,
16
+ '__defineSetter__'
17
+ ] ) ;
18
+
12
19
const SafeEval = {
13
20
/**
14
21
* @param {jsep.Expression } ast
@@ -66,10 +73,7 @@ const SafeEval = {
66
73
'*' : ( a , b ) => a * b ( ) ,
67
74
'/' : ( a , b ) => a / b ( ) ,
68
75
'%' : ( a , b ) => a % b ( )
69
- } [ ast . operator ] (
70
- SafeEval . evalAst ( ast . left , subs ) ,
71
- ( ) => SafeEval . evalAst ( ast . right , subs )
72
- ) ;
76
+ } [ ast . operator ] ( SafeEval . evalAst ( ast . left , subs ) , ( ) => SafeEval . evalAst ( ast . right , subs ) ) ;
73
77
return result ;
74
78
} ,
75
79
evalCompound ( ast , subs ) {
@@ -99,7 +103,7 @@ const SafeEval = {
99
103
return SafeEval . evalAst ( ast . alternate , subs ) ;
100
104
} ,
101
105
evalIdentifier ( ast , subs ) {
102
- if ( ast . name in subs ) {
106
+ if ( Object . hasOwn ( subs , ast . name ) ) {
103
107
return subs [ ast . name ] ;
104
108
}
105
109
throw ReferenceError ( `${ ast . name } is not defined` ) ;
@@ -108,33 +112,22 @@ const SafeEval = {
108
112
return ast . value ;
109
113
} ,
110
114
evalMemberExpression ( ast , subs ) {
111
- if (
112
- ( ast . property . type === 'Identifier' &&
113
- ast . property . name === 'constructor' ) ||
114
- ( ast . object . type === 'Identifier' &&
115
- ast . object . name === 'constructor' )
116
- ) {
117
- throw new Error ( "'constructor' property is disabled" ) ;
118
- }
119
-
120
115
const prop = ast . computed
121
116
? SafeEval . evalAst ( ast . property ) // `object[property]`
122
117
: ast . property . name ; // `object.property` property is Identifier
123
118
const obj = SafeEval . evalAst ( ast . object , subs ) ;
119
+ if ( obj === undefined || obj === null ) {
120
+ throw TypeError (
121
+ `Cannot read properties of ${ obj } (reading '${ prop } ')`
122
+ ) ;
123
+ }
124
+ if ( ! Object . hasOwn ( obj , prop ) && BLOCKED_PROTO_PROPERTIES . has ( prop ) ) {
125
+ throw TypeError (
126
+ `Cannot read properties of ${ obj } (reading '${ prop } ')`
127
+ ) ;
128
+ }
124
129
const result = obj [ prop ] ;
125
130
if ( typeof result === 'function' ) {
126
- if ( obj === Function && prop === 'bind' ) {
127
- throw new Error ( 'Function.prototype.bind is disabled' ) ;
128
- }
129
- if ( obj === Function && ( prop === 'call' || prop === 'apply' ) ) {
130
- throw new Error (
131
- 'Function.prototype.call and ' +
132
- 'Function.prototype.apply are disabled'
133
- ) ;
134
- }
135
- if ( result === Function ) {
136
- return result ; // Don't bind so can identify and throw later
137
- }
138
131
return result . bind ( obj ) ; // arrow functions aren't affected by bind.
139
132
}
140
133
return result ;
@@ -156,19 +149,16 @@ const SafeEval = {
156
149
evalCallExpression ( ast , subs ) {
157
150
const args = ast . arguments . map ( ( arg ) => SafeEval . evalAst ( arg , subs ) ) ;
158
151
const func = SafeEval . evalAst ( ast . callee , subs ) ;
159
- if ( func === Function ) {
160
- throw new Error ( 'Function constructor is disabled' ) ;
161
- }
152
+ // if (func === Function) {
153
+ // throw new Error('Function constructor is disabled');
154
+ // }
162
155
return func ( ...args ) ;
163
156
} ,
164
157
evalAssignmentExpression ( ast , subs ) {
165
158
if ( ast . left . type !== 'Identifier' ) {
166
159
throw SyntaxError ( 'Invalid left-hand side in assignment' ) ;
167
160
}
168
161
const id = ast . left . name ;
169
- if ( id === '__proto__' ) {
170
- throw new Error ( 'Assignment to __proto__ is disabled' ) ;
171
- }
172
162
const value = SafeEval . evalAst ( ast . right , subs ) ;
173
163
subs [ id ] = value ;
174
164
return subs [ id ] ;
@@ -193,7 +183,8 @@ class SafeScript {
193
183
* @returns {EvaluatedResult } Result of evaluated code
194
184
*/
195
185
runInNewContext ( context ) {
196
- const keyMap = { ...context } ;
186
+ // `Object.create(null)` creates a prototypeless object
187
+ const keyMap = Object . assign ( Object . create ( null ) , context ) ;
197
188
return SafeEval . evalAst ( this . ast , keyMap ) ;
198
189
}
199
190
}
0 commit comments