Skip to content

Commit f9f500c

Browse files
committed
improve expression parser
- allow more use of globals such as parseInt, encodeURI... - better warning when expression contains reserved keywords
1 parent 3eabe22 commit f9f500c

File tree

3 files changed

+55
-20
lines changed

3 files changed

+55
-20
lines changed

src/parsers/expression.js

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,29 @@ var Path = require('./path')
33
var Cache = require('../cache')
44
var expressionCache = new Cache(1000)
55

6-
var keywords =
7-
'Math,Date,break,case,catch,continue,debugger,default,' +
8-
'delete,do,else,false,finally,for,function,if,in,' +
9-
'instanceof,new,null,return,switch,this,throw,true,try,' +
10-
'typeof,var,void,while,with,undefined,abstract,boolean,' +
11-
'byte,char,class,const,double,enum,export,extends,' +
12-
'final,float,goto,implements,import,int,interface,long,' +
13-
'native,package,private,protected,public,short,static,' +
14-
'super,synchronized,throws,transient,volatile,' +
15-
'arguments,let,yield'
6+
var allowedKeywords =
7+
'Math,Date,this,true,false,null,undefined,Infinity,NaN,' +
8+
'isNaN,isFinite,decodeURI,decodeURIComponent,encodeURI,' +
9+
'encodeURIComponent,parseInt,parseFloat'
10+
var allowedKeywordsRE =
11+
new RegExp('^(' + allowedKeywords.replace(/,/g, '\\b|') + '\\b)')
12+
13+
// keywords that don't make sense inside expressions
14+
var imporperKeywords =
15+
'break,case,class,catch,const,continue,debugger,default,' +
16+
'delete,do,else,export,extends,finally,for,function,if,' +
17+
'import,in,instanceof,let,return,super,switch,throw,try,' +
18+
'var,while,with,yield,enum,await,implements,package,' +
19+
'proctected,static,interface,private,public'
20+
var imporoperKeywordsRE =
21+
new RegExp('^(' + imporperKeywords.replace(/,/g, '\\b|') + '\\b)')
1622

1723
var wsRE = /\s/g
1824
var newlineRE = /\n/g
19-
var saveRE = /[\{,]\s*[\w\$_]+\s*:|('[^']*'|"[^"]*")|new /g
25+
var saveRE = /[\{,]\s*[\w\$_]+\s*:|('[^']*'|"[^"]*")|new |typeof |void /g
2026
var restoreRE = /"(\d+)"/g
2127
var pathTestRE = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\])*$/
2228
var pathReplaceRE = /[^\w$\.]([A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\])*)/g
23-
var keywordsRE = new RegExp('^(' + keywords.replace(/,/g, '\\b|') + '\\b)')
2429
var booleanLiteralRE = /^(true|false)$/
2530

2631
/**
@@ -68,7 +73,7 @@ function save (str, isString) {
6873
function rewrite (raw) {
6974
var c = raw.charAt(0)
7075
var path = raw.slice(1)
71-
if (keywordsRE.test(path)) {
76+
if (allowedKeywordsRE.test(path)) {
7277
return raw
7378
} else {
7479
path = path.indexOf('"') > -1
@@ -100,6 +105,12 @@ function restore (str, i) {
100105
*/
101106

102107
function compileExpFns (exp, needSet) {
108+
if (imporoperKeywordsRE.test(exp)) {
109+
_.warn(
110+
'Avoid using reserved keywords in expression: '
111+
+ exp
112+
)
113+
}
103114
// reset state
104115
saved.length = 0
105116
// save strings and object literal keys
@@ -230,7 +241,9 @@ exports.parse = function (exp, needSet) {
230241
// global "Math"
231242
var res =
232243
pathTestRE.test(exp) &&
244+
// don't treat true/false as paths
233245
!booleanLiteralRE.test(exp) &&
246+
// Math constants e.g. Math.PI, Math.E etc.
234247
exp.slice(0, 5) !== 'Math.'
235248
? compilePathFns(exp)
236249
: compileExpFns(exp, needSet)

src/util/debug.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,8 @@ function enableDebug () {
3030
* @param {String} msg
3131
*/
3232

33-
var warned = false
3433
exports.warn = function (msg) {
3534
if (hasConsole && (!config.silent || config.debug)) {
36-
if (!config.debug && !warned) {
37-
warned = true
38-
console.log(
39-
'Set `Vue.config.debug = true` to enable debug mode.'
40-
)
41-
}
4235
console.warn('[Vue warn]: ' + msg)
4336
/* istanbul ignore if */
4437
if (config.debug) {

test/unit/specs/parsers/expression_spec.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,27 @@ var testCases = [
204204
scope: {},
205205
expected: true,
206206
paths: []
207+
},
208+
// typeof operator
209+
{
210+
exp: 'typeof test === "string"',
211+
scope: { test: "123" },
212+
expected: true,
213+
paths: ['test']
214+
},
215+
// isNaN
216+
{
217+
exp: 'isNaN(a)',
218+
scope: { a: 2 },
219+
expected: false,
220+
paths: ['a']
221+
},
222+
// parseFloat & parseInt
223+
{
224+
exp: 'parseInt(a, 10) + parseFloat(b)',
225+
scope: { a: 2.33, b: '3.45' },
226+
expected: 5.45,
227+
paths: ['a', 'b']
207228
}
208229
]
209230

@@ -253,16 +274,24 @@ describe('Expression Parser', function () {
253274
})
254275

255276
it('should warn on invalid expression', function () {
277+
expect(_.warn).not.toHaveBeenCalled()
256278
var res = expParser.parse('a--b"ffff')
257279
expect(_.warn).toHaveBeenCalled()
258280
})
259281

260282
if (leftHandThrows()) {
261283
it('should warn on invalid left hand expression for setter', function () {
284+
expect(_.warn).not.toHaveBeenCalled()
262285
var res = expParser.parse('a+b', true)
263286
expect(_.warn).toHaveBeenCalled()
264287
})
265288
}
289+
290+
it('should warn if expression contains improper reserved keywords', function () {
291+
expect(_.warn).not.toHaveBeenCalled()
292+
var res = expParser.parse('break + 1')
293+
expect(_.warn).toHaveBeenCalled()
294+
})
266295
})
267296
})
268297

0 commit comments

Comments
 (0)