Evan You 12 роки тому
батько
коміт
ae2965cc60
4 змінених файлів з 90 додано та 80 видалено
  1. 1 0
      .jshintrc
  2. 65 57
      src/exp-parser.js
  3. 1 0
      test/.jshintrc
  4. 23 23
      test/unit/specs/exp-parser.js

+ 1 - 0
.jshintrc

@@ -9,6 +9,7 @@
     "sub": true,
     "node": true,
     "laxbreak": true,
+    "evil": true,
     "globals": {
         "console": true
     }

+ 65 - 57
src/exp-parser.js

@@ -84,7 +84,6 @@ function getRel (path, compiler) {
  *  to the VM's data it's actually properly sandboxed
  */
 function makeGetter (exp, raw) {
-    /* jshint evil: true */
     var fn
     try {
         fn = new Function(exp)
@@ -103,65 +102,74 @@ function escapeDollar (v) {
         : v
 }
 
-module.exports = {
+/**
+ *  Parse and return an anonymous computed property getter function
+ *  from an arbitrary expression, together with a list of paths to be
+ *  created as bindings.
+ */
+function parse (exp, compiler) {
+    // unicode and 'constructor' are not allowed for XSS security.
+    if (unicodeRE.test(exp) || constructorRE.test(exp)) {
+        utils.warn('Unsafe expression: ' + exp)
+        return
+    }
+    // extract variable names
+    var vars = getVariables(exp)
+    if (!vars.length) {
+        return makeGetter('return ' + exp, exp)
+    }
+    vars = utils.unique(vars)
+    var accessors = '',
+        has       = utils.hash(),
+        strings   = [],
+        // construct a regex to extract all valid variable paths
+        // ones that begin with "$" are particularly tricky
+        // because we can't use \b for them
+        pathRE = new RegExp(
+            "[^$\\w\\.](" +
+            vars.map(escapeDollar).join('|') +
+            ")[$\\w\\.]*\\b", 'g'
+        ),
+        body = ('return ' + exp)
+            .replace(stringSaveRE, saveStrings)
+            .replace(pathRE, replacePath)
+            .replace(stringRestoreRE, restoreStrings)
+    body = accessors + body
 
-    /**
-     *  Parse and return an anonymous computed property getter function
-     *  from an arbitrary expression, together with a list of paths to be
-     *  created as bindings.
-     */
-    parse: function (exp, compiler) {
-        // unicode and 'constructor' are not allowed for XSS security.
-        if (unicodeRE.test(exp) || constructorRE.test(exp)) {
-            utils.warn('Unsafe expression: ' + exp)
-            return function () {}
-        }
-        // extract variable names
-        var vars = getVariables(exp)
-        if (!vars.length) {
-            return makeGetter('return ' + exp, exp)
-        }
-        vars = utils.unique(vars)
-        var accessors = '',
-            has       = utils.hash(),
-            strings   = [],
-            // construct a regex to extract all valid variable paths
-            // ones that begin with "$" are particularly tricky
-            // because we can't use \b for them
-            pathRE = new RegExp(
-                "[^$\\w\\.](" +
-                vars.map(escapeDollar).join('|') +
-                ")[$\\w\\.]*\\b", 'g'
-            ),
-            body = ('return ' + exp)
-                .replace(stringSaveRE, saveStrings)
-                .replace(pathRE, replacePath)
-                .replace(stringRestoreRE, restoreStrings)
-        body = accessors + body
+    function saveStrings (str) {
+        var i = strings.length
+        strings[i] = str
+        return '"' + i + '"'
+    }
 
-        function saveStrings (str) {
-            var i = strings.length
-            strings[i] = str
-            return '"' + i + '"'
+    function replacePath (path) {
+        // keep track of the first char
+        var c = path.charAt(0)
+        path = path.slice(1)
+        var val = 'this.' + getRel(path, compiler) + path
+        if (!has[path]) {
+            accessors += val + ';'
+            has[path] = 1
         }
+        // don't forget to put that first char back
+        return c + val
+    }
 
-        function replacePath (path) {
-            // keep track of the first char
-            var c = path.charAt(0)
-            path = path.slice(1)
-            var val = 'this.' + getRel(path, compiler) + path
-            if (!has[path]) {
-                accessors += val + ';'
-                has[path] = 1
-            }
-            // don't forget to put that first char back
-            return c + val
-        }
+    function restoreStrings (str, i) {
+        return strings[i]
+    }
 
-        function restoreStrings (str, i) {
-            return strings[i]
-        }
+    return makeGetter(body, exp)
+}
 
-        return makeGetter(body, exp)
-    }
-}
+/**
+ *  Evaludate an expression in the context of
+ *  given compiler
+ */
+function evaluate (exp, compiler) {
+    var getter = parse(exp, compiler)
+    return getter && getter.call(compiler.vm)
+}
+
+exports.parse = parse
+exports.eval  = evaluate

+ 1 - 0
test/.jshintrc

@@ -9,6 +9,7 @@
     "sub": true,
     "node": true,
     "laxbreak": true,
+    "evil": true,
     "globals": {
         "console": true,
         "it": true,

+ 23 - 23
test/unit/specs/exp-parser.js

@@ -118,26 +118,22 @@ describe('UNIT: Expression Parser', function () {
             {
                 xss: true,
                 exp: "constructor.constructor('alert(1)')()",
-                vm: {},
-                expectedValue: undefined
+                vm: {}
             },
             {
                 xss: true,
                 exp: "\"\".toString.constructor.constructor('alert(1)')()",
-                vm: {},
-                expectedValue: undefined
+                vm: {}
             },
             {
                 xss: true,
                 exp: "\"\".toString['cons' + 'tructor']['cons' + 'tructor']('alert(1)')()",
-                vm: {},
-                expectedValue: undefined
+                vm: {}
             },
             {
                 xss: true,
                 exp: "\"\".toString['\\u0063ons' + 'tructor']['\\u0063ons' + 'tructor']('alert(1)')()",
-                vm: {},
-                expectedValue: undefined
+                vm: {}
             }
         ]
 
@@ -156,16 +152,9 @@ describe('UNIT: Expression Parser', function () {
                 compilerMock = {
                     createBinding: createBinding,
                     hasKey: function () {},
-                    vm:{
-                        $data: {},
-                        $compiler:{
-                            bindings:{},
-                            createBinding: createBinding
-                        }
-                    }
+                    vm: testCase.vm
                 },
-                vm     = testCase.vm,
-                vars   = testCase.paths || Object.keys(vm),
+                vars = testCase.paths || Object.keys(testCase.vm),
                 getter
 
             if (testCase.xss) {
@@ -178,6 +167,7 @@ describe('UNIT: Expression Parser', function () {
             })
 
             if (!testCase.xss) {
+
                 it('should get correct paths', function () {
                     if (!vars.length) return
                     assert.strictEqual(caughtMissingPaths.length, vars.length)
@@ -185,17 +175,27 @@ describe('UNIT: Expression Parser', function () {
                         assert.strictEqual(vars[i], caughtMissingPaths[i])
                     }
                 })
-            }
 
-            it('getter function should return expected value', function () {
-                var value = getter.call(vm)
-                assert.strictEqual(value, testCase.expectedValue)
-            })
+                it('getter function should return expected value', function () {
+                    var value = getter.call(testCase.vm)
+                    assert.strictEqual(value, testCase.expectedValue)
+                })
+
+                it('should eval', function () {
+                    var value = ExpParser.eval(testCase.exp, compilerMock)
+                    assert.strictEqual(value, testCase.expectedValue)
+                })
+
+            } else {
+
+                it('should return undefined getter', function () {
+                    assert.strictEqual(getter, undefined)
+                })
 
-            if (testCase.xss) {
                 it('should have warned', function () {
                     assert.ok(warnSpy.warned)
                 })
+
             }
 
         })