diff --git a/js/privatebin.js b/js/privatebin.js index 17c45b94..c8f184e8 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -2994,11 +2994,18 @@ jQuery.PrivateBin = (function($) { const mimeType = me.getAttachmentMimeType(attachmentData); + // We explicitly do _not_ use the original mime type for the download link + // to always force a download instead of potentially dangerous browser rendering/parsing/interpretation + let safeMimeType = 'application/octet-stream'; + if (me.isSafeMimeType(mimeType)) { + safeMimeType = mimeType; + } + // extract data and convert to binary const rawData = attachmentData.substring(base64Start); const decodedData = rawData.length > 0 ? atob(rawData) : ''; - let blobUrl = getBlobUrl(decodedData, mimeType); + let blobUrl = getBlobUrl(decodedData, safeMimeType); attachmentLink.attr('href', blobUrl); if (typeof fileName !== 'undefined') { @@ -3028,6 +3035,28 @@ jQuery.PrivateBin = (function($) { me.handleBlobAttachmentPreview($attachmentPreview, blobUrl, mimeType); }; + + /** + * Evaluates whether this is known a safe mime type. + * + * This means, the media can safely be displayed and e.g. no XSS should be possible. + * + * @name AttachmentViewer.isSafeMimeType + * @function + * @param {string} + * @returns {bool} + */ + me.isSafeMimeType = function(mimeType) { + return ( + mimeType.startsWith('image/') && + !mimeType.includes('svg') + ) || + mimeType.startsWith('video/') || + mimeType.startsWith('audio/') || + mimeType.endsWith('/pdf') || + mimeType === 'text/plain'; + } + /** * displays the attachment * @@ -6064,4 +6093,4 @@ jQuery.PrivateBin = (function($) { CopyToClipboard: CopyToClipboard, Controller: Controller }; -})(jQuery); +})(jQuery); \ No newline at end of file diff --git a/lib/Configuration.php b/lib/Configuration.php index 92095561..b4101387 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -122,7 +122,7 @@ class Configuration 'js/kjua-0.10.0.js' => 'sha512-BYj4xggowR7QD150VLSTRlzH62YPfhpIM+b/1EUEr7RQpdWAGKulxWnOvjFx1FUlba4m6ihpNYuQab51H6XlYg==', 'js/legacy.js' => 'sha512-RQEo1hxpNc37i+jz/D9/JiAZhG8GFx3+SNxjYnI7jUgirDIqrCSj6QPAAZeaidditcWzsJ3jxfEj5lVm7ZwTRQ==', 'js/prettify.js' => 'sha512-puO0Ogy++IoA2Pb9IjSxV1n4+kQkKXYAEUtVzfZpQepyDPyXk8hokiYDS7ybMogYlyyEIwMLpZqVhCkARQWLMg==', - 'js/privatebin.js' => 'sha512-U52jFm7xsPJsJ7M/sFF6GoEBLTQ4pcvNDN5z3JcH0P2xHokiV3YrhfSi6XgTnBANdnjAXmg9GfJf3Bs2yKAiug==', + 'js/privatebin.js' => 'sha512-LgKu4erWrsmN7bYLwo1I2cptFa+RUY33oufmu0Psdp2vmGA4SAqA7sml0kIFCP/44Yd4VILvr/4fCRVSQ48GNQ==', 'js/purify-3.4.1.js' => 'sha512-280a/Vb6fVFsYaeRrkuDp4EDmdYlt2XS+dlDEO/U9qljPrAraA2bIzHTNmP+9dpwPDDwTML+RS+h5iaagPwTzA==', 'js/showdown-2.1.0.js' => 'sha512-WYXZgkTR0u/Y9SVIA4nTTOih0kXMEd8RRV6MLFdL6YU8ymhR528NLlYQt1nlJQbYz4EW+ZsS0fx1awhiQJme1Q==', 'js/zlib-1.3.2.js' => 'sha512-RAhJgxg9siMIA8ky4c10Rc2zUgnK80olHB8Tt1IOYWY4Eh1WmrviQkDn+sgBlb38ZHq3tzufGC41kP360gmosQ==',