mip-sandbox
Advanced tools
Comparing version 1.0.4 to 1.0.5
@@ -9,5 +9,5 @@ /** | ||
module.exports = function (code) { | ||
var ast = replace(code) | ||
module.exports = function (code, keywords, options) { | ||
var ast = replace(code, keywords, options && options.prefix) | ||
return escodegen.generate(ast) | ||
} |
@@ -9,5 +9,5 @@ /** | ||
module.exports = function (code, options) { | ||
var ast = replace(code) | ||
return escodegen.generate(ast, options) | ||
module.exports = function (code, keywords, options) { | ||
var ast = replace(code, keywords, options && options.prefix) | ||
return escodegen.generate(ast, options && options.escodegen) | ||
} |
@@ -6,3 +6,3 @@ /** | ||
var esprima = require('esprima') | ||
var mark = require('./global-mark') | ||
var estraverse = require('estraverse') | ||
@@ -12,10 +12,9 @@ var is = require('./utils/is') | ||
module.exports = function (code, fn, type) { | ||
var ast = esprima.parseModule(code, { | ||
range: true, | ||
loc: true | ||
}) | ||
var ast | ||
if (typeof code === 'string') { | ||
ast = mark(code) | ||
} else { | ||
ast = code | ||
} | ||
mark(ast) | ||
scope(ast) | ||
estraverse[type || 'traverse'](ast, { | ||
@@ -46,180 +45,2 @@ enter: function (node, parent) { | ||
function mark (ast) { | ||
// 作用域内变量定义 Identifier case | ||
// 1. import | ||
// 1. import a from 'xxx' 的 a | ||
// 2. import {a as b} from 'xxx' 的 b | ||
// 3. import * as c from 'xxx' 的 c | ||
// 2. var/let/const | ||
// 1. var a = 1 的 a | ||
// 2. var {a: b} = {a: 1} 中的 b | ||
// 3. var [a, b] 中的 a, b | ||
// 4. var [a, ...b] 中的 b | ||
// 5. var {a = 1} 中的 a | ||
// 3. FunctionDeclaration | ||
// 1. function a() {} 中的 a | ||
// 4. ClassDeclaration | ||
// 1. Class A extends B {} 中的 A | ||
// 5. Function params | ||
// 1. function a(args) {} 中的 args | ||
// 2. var a = function (args) {} 中的 args | ||
// 3. var b = (args) => {} 中的 args | ||
// 6. CatchClause | ||
// 1. try {} catch (e) {} 的 e | ||
// 2. 这个 e 有可能会以解构的形式去写 | ||
// 无需关心的 Identifier case | ||
// 1. MemberExpression | ||
// 1. a.b.c 中的 b、c 但 a[b].c 中只有 c 不需要关心 | ||
// 2. Property 包括解构赋值和普通的 Object 定义 | ||
// 1. {a: b} 中的 a 但 {[a]: b} 中的 a 需要关心 | ||
// 3. MethodDefinition | ||
// 1. class A {a() {}} 中的 a 但 class A {[a]() {}} 中的 a 需要关心 | ||
// 4. FunctionExpression | ||
// 1. var a = function b() {} 中的 b 因为 b() 是无效的 | ||
// 5. import | ||
// 1. import {a as b} from 'xxx' 中的 a | ||
// 变量提升 | ||
// 1. var | ||
// var a = 1 中的 a | ||
// var {a: b} = {} 中的 b | ||
// var [a, {b: {c: [d]}}, f = 1, ...e] = [] 中的 a d f e | ||
// 2. function | ||
// function a() {} 中的 a | ||
estraverse.traverse(ast, { | ||
enter: function (node, parent) { | ||
// 标记变量声明 | ||
if (is(node, /^Import\w*Specifier$/)) { | ||
node.local.isVar = true | ||
if (node.imported && is(node.imported, 'Identifier')) { | ||
node.imported.isIgnore = true | ||
} | ||
} else if (is(node, 'VariableDeclaration')) { | ||
if (node.kind === 'var') { | ||
node.declarations.forEach(elem => { | ||
elem.isLift = true | ||
}) | ||
} | ||
} else if (is(node, 'VariableDeclarator')) { | ||
if (is(node.id, 'Identifier')) { | ||
node.id.isVar = true | ||
} | ||
node.id.isLift = node.isLift | ||
} else if (is(node, 'ObjectPattern')) { | ||
node.properties.forEach(function (elem) { | ||
if (is(elem.value, 'Identifier')) { | ||
elem.value.isVar = true | ||
} | ||
elem.value.isLift = node.isLift | ||
}) | ||
} else if (is(node, 'ArrayPattern')) { | ||
node.elements.forEach(function (elem) { | ||
if (is(elem, 'Identifier')) { | ||
elem.isVar = true | ||
} | ||
elem.isLift = node.isLift | ||
}) | ||
} else if (is(node, 'AssignmentPattern')) { | ||
node.left.isVar = true | ||
node.left.isLift = node.isLift | ||
} else if (is(node, 'RestElement')) { | ||
if (is(node.argument, 'Identifier')) { | ||
node.argument.isVar = true | ||
} | ||
node.argument.isLift = node.isLift | ||
} else if (is(node, /Function/)) { | ||
if (node.id) { | ||
if (node.type === 'FunctionDeclaration') { | ||
node.id.isVar = true | ||
node.id.isLift = true | ||
} else { | ||
// FunctionExpression 的 id 没用 | ||
// var a = function b () {} 这个没法在最下面使用 b() | ||
node.id.isIgnore = true | ||
} | ||
node.id.isVar = true | ||
} | ||
node.params.forEach(function (elem) { | ||
if (is(elem, 'Identifier')) { | ||
elem.isVar = true | ||
} | ||
}) | ||
} else if (is(node, 'ClassDeclaration')) { | ||
node.id.isVar = true | ||
} else if (is(node, 'CatchClause')) { | ||
if (is(node.param, 'Identifier')) { | ||
node.param.isVar = true | ||
} | ||
} else if (is(node, 'MemberExpression')) { | ||
// a.b.c 的 b c 忽略 | ||
if (is(node.property, 'Identifier') && !node.computed) { | ||
node.property.isIgnore = true | ||
} | ||
} else if (is(node, 'Property')) { | ||
if (is(node.key, 'Identifier') && !node.computed) { | ||
node.key.isIgnore = true | ||
} | ||
} else if (is(node, 'MethodDefinition') && !node.computed) { | ||
node.key.isIgnore = true | ||
} | ||
} | ||
}) | ||
} | ||
function scope (ast, parentAst) { | ||
// 标记 scope case | ||
// 1. Program 是 top scope | ||
// 2. *Function* 其中 ArrowFunctionExpression 的 body 不一定带 BlockStatement | ||
// 1. FunctionDeclaration | ||
// 2. FunctionExpression | ||
// 3. ArrowFunctionExpression | ||
// 3. BlockStatement 只会影响 const/let | ||
// 4. ForStatement 只会影响 const/let | ||
parentAst = parentAst || [] | ||
estraverse.traverse(ast, { | ||
enter: function (node) { | ||
if (node === ast) { | ||
return | ||
} | ||
if (is(node, /Function/) || | ||
is(node, 'BlockStatement') || | ||
is(node, 'ForStatement') || | ||
is(node, 'CatchClause') | ||
) { | ||
scope(node, parentAst.concat(ast)) | ||
this.skip() | ||
return | ||
} | ||
if (!is(node, 'Identifier') || | ||
!node.isVar | ||
) { | ||
return | ||
} | ||
if (node.isLift && parentAst.length) { | ||
for (var i = parentAst.length - 1; i > -1; i--) { | ||
if (is(parentAst[i], 'Program') || is(parentAst[i], /Function/)) { | ||
parentAst[i].vars = parentAst[i].vars || [] | ||
parentAst[i].vars.push(node.name) | ||
break | ||
} | ||
} | ||
} else { | ||
ast.vars = ast.vars || [] | ||
ast.vars.push(node.name) | ||
} | ||
} | ||
}) | ||
} | ||
function hasBinding (name, context) { | ||
@@ -226,0 +47,0 @@ var parents = context.parents() |
@@ -5,4 +5,5 @@ /** | ||
*/ | ||
var keys = require('./utils/keys') | ||
var WINDOW_ORIGINAL = [ | ||
var ORIGINAL = [ | ||
'Array', | ||
@@ -16,5 +17,2 @@ 'ArrayBuffer', | ||
'Error', | ||
'File', | ||
'FileList', | ||
'FileReader', | ||
'Float32Array', | ||
@@ -24,4 +22,2 @@ 'Float64Array', | ||
'Headers', | ||
'Image', | ||
'ImageBitmap', | ||
'Infinity', | ||
@@ -34,5 +30,3 @@ 'Int16Array', | ||
'Math', | ||
'MutationObserver', | ||
'NaN', | ||
'Notification', | ||
'Number', | ||
@@ -61,8 +55,5 @@ 'Object', | ||
'WritableStream', | ||
'addEventListener', | ||
'cancelAnimationFrame', | ||
'clearInterval', | ||
'clearTimeout', | ||
'console', | ||
'createImageBitmap', | ||
'decodeURI', | ||
@@ -76,4 +67,2 @@ 'decodeURIComponent', | ||
'getComputedStyle', | ||
// 待定 | ||
'history', | ||
'innerHeight', | ||
@@ -85,4 +74,2 @@ 'innerWidth', | ||
'localStorage', | ||
// 待定 | ||
'location', | ||
'length', | ||
@@ -95,4 +82,2 @@ 'matchMedia', | ||
'parseInt', | ||
'removeEventListener', | ||
'requestAnimationFrame', | ||
'screen', | ||
@@ -103,8 +88,4 @@ 'screenLeft', | ||
'screenY', | ||
'scroll', | ||
'scrollBy', | ||
'scrollTo', | ||
'scrollX', | ||
'scrollY', | ||
'scrollbars', | ||
'sessionStorage', | ||
@@ -114,16 +95,8 @@ 'setInterval', | ||
'undefined', | ||
'unescape', | ||
'webkitCancelAnimationFrame', | ||
'webkitRequestAnimationFrame' | ||
'unescape' | ||
] | ||
var WINDOW_CUSTOM = [ | ||
'document', | ||
'window', | ||
'MIP' | ||
] | ||
var RESERVED = [ | ||
'arguments', | ||
'MIP', | ||
// 'MIP', | ||
'require', | ||
@@ -135,24 +108,116 @@ 'module', | ||
var DOCUMENT_ORIGINAL = [ | ||
'head', | ||
'body', | ||
'title', | ||
'cookie', | ||
'referrer', | ||
'readyState', | ||
'documentElement', | ||
'createElement', | ||
'createDcoumentFragment', | ||
'getElementById', | ||
'getElementsByClassName', | ||
'getElementsByTagName', | ||
'querySelector', | ||
'querySelectorAll' | ||
] | ||
var SANDBOX_STRICT = { | ||
name: 'strict', | ||
access: 'readyonly', | ||
host: 'window', | ||
mount: 'MIP.sandbox.strict', | ||
children: ORIGINAL.concat([ | ||
{ | ||
name: 'document', | ||
host: 'document', | ||
children: [ | ||
'cookie' | ||
] | ||
}, | ||
{ | ||
name: 'location', | ||
host: 'location', | ||
access: 'readonly', | ||
children: [ | ||
'href', | ||
'protocol', | ||
'host', | ||
'hostname', | ||
'port', | ||
'pathname', | ||
'search', | ||
'hash', | ||
'origin' | ||
] | ||
}, | ||
{ | ||
name: 'MIP', | ||
host: 'MIP', | ||
children: [ | ||
'watch', | ||
'setData', | ||
'viewPort', | ||
'util', | ||
'sandbox' | ||
] | ||
}, | ||
{ | ||
name: 'window', | ||
host: 'MIP.sandbox.strict' | ||
} | ||
]) | ||
} | ||
var SANDBOX = { | ||
access: 'readonly', | ||
host: 'window', | ||
mount: 'MIP.sandbox', | ||
children: ORIGINAL.concat([ | ||
'File', | ||
'FileList', | ||
'FileReader', | ||
'Image', | ||
'ImageBitmap', | ||
'MutationObserver', | ||
'Notification', | ||
'addEventListener', | ||
'cancelAnimationFrame', | ||
'createImageBitmap', | ||
// 待定 | ||
'history', | ||
// 待定 | ||
'location', | ||
'removeEventListener', | ||
'requestAnimationFrame', | ||
'scrollBy', | ||
'scrollTo', | ||
'scroll', | ||
'scrollbars', | ||
'webkitCancelAnimationFrame', | ||
'webkitRequestAnimationFrame', | ||
{ | ||
name: 'document', | ||
host: 'document', | ||
children: [ | ||
'head', | ||
'body', | ||
'title', | ||
'cookie', | ||
'referrer', | ||
'readyState', | ||
'documentElement', | ||
'createElement', | ||
'createDcoumentFragment', | ||
'getElementById', | ||
'getElementsByClassName', | ||
'getElementsByTagName', | ||
'querySelector', | ||
'querySelectorAll' | ||
] | ||
}, | ||
{ | ||
name: 'window', | ||
host: 'MIP.sandbox' | ||
}, | ||
{ | ||
name: 'MIP', | ||
host: 'MIP' | ||
}, | ||
SANDBOX_STRICT | ||
]) | ||
} | ||
module.exports = { | ||
WINDOW_ORIGINAL: WINDOW_ORIGINAL, | ||
WINDOW_CUSTOM: WINDOW_CUSTOM, | ||
DOCUMENT_ORIGINAL: DOCUMENT_ORIGINAL, | ||
RESERVED: RESERVED | ||
ORIGINAL: ORIGINAL, | ||
RESERVED: RESERVED, | ||
SANDBOX: SANDBOX, | ||
SANDBOX_STRICT: SANDBOX_STRICT, | ||
WHITELIST: keys(SANDBOX.children).concat(RESERVED), | ||
WHITELIST_STRICT: keys(SANDBOX_STRICT.children).concat(RESERVED), | ||
WHITELIST_RESERVED: ORIGINAL.concat(RESERVED) | ||
} |
@@ -11,13 +11,4 @@ /** | ||
var sandbox = {} | ||
var sandbox = defUtils.traverse(keywords.SANDBOX) | ||
defUtils.defs(sandbox, keywords.WINDOW_ORIGINAL) | ||
defUtils.def(sandbox, 'window', sandbox) | ||
var sandboxDocument = {} | ||
defUtils.defs(sandboxDocument, keywords.DOCUMENT_ORIGINAL, {host: document, setter: true}) | ||
defUtils.def(sandbox, 'document', sandboxDocument) | ||
defUtils.def(sandbox, 'MIP', window.MIP) | ||
/** | ||
@@ -33,8 +24,21 @@ * this sandbox,避免诸如 | ||
* @param {Object} that this | ||
* @return {Object} safe this | ||
* @return {Function} 返回 safe this 的方法 | ||
*/ | ||
defUtils.def(sandbox, 'this', function (that) { | ||
return that === window ? sandbox : that === document ? sandbox.document : that | ||
}) | ||
function safeThis (sandbox) { | ||
return function (that) { | ||
return that === window ? sandbox : that === document ? sandbox.document : that | ||
} | ||
} | ||
defUtils.def(sandbox, 'this', safeThis(sandbox)) | ||
defUtils.def(sandbox.strict, 'this', safeThis(sandbox.strict)) | ||
defUtils.def(sandbox, 'WHITELIST', keywords.WHITELIST) | ||
defUtils.def(sandbox, 'WHITELIST_STRICT', keywords.WHITELIST_STRICT) | ||
defUtils.def(sandbox, 'WHITELIST_RESERVED', keywords.WHITELIST_RESERVED) | ||
defUtils.def(sandbox.strict, 'WHITELIST', keywords.WHITELIST) | ||
defUtils.def(sandbox.strict, 'WHITELIST_STRICT', keywords.WHITELIST_STRICT) | ||
defUtils.def(sandbox.strict, 'WHITELIST_RESERVED', keywords.WHITELIST_RESERVED) | ||
module.exports = sandbox |
@@ -7,11 +7,7 @@ /** | ||
var detect = require('./global-detect') | ||
var keywords = require('./keywords') | ||
var is = require('./utils/is') | ||
var WINDOW_SAFE_KEYWORDS = keywords.WINDOW_ORIGINAL | ||
.concat(keywords.RESERVED) | ||
.concat(keywords.WINDOW_CUSTOM) | ||
module.exports = function (code) { | ||
module.exports = function (code, keywords) { | ||
var unsafeList = [] | ||
keywords = keywords || [] | ||
@@ -23,10 +19,7 @@ detect(code, function (node, parent, ast) { | ||
if (WINDOW_SAFE_KEYWORDS.indexOf(node.name) === -1) { | ||
if (keywords.indexOf(node.name) === -1) { | ||
unsafeList.push(node) | ||
} | ||
}) | ||
if (unsafeList.length) { | ||
return unsafeList | ||
} | ||
return unsafeList | ||
} |
@@ -7,22 +7,17 @@ /** | ||
var detect = require('./global-detect') | ||
var keywords = require('./keywords') | ||
var is = require('./utils/is') | ||
var t = require('./utils/type') | ||
var WINDOW_SAFE_KEYWORDS = keywords.WINDOW_ORIGINAL | ||
.concat(keywords.RESERVED) | ||
function sandboxExpression (name) { | ||
return t.memberExpression( | ||
t.memberExpression( | ||
t.identifier('MIP'), | ||
t.identifier('sandbox') | ||
), | ||
t.identifier(name) | ||
) | ||
function memberExpression (name) { | ||
var keys = name.split('.') | ||
var expression = t.identifier(keys[0]) | ||
for (var i = 1; i < keys.length; i++) { | ||
expression = t.memberExpression(expression, t.identifier(keys[i])) | ||
} | ||
return expression | ||
} | ||
function safeThisExpression () { | ||
function safeThisExpression (prefix) { | ||
return t.callExpression( | ||
sandboxExpression('this'), | ||
memberExpression(prefix + '.this'), | ||
[t.thisExpression()] | ||
@@ -32,3 +27,6 @@ ) | ||
module.exports = function (code) { | ||
module.exports = function (code, keywords, prefix) { | ||
keywords = keywords || [] | ||
prefix = prefix || 'MIP.sandbox' | ||
return detect( | ||
@@ -39,6 +37,6 @@ code, | ||
this.skip() | ||
return safeThisExpression() | ||
return safeThisExpression(prefix) | ||
} | ||
if (WINDOW_SAFE_KEYWORDS.indexOf(node.name) === -1) { | ||
if (keywords.indexOf(node.name) === -1) { | ||
this.skip() | ||
@@ -48,3 +46,3 @@ if (is(parent, 'Property', {shorthand: true})) { | ||
} | ||
return sandboxExpression(node.name) | ||
return memberExpression(prefix + '.' + node.name) | ||
} | ||
@@ -51,0 +49,0 @@ }, |
@@ -6,49 +6,128 @@ /** | ||
module.exports = { | ||
defs: function (obj, props, {host = window, writable = false} = {}) { | ||
Object.defineProperties( | ||
obj, | ||
props.reduce(function (obj, key) { | ||
obj[key] = { | ||
enumberable: true, | ||
configurable: false | ||
} | ||
var utils = { | ||
// 方便测试用 | ||
globals: typeof window === 'object' ? window : {}, | ||
traverse: traverse, | ||
def: def | ||
} | ||
if (typeof host[key] === 'function') { | ||
if (/^[A-Z]/.test(key)) { | ||
// class | ||
obj[key].value = host[key] | ||
obj[key].writable = false | ||
} else { | ||
// 不然直接 MIP.sandbox.setTimeout(() => {}) 会报错 | ||
obj[key].get = function () { | ||
return host[key].bind(host) | ||
} | ||
} | ||
} else { | ||
obj[key].get = function () { | ||
return host[key] | ||
} | ||
function prop (obj, name) { | ||
var keys = name.split('.') | ||
for (var i = 0; i < keys.length; i++) { | ||
if (!obj) { | ||
return | ||
} | ||
obj = obj[keys[i]] | ||
} | ||
return obj | ||
} | ||
obj[key].set = function (val) { | ||
// 只是防止用户篡改而不是不让用户写 | ||
if (writable) { | ||
host[key] = val | ||
} | ||
} | ||
function merge (a, b, exclude) { | ||
var keys = Object.keys(b) | ||
for (var i = 0; i < keys.length; i++) { | ||
if (!exclude || exclude.indexOf(keys[i]) === -1) { | ||
a[keys[i]] = b[keys[i]] | ||
} | ||
} | ||
return a | ||
} | ||
function traverse (node, parent, mount) { | ||
mount = mount || {} | ||
var host | ||
if (typeof node.host === 'string') { | ||
host = prop(utils.globals, node.host) | ||
if (host == null) { | ||
host = mount[node.host] | ||
} | ||
if (host == null) { | ||
throw Error('host ' + node.host + ' not found.') | ||
} | ||
} else { | ||
host = utils.globals | ||
} | ||
if (!node.children && parent) { | ||
def(parent, node.name, host) | ||
return | ||
} | ||
var options = merge({}, node, ['children']) | ||
merge(options, {host: host}) | ||
var obj = {} | ||
if (node.mount) { | ||
mount[node.mount] = obj | ||
} | ||
node.children.forEach(function (child) { | ||
if (typeof child === 'string') { | ||
def(obj, child, child, options) | ||
} else { | ||
traverse(child, obj, mount) | ||
} | ||
}) | ||
if (node.name) { | ||
def(parent, node.name, obj) | ||
return | ||
} | ||
return obj | ||
} | ||
function def (obj, name, props, options) { | ||
options = options || {} | ||
var descriptor | ||
if (options.type === 'raw') { | ||
descriptor = props | ||
} else if (typeof props === 'string' && options.type !== 'getter') { | ||
descriptor = { | ||
enumerable: true, | ||
configurable: false | ||
} | ||
var host = options.host || utils.globals | ||
if (typeof host[name] === 'function') { | ||
if (/^[A-Z]/.test(name)) { | ||
// class | ||
descriptor.value = host[name] | ||
descriptor.writable = false | ||
} else { | ||
// 不然直接 MIP.sandbox.setTimeout(() => {}) 会报错 | ||
descriptor.get = function () { | ||
return host[name].bind(host) | ||
} | ||
} | ||
} else { | ||
descriptor.get = function () { | ||
return host[name] | ||
} | ||
return obj | ||
}, {}) | ||
) | ||
}, | ||
def: function (obj, prop, getter) { | ||
Object.defineProperty(obj, prop, { | ||
enumberable: true, | ||
descriptor.set = function (val) { | ||
// 只是防止用户篡改而不是不让用户写 | ||
if (options.access !== 'readonly') { | ||
host[name] = val | ||
} | ||
} | ||
} | ||
} else { | ||
descriptor = { | ||
enumerable: true, | ||
get: function () { | ||
return getter | ||
return props | ||
} | ||
}) | ||
} | ||
} | ||
Object.defineProperty(obj, name, descriptor) | ||
} | ||
module.exports = utils |
{ | ||
"name": "mip-sandbox", | ||
"version": "1.0.4", | ||
"version": "1.0.5", | ||
"description": "sandbox tools for MIP project", | ||
"main": "lib/sandbox.js", | ||
"scripts": { | ||
"test": "mocha test/*.spec.js", | ||
"test": "sh test/test.sh", | ||
"release": "sh publish.sh" | ||
@@ -29,2 +29,3 @@ }, | ||
"estraverse": "^4.2.0", | ||
"esutils": "^2.0.2", | ||
"source-map": "~0.6.1" | ||
@@ -34,4 +35,11 @@ }, | ||
"chai": "^4.1.2", | ||
"mocha": "^5.2.0" | ||
"karma": "^2.0.2", | ||
"karma-chrome-launcher": "^2.2.0", | ||
"karma-mocha": "^1.3.0", | ||
"karma-sourcemap-loader": "^0.3.7", | ||
"karma-spec-reporter": "0.0.32", | ||
"karma-webpack": "^3.0.0", | ||
"mocha": "^5.2.0", | ||
"webpack": "^4.12.0" | ||
} | ||
} |
107
README.md
@@ -31,4 +31,21 @@ # mip-sandbox | ||
使用 `mip-sandbox/lib/unsafe-detect` 方法进行不安全全局变量检测,该函数的定义如下 | ||
```javascript | ||
/** | ||
* 不安全全局变量检测 | ||
* | ||
* @params {string|AST} code 代码字符串或代码 AST | ||
* @params {Array=} keywords 安全全局变量声明列表, | ||
* 在默认情况下,所有全局变量包括 window document 等均认为不安全, | ||
* 需要传入该参数进行条件过滤 | ||
* @return {Array.<ASTNode>} 不安全全局变量列表 | ||
*/ | ||
``` | ||
使用例子如下: | ||
```javascript | ||
var detect = require('mip-sandbox/lib/unsafe-detect') | ||
var keywords = require('mip-sandbox/lib/keywords') | ||
@@ -40,3 +57,5 @@ var code = ` | ||
var results = detect(code) | ||
// 严格模式 请使用 keywords.WHITLIST_STRICT | ||
// 在前端使用时,可通过 MIP.sandbox.WHITELIST 去拿该列表 | ||
var results = detect(code, keywords.WHITELIST) | ||
@@ -58,4 +77,24 @@ console.log(result) | ||
使用 `mip-sandbox/lib/generate` 方法进行不安全全局变量替换,该函数的定义如下 | ||
```javascript | ||
/** | ||
* 不安全全局变量替换 | ||
* | ||
* @param {string|AST} code 代码字符串或代码 AST | ||
* @param {Array=} keywords 安全全局变量声明列表, | ||
* 在默认情况下,所有全局变量包括 window document 等均认为不安全, | ||
* 需要传入该参数进行条件过滤 | ||
* @param {Object=} options options | ||
* @param {string=} options.prefix 默认全局变量注入的前缀,默认为 MIP.sandbox | ||
* @param {Object=} options.escodegen 透传给 escodegen 的参数 | ||
* @return {string} 替换后的代码字符串 | ||
*/ | ||
``` | ||
使用例子如下: | ||
```javascript | ||
var generate = require('mip-sandbox/lib/generate') | ||
var keywords = require('mip-sandbox/lib/keywords') | ||
@@ -68,3 +107,3 @@ var code = ` | ||
var result = generate(code) | ||
var result = generate(code, keywords.WHITELIST) | ||
@@ -76,3 +115,16 @@ console.log(result) | ||
// MIP.sandbox.window.console.log(a) | ||
``` | ||
对于严格模式下 window 需要替换成 `MIP.sandbox.strict.window` 在这种情况下,需要传入第三个参数: | ||
**options.prefix** | ||
默认的 options.prefix === 'MIP.sandbox',在严格下可以传入 MIP.sandbox.strict,得到的结果将如下所示: | ||
```javascript | ||
var result = generate(code, keywords.WHITELIST, {prefix: 'MIP.sandbox.strict'}) | ||
// var a = 1 | ||
// console.log(MIP.sandbox.b) | ||
// MIP.sandbox.strict.window.console.log(a) | ||
``` | ||
@@ -82,3 +134,3 @@ | ||
该方法的第二个参数 options 将会透传给 escodegen 因此比如需要返回 sourcemap 的话,请于第二个参数传入 sourcemap 相关参数 | ||
该方法的第三个参数 options.escodegen 将会透传给 escodegen 因此比如需要返回 sourcemap 的话,请于第二个参数传入 sourcemap 相关参数 | ||
@@ -88,5 +140,7 @@ 如: | ||
```javascript | ||
var output = generate(code, { | ||
sourceMap: 'name', | ||
sourceMapWithCode: true | ||
var output = generate(code, keywords.WHITELIST, { | ||
escodegen: { | ||
sourceMap: 'name', | ||
sourceMapWithCode: true | ||
} | ||
}) | ||
@@ -98,9 +152,39 @@ | ||
对于不需要生成 sourceMap 的情况,可以使用 generate-lite 来去掉 source-map 相关代码以减小打包体积: | ||
对于不需要生成 sourceMap 的情况,可以使用 generate-lite 来去掉 source-map 相关代码以减小打包体积。 | ||
该方法的定义如下: | ||
```javascript | ||
/** | ||
* 不安全全局变量替换 | ||
* | ||
* @param {string|AST} code 代码字符串或代码 AST | ||
* @param {Array=} keywords 安全全局变量声明列表, | ||
* 在默认情况下,所有全局变量包括 window document 等均认为不安全, | ||
* 需要传入该参数进行条件过滤 | ||
* @return {string} 替换后的代码字符串 | ||
*/ | ||
``` | ||
```javascript | ||
var generate = require('mip-sandbox/lib/generate-lite') | ||
var code = generate(code) | ||
var keywords = require('mip-sandbox/lib/keywords') | ||
var code = generate(code, keywords.WHITELIST) | ||
``` | ||
### 沙盒检测替换优化 | ||
在某些场景下需要同时使用 detect 和 generate 去实现功能,这时,如果对这两个方法传入的 code 都是字符串的话,就需要对字符串做两次 ast 解析和标记,为了解决这个问题,可以调用 global-mark 生成解析标记好的 ast,再将 ast 传入 detect 和 generate 中,从而提高效率: | ||
```javascript | ||
var mark = require('mip-sandbox/lib/global-mark') | ||
var detect = require('mip-sandbox/lib/unsafe-detect') | ||
var generate = require('mip-sandbox/lib/generate') | ||
var keywords = require('mip-sandbox/lib/keywords') | ||
var ast - mark(code) | ||
var unsafeList = detect(ast, keywords.WHITELIST) | ||
var generated = generated(ast, keywords.WHITELIST) | ||
``` | ||
## 沙盒替换规则 | ||
@@ -150,2 +234,8 @@ | ||
## 严格模式 | ||
在 mip-script 中,理论上只允许进行数据运算和发请求等等操作,不允许直接操作 DOM ,因此在 mip-script 中写的 js 将会以沙盒的严格模式进行全局变量替换,比如 window 会被替换成 `MIP.sandbox.strict.window`、 this 将会替换成 `MIP..strict.this(this)`。 | ||
其中 MIP.sandbox.strict 是 MIP.sandbox 的子集。 | ||
## 可用全局变量 | ||
@@ -292,3 +382,2 @@ | ||
``` | ||
@@ -295,0 +384,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
218903
17
5253
377
4
9
+ Addedesutils@^2.0.2
+ Addedesutils@2.0.3(transitive)