mirror of https://github.com/vuejs/core.git
fix(runtime-dom): sanitize wrongly passed string value as event handler (#8953)
close #8818
This commit is contained in:
parent
15ffe8f2c9
commit
7ccd453dd0
|
@ -192,4 +192,15 @@ describe(`runtime-dom: events patching`, () => {
|
||||||
testElement.dispatchEvent(new CustomEvent('foobar'))
|
testElement.dispatchEvent(new CustomEvent('foobar'))
|
||||||
expect(fn2).toHaveBeenCalledTimes(1)
|
expect(fn2).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('handles an unknown type', () => {
|
||||||
|
const el = document.createElement('div')
|
||||||
|
patchProp(el, 'onClick', null, 'test')
|
||||||
|
el.dispatchEvent(new Event('click'))
|
||||||
|
expect(
|
||||||
|
'[Vue warn]: Wrong type passed to the event invoker, ' +
|
||||||
|
'did you maybe forget @ or : in front of your prop?' +
|
||||||
|
'\nReceived onClick="test"',
|
||||||
|
).toHaveBeenWarned()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { hyphenate, isArray } from '@vue/shared'
|
import { NOOP, hyphenate, isArray, isFunction, isString } from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
type ComponentInternalInstance,
|
type ComponentInternalInstance,
|
||||||
ErrorCodes,
|
ErrorCodes,
|
||||||
callWithAsyncErrorHandling,
|
callWithAsyncErrorHandling,
|
||||||
|
warn,
|
||||||
} from '@vue/runtime-core'
|
} from '@vue/runtime-core'
|
||||||
|
|
||||||
interface Invoker extends EventListener {
|
interface Invoker extends EventListener {
|
||||||
|
@ -36,7 +37,7 @@ export function patchEvent(
|
||||||
el: Element & { [veiKey]?: Record<string, Invoker | undefined> },
|
el: Element & { [veiKey]?: Record<string, Invoker | undefined> },
|
||||||
rawName: string,
|
rawName: string,
|
||||||
prevValue: EventValue | null,
|
prevValue: EventValue | null,
|
||||||
nextValue: EventValue | null,
|
nextValue: EventValue | unknown,
|
||||||
instance: ComponentInternalInstance | null = null,
|
instance: ComponentInternalInstance | null = null,
|
||||||
) {
|
) {
|
||||||
// vei = vue event invokers
|
// vei = vue event invokers
|
||||||
|
@ -44,12 +45,19 @@ export function patchEvent(
|
||||||
const existingInvoker = invokers[rawName]
|
const existingInvoker = invokers[rawName]
|
||||||
if (nextValue && existingInvoker) {
|
if (nextValue && existingInvoker) {
|
||||||
// patch
|
// patch
|
||||||
existingInvoker.value = nextValue
|
existingInvoker.value = __DEV__
|
||||||
|
? sanitizeEventValue(nextValue, rawName)
|
||||||
|
: (nextValue as EventValue)
|
||||||
} else {
|
} else {
|
||||||
const [name, options] = parseName(rawName)
|
const [name, options] = parseName(rawName)
|
||||||
if (nextValue) {
|
if (nextValue) {
|
||||||
// add
|
// add
|
||||||
const invoker = (invokers[rawName] = createInvoker(nextValue, instance))
|
const invoker = (invokers[rawName] = createInvoker(
|
||||||
|
__DEV__
|
||||||
|
? sanitizeEventValue(nextValue, rawName)
|
||||||
|
: (nextValue as EventValue),
|
||||||
|
instance,
|
||||||
|
))
|
||||||
addEventListener(el, name, invoker, options)
|
addEventListener(el, name, invoker, options)
|
||||||
} else if (existingInvoker) {
|
} else if (existingInvoker) {
|
||||||
// remove
|
// remove
|
||||||
|
@ -116,6 +124,18 @@ function createInvoker(
|
||||||
return invoker
|
return invoker
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sanitizeEventValue(value: unknown, propName: string): EventValue {
|
||||||
|
if (isFunction(value) || isArray(value)) {
|
||||||
|
return value as EventValue
|
||||||
|
}
|
||||||
|
warn(
|
||||||
|
`Wrong type passed to the event invoker, did you maybe forget @ or : ` +
|
||||||
|
`in front of your prop?\nReceived ` +
|
||||||
|
`${propName}=${isString(value) ? JSON.stringify(value) : `[${typeof value}]`}`,
|
||||||
|
)
|
||||||
|
return NOOP
|
||||||
|
}
|
||||||
|
|
||||||
function patchStopImmediatePropagation(
|
function patchStopImmediatePropagation(
|
||||||
e: Event,
|
e: Event,
|
||||||
value: EventValue,
|
value: EventValue,
|
||||||
|
@ -126,7 +146,9 @@ function patchStopImmediatePropagation(
|
||||||
originalStop.call(e)
|
originalStop.call(e)
|
||||||
;(e as any)._stopped = true
|
;(e as any)._stopped = true
|
||||||
}
|
}
|
||||||
return value.map(fn => (e: Event) => !(e as any)._stopped && fn && fn(e))
|
return (value as Function[]).map(
|
||||||
|
fn => (e: Event) => !(e as any)._stopped && fn && fn(e),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue