Pārlūkot izejas kodu

add more test cases, stricter SVG handling

El RIDO 3 nedēļas atpakaļ
vecāks
revīzija
b9aad1c866
3 mainītis faili ar 46 papildinājumiem un 11 dzēšanām
  1. 7 6
      js/privatebin.js
  2. 38 4
      js/test/AttachmentViewer.js
  3. 1 1
      lib/Configuration.php

+ 7 - 6
js/privatebin.js

@@ -2999,16 +2999,16 @@ jQuery.PrivateBin = (function($) {
 
             // 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';
+            let sanitizedMimeType = 'application/octet-stream';
             if (me.isSafeMimeType(mimeType)) {
-                safeMimeType = mimeType;
+                sanitizedMimeType = mimeType;
             }
 
             // extract data and convert to binary
             const rawData = attachmentData.substring(base64Start);
             const decodedData = rawData.length > 0 ? atob(rawData) : '';
 
-            let blobUrl = getBlobUrl(decodedData, safeMimeType);
+            let blobUrl = getBlobUrl(decodedData, sanitizedMimeType);
             attachmentLink.attr('href', blobUrl);
 
             if (typeof fileName !== 'undefined') {
@@ -3024,18 +3024,19 @@ jQuery.PrivateBin = (function($) {
             // 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 (/^image\/.*svg/.test(mimeType)) {
+            if (mimeType.startsWith('image\/svg')) {
                 const sanitizedData = DOMPurify.sanitize(
                     decodedData,
                     purifySvgConfig
                 );
-                blobUrl = getBlobUrl(sanitizedData, mimeType);
+                sanitizedMimeType = 'image/svg+xml';
+                blobUrl = getBlobUrl(sanitizedData, sanitizedMimeType);
             }
 
             template.removeClass('hidden');
             $attachment.append(template);
 
-            me.handleBlobAttachmentPreview($attachmentPreview, blobUrl, mimeType);
+            me.handleBlobAttachmentPreview($attachmentPreview, blobUrl, sanitizedMimeType);
         };
 
 

+ 38 - 4
js/test/AttachmentViewer.js

@@ -150,7 +150,7 @@ describe('AttachmentViewer', function () {
                         window.URL,
                         'createObjectURL',
                         {value: function(blob) {
-                            return 'blob:' + location.origin + '/1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed';
+                            return 'blob:' + blob.type + '/1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed';
                         }}
                     );
                 }
@@ -165,6 +165,7 @@ describe('AttachmentViewer', function () {
                 for (const filename of maliciousFileNames) {
                     $.PrivateBin.AttachmentViewer.setAttachment('data:;base64,', filename);
                     assert.ok(!$('body').html().includes(filename), 'does not allow file name ' + filename);
+                    $.PrivateBin.AttachmentViewer.removeAttachment();
                 }
 
                 const maliciousMimeTypes = [
@@ -176,8 +177,6 @@ describe('AttachmentViewer', function () {
                     // SVG bypass
                     'text/html svg',
                     'text/html(svg',
-                    'image/SVG+xml',
-                    'image/Svg+xml',
 
                     // invalid bytes after string
                     'image/png\x01',
@@ -187,10 +186,45 @@ describe('AttachmentViewer', function () {
                     $.PrivateBin.AttachmentViewer.setAttachment('data:' + mimeType + ';base64,', 'example file name');
                     assert.ok(!$('body').html().includes(mimeType), 'does not allow MIME type: ' + mimeType);
                     assert.ok(!$('body').html().includes(mimeType.toLowerCase()), 'does not allow lower cased MIME type: ' + mimeType);
+                    assert.ok(!$('body').html().includes('<img'), 'does not allow image MIME type: ' + mimeType);
+                    $.PrivateBin.AttachmentViewer.removeAttachment();
+                }
+
+                const supportedSafeMimeTypes = [
+                    'text/plain',
+                    'image/png',
+                    'image/jpeg',
+                ];
+                for (const mimeType of supportedSafeMimeTypes) {
+                    assert.ok($.PrivateBin.AttachmentViewer.isSafeMimeType(mimeType), 'treats as safe MIME type: '+ mimeType);
+                }
+
+                const supportedPreviewMimeTypes = [
+                    'application/pdf',
+                    'audio/wav',
+                    'video/avi',
+                ];
+                for (const mimeType of supportedPreviewMimeTypes) {
+                    assert.ok($.PrivateBin.AttachmentViewer.isSafeMimeType(mimeType), 'treats as safe preview MIME type: '+ mimeType);
+                    $.PrivateBin.AttachmentViewer.setAttachment('data:' + mimeType + ';base64,', 'example file name');
+                    assert.ok($('body').html().includes(mimeType), 'allows MIME type: ' + mimeType);
+                    $.PrivateBin.AttachmentViewer.removeAttachment();
+                }
+
+                // special case: not a safe type, but renders a sanitized preview
+                const svgMimeTypes = [
+                    'image/svg+xml',
+                    'image/SVG+xml',
+                    'image/sVg',
+                ];
+                for (const mimeType of svgMimeTypes) {
+                    assert.ok(!$.PrivateBin.AttachmentViewer.isSafeMimeType(mimeType), 'treats as unsafe MIME type: '+ mimeType);
+                    $.PrivateBin.AttachmentViewer.setAttachment('data:' + mimeType + ';base64,', 'example file name');
+                    assert.ok($('body').html().includes('image/svg+xml'), 'allows sanitized MIME type: ' + mimeType);
+                    $.PrivateBin.AttachmentViewer.removeAttachment();
                 }
                 clean();
             }
         );
-
     });
 });

+ 1 - 1
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-1MDRhKfiCDy5uGEaxPzl+rykAov9m4XqMGWpYKU6Up7J1iialwWm2O2l71bSDAUnqgC+xlC+SZIrSPtLAyqjKw==',
+            'js/privatebin.js'       => 'sha512-XkiRov+DDF3Un0/+xqnGV8OEzXB6/6XispcDGFPCO3ZtvRsJwoEasfZf3DI2a2eo06zkGl/rn6Yb5SChRNtYSw==',
             '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==',