aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEl RIDO <[email protected]>2022-03-13 19:56:12 +0100
committerEl RIDO <[email protected]>2022-03-13 19:56:12 +0100
commit2a4d572c1e9eb9b608d32b0cc0cb3b6c3b684eab (patch)
treec88e2e67f0c0fa1a618c4231f3a0a318fc0d03aa
parent6c1f0dde0c4cccd8e132d27842a3cba556648d02 (diff)
downloadprivatebin-2a4d572c1e9eb9b608d32b0cc0cb3b6c3b684eab.tar.gz
privatebin-2a4d572c1e9eb9b608d32b0cc0cb3b6c3b684eab.tar.bz2
privatebin-2a4d572c1e9eb9b608d32b0cc0cb3b6c3b684eab.zip
Sanitize SVG preview, preventing script execution in instance context, while dropping support for attachment download in IE
-rw-r--r--CHANGELOG.md2
-rw-r--r--js/privatebin.js118
-rw-r--r--tpl/bootstrap.php2
-rw-r--r--tpl/page.php2
4 files changed, 89 insertions, 35 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ef114663..1fb3ae22 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,11 +10,13 @@
* ADDED: Oracle database support (#868)
* ADDED: Configuration option to limit paste creation and commenting to certain IPs (#883)
* ADDED: Set CSP also as meta tag, to deal with misconfigured webservers mangling the HTTP header
+ * ADDED: Sanitize SVG preview, preventing script execution in instance context
* CHANGED: Language selection cookie only transmitted over HTTPS (#472)
* CHANGED: Upgrading libraries to: base-x 4.0.0, bootstrap 3.4.1 (JS), DOMpurify 2.3.6, ip-lib 1.18.0, jQuery 3.6.0, random_compat 2.0.21 & Showdown 2.0.0
* CHANGED: Removed automatic `.ini` configuration file migration (#808)
* CHANGED: Removed configurable `dir` for `traffic` & `purge` limiters (#419)
* CHANGED: Server salt, traffic and purge limiter now stored in the storage backend (#419)
+ * CHANGED: Drop support for attachment download in IE
* **1.3.5 (2021-04-05)**
* ADDED: Translations for Hebrew, Lithuanian, Indonesian and Catalan
* ADDED: Make the project info configurable (#681)
diff --git a/js/privatebin.js b/js/privatebin.js
index b6a3226e..53474bd1 100644
--- a/js/privatebin.js
+++ b/js/privatebin.js
@@ -53,6 +53,31 @@ jQuery.PrivateBin = (function($, RawDeflate) {
let z;
/**
+ * DOMpurify settings for HTML content
+ *
+ * @private
+ */
+ const purifyHtmlConfig = {
+ ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|magnet):)/i,
+ SAFE_FOR_JQUERY: true,
+ USE_PROFILES: {
+ html: true
+ }
+ };
+
+ /**
+ * DOMpurify settings for SVG content
+ *
+ * @private
+ */
+ const purifySvgConfig = {
+ USE_PROFILES: {
+ svg: true,
+ svgFilters: true
+ }
+ };
+
+ /**
* CryptoData class
*
* bundles helper fuctions used in both paste and comment formats
@@ -409,7 +434,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
element.html().replace(
/(((https?|ftp):\/\/[\w?!=&.\/-;#@~%+*-]+(?![\w\s?!&.\/;#~%"=-]>))|((magnet):[\w?=&.\/-;#@~%+*-]+))/ig,
'<a href="$1" rel="nofollow noopener noreferrer">$1</a>'
- )
+ ),
+ purifyHtmlConfig
)
);
};
@@ -2536,7 +2562,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
// let showdown convert the HTML and sanitize HTML *afterwards*!
$plainText.html(
DOMPurify.sanitize(
- converter.makeHtml(text)
+ converter.makeHtml(text),
+ purifyHtmlConfig
)
);
// add table classes from bootstrap css
@@ -2752,6 +2779,34 @@ jQuery.PrivateBin = (function($, RawDeflate) {
$dropzone;
/**
+ * get blob URL from string data and mime type
+ *
+ * @name AttachmentViewer.getBlobUrl
+ * @private
+ * @function
+ * @param {string} data - raw data of attachment
+ * @param {string} data - mime type of attachment
+ * @return {string} objectURL
+ */
+ function getBlobUrl(data, mimeType)
+ {
+ // Transform into a Blob
+ const buf = new Uint8Array(data.length);
+ for (let i = 0; i < data.length; ++i) {
+ buf[i] = data.charCodeAt(i);
+ }
+ const blob = new window.Blob(
+ [buf],
+ {
+ type: mimeType
+ }
+ );
+
+ // Get Blob URL
+ return window.URL.createObjectURL(blob);
+ }
+
+ /**
* sets the attachment but does not yet show it
*
* @name AttachmentViewer.setAttachment
@@ -2761,44 +2816,39 @@ jQuery.PrivateBin = (function($, RawDeflate) {
*/
me.setAttachment = function(attachmentData, fileName)
{
- // data URI format: data:[<mediaType>][;base64],<data>
+ // data URI format: data:[<mimeType>][;base64],<data>
// 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);
};
/**
@@ -3665,7 +3715,14 @@ jQuery.PrivateBin = (function($, RawDeflate) {
for (let i = 0; i < $head.length; ++i) {
newDoc.write($head[i].outerHTML);
}
- newDoc.write('</head><body><pre>' + DOMPurify.sanitize(Helper.htmlEntities(paste)) + '</pre></body></html>');
+ newDoc.write(
+ '</head><body><pre>' +
+ DOMPurify.sanitize(
+ Helper.htmlEntities(paste),
+ purifyHtmlConfig
+ ) +
+ '</pre></body></html>'
+ );
newDoc.close();
}
@@ -5394,11 +5451,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
// first load translations
I18n.loadTranslations();
- DOMPurify.setConfig({
- ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|magnet):)/i,
- SAFE_FOR_JQUERY: true
- });
-
// Add a hook to make all links open a new window
DOMPurify.addHook('afterSanitizeAttributes', function(node) {
// set all elements owning target to target=_blank
diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php
index 1fc8feb2..4c688784 100644
--- a/tpl/bootstrap.php
+++ b/tpl/bootstrap.php
@@ -73,7 +73,7 @@ endif;
?>
<script type="text/javascript" data-cfasync="false" src="js/purify-2.3.6.js" integrity="sha512-N1GGPjbqLbwK821ZN7C925WuTwU4aDxz2CEEOXQ6/s6m6MBwVj8fh5fugiE2hzsm0xud3q7jpjZQ4ILnpMREYQ==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-LYos+qXHIRqFf5ZPNphvtTB0cgzHUizu2wwcOwcwz/VIpRv9lpcBgPYz4uq6jx0INwCAj6Fbnl5HoKiLufS2jg==" crossorigin="anonymous"></script>
- <script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-EdUms2nI12Cmtv014stIEBlyPjeKMHlkg7NiBJup1b7jJF5amKhev2RwTaldINXK4UaWbZtQ6hGuMPNvvNQZFA==" crossorigin="anonymous"></script>
+ <script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-5fNML95dptjIMzFG4KAGE2nNbORbyrCWq4ql9tkvoUF2HSnemlxzngQ5PlWhpi0J2ttKIEqfGMBjU5MywzzdWQ==" crossorigin="anonymous"></script>
<!-- icon -->
<link rel="apple-touch-icon" href="<?php echo I18n::encode($BASEPATH); ?>img/apple-touch-icon.png" sizes="180x180" />
<link rel="icon" type="image/png" href="img/favicon-32x32.png" sizes="32x32" />
diff --git a/tpl/page.php b/tpl/page.php
index 74254c8e..ca84e8f6 100644
--- a/tpl/page.php
+++ b/tpl/page.php
@@ -51,7 +51,7 @@ endif;
?>
<script type="text/javascript" data-cfasync="false" src="js/purify-2.3.6.js" integrity="sha512-N1GGPjbqLbwK821ZN7C925WuTwU4aDxz2CEEOXQ6/s6m6MBwVj8fh5fugiE2hzsm0xud3q7jpjZQ4ILnpMREYQ==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-LYos+qXHIRqFf5ZPNphvtTB0cgzHUizu2wwcOwcwz/VIpRv9lpcBgPYz4uq6jx0INwCAj6Fbnl5HoKiLufS2jg==" crossorigin="anonymous"></script>
- <script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-EdUms2nI12Cmtv014stIEBlyPjeKMHlkg7NiBJup1b7jJF5amKhev2RwTaldINXK4UaWbZtQ6hGuMPNvvNQZFA==" crossorigin="anonymous"></script>
+ <script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-5fNML95dptjIMzFG4KAGE2nNbORbyrCWq4ql9tkvoUF2HSnemlxzngQ5PlWhpi0J2ttKIEqfGMBjU5MywzzdWQ==" crossorigin="anonymous"></script>
<!-- icon -->
<link rel="apple-touch-icon" href="img/apple-touch-icon.png?<?php echo rawurlencode($VERSION); ?>" sizes="180x180" />
<link rel="icon" type="image/png" href="img/favicon-32x32.png?<?php echo rawurlencode($VERSION); ?>" sizes="32x32" />