(safeEvaluate)
| 5157 | * the safety of safeEvaluate for confinement. |
| 5158 | */ |
| 5159 | const makeFunctionConstructor= (safeEvaluate)=>{ |
| 5160 | // Define an unused parameter to ensure Function.length === 1 |
| 5161 | const newFunction= function Function(_body) { |
| 5162 | // Sanitize all parameters at the entry point. |
| 5163 | // eslint-disable-next-line prefer-rest-params |
| 5164 | const bodyText= `${arrayPop(arguments)|| '' }`; |
| 5165 | // eslint-disable-next-line prefer-rest-params |
| 5166 | const parameters= `${arrayJoin(arguments,',') }`; |
| 5167 | |
| 5168 | // Are parameters and bodyText valid code, or is someone |
| 5169 | // attempting an injection attack? This will throw a SyntaxError if: |
| 5170 | // - parameters doesn't parse as parameters |
| 5171 | // - bodyText doesn't parse as a function body |
| 5172 | // - either contain a call to super() or references a super property. |
| 5173 | // |
| 5174 | // It seems that XS may still be vulnerable to the attack explained at |
| 5175 | // https://github.com/tc39/ecma262/pull/2374#issuecomment-813769710 |
| 5176 | // where `new Function('/*', '*/ ) {')` would incorrectly validate. |
| 5177 | // Before we worried about this, we check the parameters and bodyText |
| 5178 | // together in one call |
| 5179 | // ```js |
| 5180 | // new FERAL_FUNCTION(parameters, bodyTest); |
| 5181 | // ``` |
| 5182 | // However, this check is vulnerable to that bug. Aside from that case, |
| 5183 | // all engines do seem to validate the parameters, taken by themselves, |
| 5184 | // correctly. And all engines do seem to validate the bodyText, taken |
| 5185 | // by itself correctly. So with the following two checks, SES builds a |
| 5186 | // correct safe `Function` constructor by composing two calls to an |
| 5187 | // original unsafe `Function` constructor that may suffer from this bug |
| 5188 | // but is otherwise correctly validating. |
| 5189 | // |
| 5190 | // eslint-disable-next-line no-new |
| 5191 | new FERAL_FUNCTION(parameters, ''); |
| 5192 | // eslint-disable-next-line no-new |
| 5193 | new FERAL_FUNCTION(bodyText); |
| 5194 | |
| 5195 | // Safe to be combined. Defeat potential trailing comments. |
| 5196 | // TODO: since we create an anonymous function, the 'this' value |
| 5197 | // isn't bound to the global object as per specs, but set as undefined. |
| 5198 | const src= `(function anonymous(${parameters}\n) {\n${bodyText}\n})`; |
| 5199 | return safeEvaluate(src); |
| 5200 | }; |
| 5201 | |
| 5202 | defineProperties(newFunction, { |
| 5203 | // Ensure that any function created in any evaluator in a realm is an |
| 5204 | // instance of Function in any evaluator of the same realm. |
| 5205 | prototype: { |
| 5206 | value: FERAL_FUNCTION.prototype, |
| 5207 | writable: false, |
| 5208 | enumerable: false, |
| 5209 | configurable: false}}); |
| 5210 | |
| 5211 | |
| 5212 | |
| 5213 | // Assert identity of Function.__proto__ accross all compartments |
| 5214 | getPrototypeOf(FERAL_FUNCTION)=== FERAL_FUNCTION.prototype|| |
| 5215 | Fail `Function prototype is the same accross compartments`; |
| 5216 | getPrototypeOf(newFunction)=== FERAL_FUNCTION.prototype|| |
no test coverage detected