Skip to content

Commit

Permalink
fix(runtime-dom): sanitize wrongly passed string value as event handl…
Browse files Browse the repository at this point in the history
…er (#8953)

close #8818
  • Loading branch information
Tofandel committed Apr 15, 2024
1 parent 15ffe8f commit 7ccd453
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 5 deletions.
11 changes: 11 additions & 0 deletions packages/runtime-dom/__tests__/patchEvents.spec.ts
Expand Up @@ -192,4 +192,15 @@ describe(`runtime-dom: events patching`, () => {
testElement.dispatchEvent(new CustomEvent('foobar'))
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()
})
})
32 changes: 27 additions & 5 deletions packages/runtime-dom/src/modules/events.ts
@@ -1,8 +1,9 @@
import { hyphenate, isArray } from '@vue/shared'
import { NOOP, hyphenate, isArray, isFunction, isString } from '@vue/shared'
import {
type ComponentInternalInstance,
ErrorCodes,
callWithAsyncErrorHandling,
warn,
} from '@vue/runtime-core'

interface Invoker extends EventListener {
Expand Down Expand Up @@ -36,20 +37,27 @@ export function patchEvent(
el: Element & { [veiKey]?: Record<string, Invoker | undefined> },
rawName: string,
prevValue: EventValue | null,
nextValue: EventValue | null,
nextValue: EventValue | unknown,
instance: ComponentInternalInstance | null = null,
) {
// vei = vue event invokers
const invokers = el[veiKey] || (el[veiKey] = {})
const existingInvoker = invokers[rawName]
if (nextValue && existingInvoker) {
// patch
existingInvoker.value = nextValue
existingInvoker.value = __DEV__
? sanitizeEventValue(nextValue, rawName)
: (nextValue as EventValue)
} else {
const [name, options] = parseName(rawName)
if (nextValue) {
// 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)
} else if (existingInvoker) {
// remove
Expand Down Expand Up @@ -116,6 +124,18 @@ function createInvoker(
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(
e: Event,
value: EventValue,
Expand All @@ -126,7 +146,9 @@ function patchStopImmediatePropagation(
originalStop.call(e)
;(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 {
return value
}
Expand Down

0 comments on commit 7ccd453

Please sign in to comment.