CVE-2022-24833 (PHP, published 2022-04-11T21:15Z)
Description: value: PrivateBin is minimalist, open source online pastebin clone where the server has zero knowledge of pasted data. In PrivateBin < v1.4.0 a cross-site scripting (XSS) vulnerability was found. The vulnerability is present in all versions from v0.21 of the project, which was at the time still called ZeroBin. The issue is caused by the fact that SVGs can contain JavaScript. This can allow an attacker to execute code, if the user opens a paste with a specifically crafted SVG attachment, and interacts with the preview image and the instance isn't protected by an appropriate content security policy. Users are advised to either upgrade to version 1.4.0 or to ensure the content security policy of their instance is set correctly.
Severity: MEDIUM (CVSS v3 base score: 6.1)
Weakness: CWE-79 \u2014 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
Repository: https://github.com/PrivateBin/PrivateBin
Files changed:
CHANGELOG.md
js/privatebin.js
tpl/bootstrap.php
tpl/page.php
Commit message: Sanitize SVG preview, preventing script execution in instance context, while dropping support for attachment download in IE


Vulnerable code:
                    )
                        converter.makeHtml(text)
            // data URI format: data:[<mediaType>][;base64],<data>
            // position in data URI string of where mediaType ends
            const mediaTypeEnd = attachmentData.indexOf(';');
            // extract mediaType
            const mediaType = attachmentData.substring(5, mediaTypeEnd);
            // 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);
            }
            me.handleBlobAttachmentPreview($attachmentPreview, blobUrl, mediaType);
            newDoc.write('</head><body><pre>' + DOMPurify.sanitize(Helper.htmlEntities(paste)) + '</pre></body></html>');
            DOMPurify.setConfig({
                ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|magnet):)/i,
                SAFE_FOR_JQUERY: true
            });

		<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-EdUms2nI12Cmtv014stIEBlyPjeKMHlkg7NiBJup1b7jJF5amKhev2RwTaldINXK4UaWbZtQ6hGuMPNvvNQZFA==" crossorigin="anonymous"></script>

Fixed code:
    * ADDED: Sanitize SVG preview, preventing script execution in instance context
    * CHANGED: Drop support for attachment download in IE
    /**
     * 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
        }
    };

                    ),
                    purifyHtmlConfig
                        converter.makeHtml(text),
                        purifyHtmlConfig
         * 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);
         }

         /**
            // data URI format: data:[<mimeType>][;base64],<data>
            // position in data URI string of where mimeType ends
            const mimeTypeEnd = attachmentData.indexOf(';');
            // extract mimeType
            const mimeType = attachmentData.substring(5, mimeTypeEnd);
            let blobUrl = getBlobUrl(decodedData, mimeType);
            $attachmentLink.attr('href', blobUrl);
            // 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);
            newDoc.write(
                '</head><body><pre>' +
                DOMPurify.sanitize(
                    Helper.htmlEntities(paste),
                    purifyHtmlConfig
                ) +
                '</pre></body></html>'
            );
		<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-5fNML95dptjIMzFG4KAGE2nNbORbyrCWq4ql9tkvoUF2HSnemlxzngQ5PlWhpi0J2ttKIEqfGMBjU5MywzzdWQ==" crossorigin="anonymous"></script>
		<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-5fNML95dptjIMzFG4KAGE2nNbORbyrCWq4ql9tkvoUF2HSnemlxzngQ5PlWhpi0J2ttKIEqfGMBjU5MywzzdWQ==" crossorigin="anonymous"></script>