|
|
@@ -1996,11 +1996,15 @@ window.PrivateBin = (function () {
|
|
|
* @param {string} url
|
|
|
* @param {string} deleteUrl
|
|
|
*/
|
|
|
- me.createPasteNotification = function (url, deleteUrl) {
|
|
|
+ me.createPasteNotification = function(url, deleteUrl)
|
|
|
+ {
|
|
|
+ const ua = navigator.userAgent;
|
|
|
+ const isMac = /Mac/.test(ua);
|
|
|
+ const hotkey = isMac ? I18n._('Cmd') : I18n._('Ctrl');
|
|
|
I18n._(
|
|
|
document.getElementById('pastelink'),
|
|
|
'Your document is <a id="pasteurl" href="%s">%s</a> <span id="copyhint">(Hit <kbd>Ctrl</kbd>+<kbd>c</kbd> to copy)</span>',
|
|
|
- url, url
|
|
|
+ url, url, hotkey
|
|
|
);
|
|
|
// save newly created element
|
|
|
pasteUrl = document.getElementById('pasteurl');
|
|
|
@@ -2890,11 +2894,18 @@ window.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.setAttribute('href', blobUrl);
|
|
|
|
|
|
if (typeof fileName !== 'undefined') {
|
|
|
@@ -2924,6 +2935,50 @@ window.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';
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 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 and (if possible) the preview
|
|
|
*
|