Skip to content

Commit 9c2bb94

Browse files
committed
common xss protection
1 parent 6606ac4 commit 9c2bb94

File tree

2 files changed

+92
-39
lines changed

2 files changed

+92
-39
lines changed

src/exp-parser.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
var utils = require('./utils'),
22
stringSaveRE = /"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g,
3-
stringRestoreRE = /"(\d+)"/g
3+
stringRestoreRE = /"(\d+)"/g,
4+
constructorRE = /(^|\.)constructor\(/
45

56
// Variable extraction scooped from https://github.com/RubyLouvre/avalon
67

@@ -109,6 +110,10 @@ module.exports = {
109110
* created as bindings.
110111
*/
111112
parse: function (exp, compiler) {
113+
if (constructorRE.test(exp)) {
114+
utils.warn('Unsafe expression: ' + exp)
115+
return function () {}
116+
}
112117
// extract variable names
113118
var vars = getVariables(exp)
114119
if (!vars.length) {

test/unit/specs/exp-parser.js

Lines changed: 86 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,21 @@
11
describe('UNIT: Expression Parser', function () {
22

3-
var ExpParser = require('vue/src/exp-parser')
3+
var ExpParser = require('vue/src/exp-parser'),
4+
utils = require('vue/src/utils'),
5+
oldWarn = utils.warn
6+
7+
var warnSpy = {
8+
warned: false,
9+
swapWarn: function () {
10+
utils.warn = function () {
11+
warnSpy.warned = true
12+
}
13+
},
14+
resetWarn: function () {
15+
utils.warn = oldWarn
16+
warnSpy.warned = false
17+
}
18+
}
419

520
var testCases = [
621
{
@@ -72,6 +87,50 @@ describe('UNIT: Expression Parser', function () {
7287

7388
testCases.forEach(describeCase)
7489

90+
// extra case for invalid expressions
91+
describe('invalid expression', function () {
92+
93+
before(warnSpy.swapWarn)
94+
95+
it('should capture the error and warn', function () {
96+
function noop () {}
97+
ExpParser.parse('a + "fsef', {
98+
createBinding: noop,
99+
hasKey: noop,
100+
vm: {
101+
$compiler: {
102+
bindings: {},
103+
createBinding: noop
104+
},
105+
$data: {}
106+
}
107+
})
108+
assert.ok(warnSpy.warned)
109+
})
110+
111+
after(warnSpy.resetWarn)
112+
113+
})
114+
115+
describe('Basic XSS protection', function () {
116+
117+
var cases = [{
118+
xss: true,
119+
exp: "constructor.constructor('alert(1)')()",
120+
vm: {},
121+
expectedValue: undefined
122+
},
123+
{
124+
xss: true,
125+
exp: "\"\".toString.constructor.constructor('alert(1)')()",
126+
vm: {},
127+
expectedValue: undefined
128+
}]
129+
130+
cases.forEach(describeCase)
131+
132+
})
133+
75134
function describeCase (testCase) {
76135
describe(testCase.exp, function () {
77136

@@ -91,52 +150,41 @@ describe('UNIT: Expression Parser', function () {
91150
}
92151
}
93152
},
94-
getter = ExpParser.parse(testCase.exp, compilerMock),
95153
vm = testCase.vm,
96-
vars = testCase.paths || Object.keys(vm)
154+
vars = testCase.paths || Object.keys(vm),
155+
getter
97156

98-
it('should get correct paths', function () {
99-
if (!vars.length) return
100-
assert.strictEqual(caughtMissingPaths.length, vars.length)
101-
for (var i = 0; i < vars.length; i++) {
102-
assert.strictEqual(vars[i], caughtMissingPaths[i])
103-
}
157+
if (testCase.xss) {
158+
before(warnSpy.swapWarn)
159+
after(warnSpy.resetWarn)
160+
}
161+
162+
before(function () {
163+
getter = ExpParser.parse(testCase.exp, compilerMock)
104164
})
105165

106-
it('should generate correct getter function', function () {
166+
if (!testCase.xss) {
167+
it('should get correct paths', function () {
168+
if (!vars.length) return
169+
assert.strictEqual(caughtMissingPaths.length, vars.length)
170+
for (var i = 0; i < vars.length; i++) {
171+
assert.strictEqual(vars[i], caughtMissingPaths[i])
172+
}
173+
})
174+
}
175+
176+
it('getter function should return expected value', function () {
107177
var value = getter.call(vm)
108178
assert.strictEqual(value, testCase.expectedValue)
109179
})
110180

111-
})
112-
}
113-
114-
// extra case for invalid expressions
115-
describe('invalid expression', function () {
116-
117-
it('should capture the error and warn', function () {
118-
var utils = require('vue/src/utils'),
119-
oldWarn = utils.warn,
120-
warned = false
121-
utils.warn = function () {
122-
warned = true
181+
if (testCase.xss) {
182+
it('should have warned', function () {
183+
assert.ok(warnSpy.warned)
184+
})
123185
}
124-
function noop () {}
125-
ExpParser.parse('a + "fsef', {
126-
createBinding: noop,
127-
hasKey: noop,
128-
vm: {
129-
$compiler: {
130-
bindings: {},
131-
createBinding: noop
132-
},
133-
$data: {}
134-
}
135-
})
136-
assert.ok(warned)
137-
utils.warn = oldWarn
138-
})
139186

140-
})
187+
})
188+
}
141189

142190
})

0 commit comments

Comments
 (0)