diff options
author | Christoph Wurst <ChristophWurst@users.noreply.github.com> | 2021-09-14 20:06:47 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-14 20:06:47 +0200 |
commit | f8671718cdaae8d2edbf7c8ab74c301e010eac57 (patch) | |
tree | 3244a8f4956875ead2e26dcec84fbe29faf8e9a8 | |
parent | 582ba59b4485f86e4e56bfdac3d0c30f041021a9 (diff) | |
parent | 255fd72be679eebd082c1d717e59cff28d02ce3e (diff) |
Merge pull request #2437 from nextcloud/backport/2407/stable4.0
[stable4.0] Run SVGs through some sanitization
-rw-r--r-- | src/components/ContactDetails/ContactDetailsAvatar.vue | 27 | ||||
-rw-r--r-- | src/components/ContactsList/ContactsListItem.vue | 33 | ||||
-rw-r--r-- | src/models/contact.js | 18 |
3 files changed, 65 insertions, 13 deletions
diff --git a/src/components/ContactDetails/ContactDetailsAvatar.vue b/src/components/ContactDetails/ContactDetailsAvatar.vue index 1700c8dd..9a1bd3f5 100644 --- a/src/components/ContactDetails/ContactDetailsAvatar.vue +++ b/src/components/ContactDetails/ContactDetailsAvatar.vue @@ -32,8 +32,8 @@ @change="processFile"> <!-- Avatar display --> - <div v-if="contact.photo" - :style="{ 'backgroundImage': `url(${contact.photoUrl})` }" + <div v-if="photoUrl" + :style="{ 'backgroundImage': `url(${photoUrl})` }" class="contact-header-avatar__photo" @click="toggleModal" /> <Avatar v-else @@ -136,7 +136,7 @@ <div class="contact-header-modal__photo-wrapper" @click.exact.self="toggleModal"> <img ref="img" - :src="contact.photoUrl" + :src="photoUrl" class="contact-header-modal__photo"> </div> </Modal> @@ -178,11 +178,18 @@ export default { }, }, + watch: { + contact() { + this.loadPhotoUrl() + }, + }, + data() { return { maximizeAvatar: false, opened: false, loading: false, + photoUrl: false, root: generateRemoteUrl(`dav/files/${getCurrentUser().uid}`), } }, @@ -210,6 +217,10 @@ export default { }, }, + mounted() { + this.loadPhotoUrl() + }, + methods: { onLoad() { console.debug(...arguments) @@ -354,6 +365,16 @@ export default { this.loading = false }, + async loadPhotoUrl() { + this.photoUrl = false + const photoUrl = await this.contact.getPhotoUrl() + if (!photoUrl) { + console.warn('contact has an invalid photo') + return + } + this.photoUrl = photoUrl + }, + /** * Toggle the full image preview */ diff --git a/src/components/ContactsList/ContactsListItem.vue b/src/components/ContactsList/ContactsListItem.vue index 7441aa23..c5d1aee3 100644 --- a/src/components/ContactsList/ContactsListItem.vue +++ b/src/components/ContactsList/ContactsListItem.vue @@ -35,7 +35,16 @@ export default { required: true, }, }, - + data() { + return { + avatarUrl: undefined, + } + }, + watch: { + source() { + this.loadAvatarUrl() + } + }, computed: { selectedGroup() { return this.$route.params.selectedGroup @@ -48,18 +57,26 @@ export default { id() { return window.btoa(this.source.key).slice(0, -2) }, - - avatarUrl() { + }, + mounted() { + this.loadAvatarUrl() + }, + methods: { + async loadAvatarUrl() { + this.avatarUrl = undefined if (this.source.photo) { - return `${this.source.photoUrl}` + const photoUrl = await this.source.getPhotoUrl() + if (!photoUrl) { + console.warn('contact has an invalid photo') + // Invalid photo data + return + } + this.avatarUrl = photoUrl } if (this.source.url) { - return `${this.source.url}?photo` + this.avatarUrl = `${this.source.url}?photo` } - return undefined }, - }, - methods: { /** * Select this contact within the list diff --git a/src/models/contact.js b/src/models/contact.js index ef122d1c..b87e295d 100644 --- a/src/models/contact.js +++ b/src/models/contact.js @@ -26,6 +26,7 @@ import b64toBlob from 'b64-to-blob' import store from '../store' import updateDesignSet from '../services/updateDesignSet' +import sanitizeSVG from '@mattkrick/sanitize-svg' /** * Check if the given value is an empty array or an empty string @@ -229,11 +230,13 @@ export default class Contact { * Return the photo usable url * We cannot fetch external url because of csp policies * - * @readonly * @memberof Contact */ - get photoUrl() { + async getPhotoUrl() { const photo = this.vCard.getFirstProperty('photo') + if (!photo) { + return false + } const encoding = photo.getFirstParameter('encoding') let photoType = photo.getFirstParameter('type') let photoB64 = this.photo @@ -247,6 +250,17 @@ export default class Contact { photoType = photoB64.split(';')[0].split('/') } + // Verify if SVG is valid + if (photoType.startsWith('svg')) { + const imageSvg = atob(photoB64) + const cleanSvg = await sanitizeSVG(imageSvg) + + if (!cleanSvg) { + console.error('Invalid SVG for the following contact. Ignoring...', this.contact, { photoB64, photoType }) + return false + } + } + try { // Create blob from url const blob = b64toBlob(photoB64, `image/${photoType}`) |