// position in data URI string of where data begins
const base64Start = attachmentData.indexOf(',') + 1;
- // position in data URI string of where mediaType ends
- const mediaTypeEnd = attachmentData.indexOf(';');
+ // position in data URI string of where mimeType ends
+ const mimeTypeEnd = attachmentData.indexOf(';');
- // extract mediaType
- const mediaType = attachmentData.substring(5, mediaTypeEnd);
+ // extract mimeType
+ const mimeType = attachmentData.substring(5, mimeTypeEnd);
// extract data and convert to binary
const rawData = attachmentData.substring(base64Start);
const decodedData = rawData.length > 0 ? atob(rawData) : '';
- // Transform into a Blob
- const buf = new Uint8Array(decodedData.length);
- for (let i = 0; i < decodedData.length; ++i) {
- buf[i] = decodedData.charCodeAt(i);
- }
- const blob = new window.Blob([ buf ], { type: mediaType });
-
- // Get Blob URL
- const blobUrl = window.URL.createObjectURL(blob);
-
- // IE does not support setting a data URI on an a element
- // Using msSaveBlob to download
- if (window.Blob && navigator.msSaveBlob) {
- $attachmentLink.off('click').on('click', function () {
- navigator.msSaveBlob(blob, fileName);
- });
- } else {
- $attachmentLink.attr('href', blobUrl);
- }
+ let blobUrl = getBlobUrl(decodedData, mimeType);
+ $attachmentLink.attr('href', blobUrl);
if (typeof fileName !== 'undefined') {
$attachmentLink.attr('download', fileName);
}
- me.handleBlobAttachmentPreview($attachmentPreview, blobUrl, mediaType);
+ // sanitize SVG preview
+ // prevents executing embedded scripts when CSP is not set and user
+ // right-clicks/long-taps and opens the SVG in a new tab - prevented
+ // in the preview by use of an img tag, which disables scripts, too
+ if (mimeType.match(/^image\/.*svg/i)) {
+ const sanitizedData = DOMPurify.sanitize(
+ decodedData,
+ purifySvgConfig
+ );
+ blobUrl = getBlobUrl(sanitizedData, mimeType);
+ }
+
+ me.handleBlobAttachmentPreview($attachmentPreview, blobUrl, mimeType);
};
/**
@@ -2808,6 +2863,9 @@ jQuery.PrivateBin = (function($, RawDeflate) {
*/
me.showAttachment = function()
{
+ // skip, if attachments got disabled
+ if (!$attachment || !$attachmentPreview) return;
+
$attachment.removeClass('hidden');
if (attachmentHasPreview) {
@@ -3015,13 +3073,13 @@ jQuery.PrivateBin = (function($, RawDeflate) {
me.handleBlobAttachmentPreview = function ($targetElement, blobUrl, mimeType) {
if (blobUrl) {
attachmentHasPreview = true;
- if (mimeType.match(/image\//i)) {
+ if (mimeType.match(/^image\//i)) {
$targetElement.html(
$(document.createElement('img'))
.attr('src', blobUrl)
.attr('class', 'img-thumbnail')
);
- } else if (mimeType.match(/video\//i)) {
+ } else if (mimeType.match(/^video\//i)) {
$targetElement.html(
$(document.createElement('video'))
.attr('controls', 'true')
@@ -3032,7 +3090,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
.attr('type', mimeType)
.attr('src', blobUrl))
);
- } else if (mimeType.match(/audio\//i)) {
+ } else if (mimeType.match(/^audio\//i)) {
$targetElement.html(
$(document.createElement('audio'))
.attr('controls', 'true')
@@ -3664,7 +3722,14 @@ jQuery.PrivateBin = (function($, RawDeflate) {
for (let i = 0; i < $head.length; ++i) {
newDoc.write($head[i].outerHTML);
}
- newDoc.write('' + DOMPurify.sanitize(Helper.htmlEntities(paste)) + '