问题起因
起因是一个前端js沙箱的实现中,发现通过沙箱执行代码中访问 RegExp.$n 的值一直是空字符串,类似于:
1 2 3 4 5
| const code = ` const r = /^(\\d{4})-(\\d{1,2})-(\\d{1,2})$/ r.exec('2022-08-31') return RegExp.$1;`; evalScriptInSandbox(code);
|
正常情况下,evalScriptInSandbox(code)
应该返回 '2022'
,但是执行发现只会返回空字符串,最后发现是 RegExp 的执行上下文问题。
因为沙箱的实现类似于这种:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const evalScriptSandbox = (code, fakeWindow) => { const resolver = new Function(` return function(window) { with(window) { try { return (function() { "use strict"; ${code} })(); } catch(e) { console.log(e); } } } `);
return resolver().call(fakeWindow, fakeWindow); };
|
解决方式就是访问fakeWindow
中的 RegExp
对象时,需要返回宿主环境的 RegExp
,这和浏览器的内部实现有关系,暂时没发现其他的解决方式。
Reproduce Step :
1 2 3 4
| $ git clone https://github.com/lilong7676/js-regexp-bug.git $ cd js-regexp-bug $ npm i $ npm run start
|
⚠️
⚠️另外需要注意,在 eval、Function 构造函数中执行正则表达式时,需要转义反斜线,即:
1 2 3 4 5
| eval(` const r = /^(\d{4})-(\d{1,2})-(\d{1,2})$/ r.exec('2022-08-31'); // null console.log(RegExp.$1) // '' `)
|
在 eval 中需要写为:
1 2 3 4 5
| eval(` const r = /^(\\d{4})-(\\d{1,2})-(\\d{1,2})$/ r.exec('2022-08-31'); console.log(RegExp.$1); // 2022 `)
|
类似的原生对象,如 eval 等,也会在这种沙箱的实现方式中出现作用域问题,需要注意。