Skip to content

Commit

Permalink
fix(VCalendar): prevent XSS from eventName function
Browse files Browse the repository at this point in the history
fixes #15757

BREAKING CHANGE: eventName can no longer render arbitrary HTML, convert
to VNodes instead
  • Loading branch information
KaelWD committed Sep 8, 2022
1 parent 1be5260 commit ade1434
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ import ripple from '../../../directives/ripple'
// Mixins
import CalendarBase from './calendar-base'

// Helpers
import { escapeHTML } from '../../../util/helpers'

// Util
import props from '../util/props'
import {
Expand Down Expand Up @@ -116,7 +113,7 @@ export default CalendarBase.extend({
eventNameFunction (): CalendarEventNameFunction {
return typeof this.eventName === 'function'
? this.eventName
: (event, timedEvent) => escapeHTML(event.input[this.eventName as string] as string || '')
: (event, timedEvent) => event.input[this.eventName as string] as string || ''
},
eventModeFunction (): CalendarEventOverlapMode {
return typeof this.eventOverlapMode === 'function'
Expand Down Expand Up @@ -303,16 +300,23 @@ export default CalendarBase.extend({
const eventSummary = () => {
const name = this.eventNameFunction(event, timedEvent)
if (event.start.hasTime) {
const eventSummaryClass = 'v-event-summary'
if (timedEvent) {
const time = timeSummary()
const delimiter = singline ? ', ' : '<br>'
const delimiter = singline ? ', ' : this.$createElement('br')

return `<span class="${eventSummaryClass}"><strong>${name}</strong>${delimiter}${time}</span>`
return this.$createElement('span', { staticClass: 'v-event-summary' }, [
this.$createElement('strong', [name]),
delimiter,
time,
])
} else {
const time = formatTime(event.start, true)

return `<span class="${eventSummaryClass}"><strong>${time}</strong> ${name}</span>`
return this.$createElement('span', { staticClass: 'v-event-summary' }, [
this.$createElement('strong', [time]),
' ',
name,
])
}
}

Expand Down Expand Up @@ -345,13 +349,10 @@ export default CalendarBase.extend({
: [this.genName(eventSummary)]
)
},
genName (eventSummary: () => string): VNode {
genName (eventSummary: () => string | VNode): VNode {
return this.$createElement('div', {
staticClass: 'pl-1',
domProps: {
innerHTML: eventSummary(),
},
})
}, [eventSummary()])
},
genPlaceholder (day: CalendarTimestamp): VNode {
const height = this.eventHeight + this.eventMarginBottom
Expand Down
4 changes: 2 additions & 2 deletions packages/vuetify/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Vue, { Component, PluginFunction, VueConstructor, DirectiveOptions } from 'vue'
import Vue, { Component, PluginFunction, VueConstructor, DirectiveOptions, VNode } from 'vue'
import './lib'
import './alacarte'
import './colors'
Expand Down Expand Up @@ -275,7 +275,7 @@ export type CalendarEventTimedFunction = (event: CalendarEvent) => boolean

export type CalendarEventCategoryFunction = (event: CalendarEvent) => string

export type CalendarEventNameFunction = (event: CalendarEventParsed, timedEvent: boolean) => string
export type CalendarEventNameFunction = (event: CalendarEventParsed, timedEvent: boolean) => string | VNode

export type DataTableFilterFunction = (value: any, search: string | null, item: any) => boolean

Expand Down

0 comments on commit ade1434

Please sign in to comment.