소스 검색

splitting out PrivateBin.InitialCheck class into Legacy.Check and working on making it compatible with IE 11

El RIDO 6 년 전
부모
커밋
63426d6f8b
7개의 변경된 파일299개의 추가작업 그리고 163개의 파일을 삭제
  1. 1 0
      js/common.js
  2. 256 0
      js/legacy.js
  3. 27 153
      js/privatebin.js
  4. 8 5
      js/test/Check.js
  5. 3 3
      js/test/CryptTool.js
  6. 2 1
      tpl/bootstrap.php
  7. 2 1
      tpl/page.php

+ 1 - 0
js/common.js

@@ -20,6 +20,7 @@ global.showdown = require('./showdown-1.9.1');
 global.DOMPurify = require('./purify-1.0.11');
 global.baseX = require('./base-x-3.0.5.1').baseX;
 require('./bootstrap-3.3.7');
+require('./legacy');
 require('./privatebin');
 
 // internal variables

+ 256 - 0
js/legacy.js

@@ -0,0 +1,256 @@
+/**
+ * PrivateBin
+ *
+ * a zero-knowledge paste bin
+ *
+ * @see       {@link https://github.com/PrivateBin/PrivateBin}
+ * @copyright 2012 Sébastien SAUVAGE ({@link http://sebsauvage.net})
+ * @license   {@link https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License}
+ * @version   1.3
+ * @name      Legacy
+ * @namespace
+ *
+ * IMPORTANT NOTICE FOR DEVELOPERS:
+ * The logic in this file is intended to run in legacy browsers. Avoid any use of:
+ * - ES6 or newer in general
+ * - const/let, use the traditional var declarations instead
+ * - async/await or Promises, use traditional callbacks
+ * - shorthand function notation "() => output", use the full "function() {return output;}" style
+ * - IE doesn't support:
+ *   - URL(), use the traditional window.location object
+ *   - endsWith(), use indexof()
+ * - yes, this logic needs to support IE 5 or 6, to at least display the error message
+ */
+
+// main application start, called when DOM is fully loaded
+jQuery(document).ready(function() {
+    'use strict';
+    // run main controller
+    $.Legacy.Check.init();
+});
+
+jQuery.Legacy = (function($) {
+    'use strict';
+
+    /**
+     * compatibility check
+     *
+     * @name   Check
+     * @class
+     */
+    var Check = (function () {
+        var me = {};
+
+        /**
+         * Status of the initial check, true means it passed
+         *
+         * @private
+         * @prop   {bool}
+         */
+        var status = false;
+
+        /**
+         * Initialization check did run
+         *
+         * @private
+         * @prop   {bool}
+         */
+        var init = false;
+
+        /**
+         * blacklist of UserAgents (parts) known to belong to a bot
+         *
+         * @private
+         * @enum   {Array}
+         * @readonly
+         */
+        var badBotUA = [
+            'Bot',
+            'bot'
+        ];
+
+        /**
+         * whitelist of top level domains to consider a secure context,
+         * regardless of protocol
+         *
+         * @private
+         * @enum   {Array}
+         * @readonly
+         */
+        var tld = [
+            '.onion',
+            '.i2p'
+        ];
+
+        /**
+         * whitelist of hostnames to consider a secure context,
+         * regardless of protocol
+         *
+         * @private
+         * @enum   {Array}
+         * @readonly
+         */
+        // whitelists of TLDs & local hostnames
+        var hostname = [
+            'localhost',
+            '127.0.0.1',
+            '[::1]'
+        ];
+
+        /**
+         * check if the context is secure
+         *
+         * @private
+         * @name   Check.isSecureContext
+         * @function
+         * @return {bool}
+         */
+        function isSecureContext()
+        {
+            // use .isSecureContext if available
+            if (window.isSecureContext === true || window.isSecureContext === false) {
+                return window.isSecureContext;
+            }
+
+            // HTTP is obviously insecure
+            if (window.location.protocol !== 'http:') {
+                return true;
+            }
+
+            // filter out actually secure connections over HTTP
+            for (var i = 0; i < tld.length; i++) {
+                if (
+                    window.location.hostname.indexOf(
+                        tld[i],
+                        window.location.hostname.length - tld[i].length
+                    ) !== -1
+                ) {
+                    return true;
+                }
+            }
+
+            // whitelist localhost for development
+            for (var i = 0; i < hostname.length; i++) {
+                if (window.location.hostname === hostname[i]) {
+                    return true;
+                }
+            }
+
+            // totally INSECURE http protocol!
+            return false;
+        }
+
+        /**
+         * checks whether this is a bot we dislike
+         *
+         * @private
+         * @name   Check.isBadBot
+         * @function
+         * @return {bool}
+         */
+        function isBadBot() {
+            // check whether a bot user agent part can be found in the current
+            // user agent
+            for (var i = 0; i < badBotUA.length; i++) {
+                if (navigator.userAgent.indexOf(badBotUA[i]) !== -1) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * checks whether this is an unsupported browser, via feature detection
+         *
+         * @private
+         * @name   Check.isOldBrowser
+         * @function
+         * @return {bool}
+         */
+        function isOldBrowser() {
+            // webcrypto support
+            if (!(
+                'crypto' in window &&
+                'getRandomValues' in window.crypto &&
+                'subtle' in window.crypto &&
+                'encrypt' in window.crypto.subtle &&
+                'decrypt' in window.crypto.subtle &&
+                'Uint8Array' in window &&
+                'Uint32Array' in window
+            )) {
+                return true;
+            }
+
+             // not checking for async/await, ES6 or Promise support, as most
+             // browsers introduced these earlier then webassembly and webcrypto:
+             // https://github.com/PrivateBin/PrivateBin/pull/431#issuecomment-493129359
+
+            return false;
+        }
+
+        /**
+         * returns if the check has concluded
+         *
+         * @name   Check.getInit
+         * @function
+         * @return {bool}
+         */
+        me.getInit = function()
+        {
+            return init;
+        }
+        
+        /**
+         * returns the current status of the check
+         *
+         * @name   Check.getStatus
+         * @function
+         * @return {bool}
+         */
+        me.getStatus = function()
+        {
+            return status;
+        }
+        
+        /**
+         * init on application start, returns an all-clear signal
+         *
+         * @name   Check.init
+         * @function
+         */
+        me.init = function()
+        {
+            // prevent bots from viewing a paste and potentially deleting data
+            // when burn-after-reading is set
+            if (isBadBot()) {
+                $.PrivateBin.Alert.showError('I love you too, bot…');
+                init = true;
+                return;
+            }
+
+            if (isOldBrowser()) {
+                // some browsers (Chrome based ones) would have webcrypto support if using HTTPS
+                if (!isSecureContext()) {
+                    $.PrivateBin.Alert.showError(['Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href="%s">switching to HTTPS</a>.', 'https' + window.location.href.slice(4)]);
+                }
+                $('#oldnotice').removeClass('hidden');
+                init = true;
+                return;
+            }
+
+            if (!isSecureContext()) {
+                $('#httpnotice').removeClass('hidden');
+            }
+            init = true;
+
+            // only if everything passed, we set the status to true
+            status = true;
+        }
+
+        return me;
+    })();
+
+    return {
+        Check: Check
+    };
+})(jQuery);

+ 27 - 153
js/privatebin.js

@@ -14,6 +14,7 @@
  */
 
 jQuery.fn.draghover = function() {
+    'use strict';
     return this.each(function() {
         let collection = $(),
             self = $(this);
@@ -4709,156 +4710,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
         return me;
     })();
 
-
-    /**
-     * initial (security) check
-     *
-     * @name   InitialCheck
-     * @class
-     */
-    const InitialCheck = (function () {
-        const me = {};
-
-        /**
-         * blacklist of UserAgents (parts) known to belong to a bot
-         *
-         * @private
-         * @enum   {Array}
-         * @readonly
-         */
-        const badBotUA = [
-            'Bot',
-            'bot'
-        ];
-
-        /**
-         * check if the connection is insecure
-         *
-         * @private
-         * @name   InitialCheck.isInsecureConnection
-         * @function
-         * @return {bool}
-         */
-        function isInsecureConnection()
-        {
-            // use .isSecureContext if available
-            if (window.isSecureContext === true || window.isSecureContext === false) {
-                return !window.isSecureContext;
-            }
-
-            const url = new URL(window.location);
-
-            // HTTP is obviously insecure
-            if (url.protocol !== 'http:') {
-                return false;
-            }
-
-            // filter out actually secure connections over HTTP
-            for (const tld of ['.onion', '.i2p']) {
-                if (url.hostname.endsWith(tld)) {
-                    return false;
-                }
-            }
-
-            // whitelist localhost for development
-            for (const hostname of ['localhost', '127.0.0.1', '[::1]']) {
-                if (url.hostname === hostname) {
-                    return false;
-                }
-            }
-
-            // totally INSECURE http protocol!
-            return true;
-        }
-
-        /**
-         * checks whether this is a bot we dislike
-         *
-         * @private
-         * @name   InitialCheck.isBadBot
-         * @function
-         * @return {bool}
-         */
-        function isBadBot() {
-            // check whether a bot user agent part can be found in the current
-            // user agent
-            for (const UAfragment of badBotUA) {
-                if (navigator.userAgent.indexOf(UAfragment) >= 0) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * checks whether this is an unsupported browser, via feature detection
-         *
-         * @private
-         * @name   InitialCheck.isOldBrowser
-         * @function
-         * @return {bool}
-         */
-        function isOldBrowser() {
-            // webcrypto support
-            if (!(
-                'crypto' in window &&
-                'getRandomValues' in window.crypto &&
-                'subtle' in window.crypto &&
-                'encrypt' in window.crypto.subtle &&
-                'decrypt' in window.crypto.subtle &&
-                'Uint8Array' in window &&
-                'Uint32Array' in window
-            )) {
-                return true;
-            }
-
-             // not checking for async/await, ES6 or Promise support, as most
-             // browsers introduced these earlier then webassembly and webcrypto:
-             // https://github.com/PrivateBin/PrivateBin/pull/431#issuecomment-493129359
-
-            return false;
-        }
-
-        /**
-         * init on application start, returns an all-clear signal
-         *
-         * @name   InitialCheck.init
-         * @function
-         * @return {bool}
-         */
-        me.init = function()
-        {
-            // prevent bots from viewing a paste and potentially deleting data
-            // when burn-after-reading is set
-            if (isBadBot()) {
-                Alert.showError('I love you too, bot…');
-                return false;
-            }
-
-            if (isOldBrowser()) {
-                // some browsers (Chrome based ones) would have webcrypto support if using HTTPS
-                if (isInsecureConnection()) {
-                    Alert.showError(['Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href="%s">switching to HTTPS</a>.', 'https' + window.location.href.slice(4)]);
-                }
-                $('#oldnotice').removeClass('hidden');
-                return false;
-            }
-
-            if (isInsecureConnection()) {
-                $('#httpnotice').removeClass('hidden');
-            }
-
-            z = zlib.catch(function () {
-                if ($('body').data('compression') !== 'none') {
-                    Alert.showWarning('Your browser doesn\'t support WebAssembly, used for zlib compression. You can create uncompressed documents, but can\'t read compressed ones.');
-                }
-            });
-            return true;
-        }
-
-        return me;
-    })();
-
     /**
      * (controller) main PrivateBin logic
      *
@@ -5033,13 +4884,29 @@ jQuery.PrivateBin = (function($, RawDeflate) {
             DiscussionViewer.prepareNewDiscussion();
         };
 
+        /**
+         * try initializing zlib or display a warning if it fails,
+         * extracted from main init to allow unit testing
+         *
+         * @name   Controller.initZ
+         * @function
+         */
+        me.initZ = function()
+        {
+            z = zlib.catch(function () {
+                if ($('body').data('compression') !== 'none') {
+                    Alert.showWarning('Your browser doesn\'t support WebAssembly, used for zlib compression. You can create uncompressed documents, but can\'t read compressed ones.');
+                }
+            });
+        }
+
         /**
          * application start
          *
          * @name   Controller.init
          * @function
          */
-        me.init = async function()
+        me.init = function()
         {
             // first load translations
             I18n.loadTranslations();
@@ -5057,10 +4924,18 @@ jQuery.PrivateBin = (function($, RawDeflate) {
             Prompt.init();
             TopNav.init();
             UiHelper.init();
-            if (!InitialCheck.init()) {
+
+            // check for legacy browsers before going any further
+            if (!$.Legacy.Check.getInit()) {
+                // Legacy check didn't complete, wait and try again
+                setTimeout(init, 500);
+                return;
+            }
+            if (!$.Legacy.Check.getStatus()) {
                 // something major is wrong, stop right away
                 return;
             }
+            me.initZ();
 
             // check whether existing paste needs to be shown
             try {
@@ -5100,7 +4975,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
         ServerInteraction: ServerInteraction,
         PasteEncrypter: PasteEncrypter,
         PasteDecrypter: PasteDecrypter,
-        InitialCheck: InitialCheck,
         Controller: Controller
     };
 })(jQuery, RawDeflate);

+ 8 - 5
js/test/InitialCheck.js → js/test/Check.js

@@ -2,7 +2,7 @@
 var common = require('../common');
 /* global WebCrypto */
 
-describe('InitialCheck', function () {
+describe('Check', function () {
     describe('init', function () {
         this.timeout(30000);
         before(function () {
@@ -23,7 +23,8 @@ describe('InitialCheck', function () {
                         '</body></html>'
                     );
                     $.PrivateBin.Alert.init();
-                    const result1 = !$.PrivateBin.InitialCheck.init(),
+                    $.Legacy.Check.init();
+                    const result1 = $.Legacy.Check.getInit() && !$.Legacy.Check.getStatus(),
                           result2 = !$('#errormessage').hasClass('hidden');
                     clean();
                     return result1 && result2;
@@ -50,7 +51,8 @@ describe('InitialCheck', function () {
                     '<div id="oldnotice" class="hidden"></div></body></html>'
                 );
                 $.PrivateBin.Alert.init();
-                const result1 = !$.PrivateBin.InitialCheck.init(),
+                $.Legacy.Check.init();
+                const result1 = $.Legacy.Check.getInit() && !$.Legacy.Check.getStatus(),
                       result2 = isSecureContext === $('#errormessage').hasClass('hidden'),
                       result3 = !$('#oldnotice').hasClass('hidden');
                 clean();
@@ -70,9 +72,10 @@ describe('InitialCheck', function () {
                     '<html><body><div id="httpnotice" class="hidden"></div>'+
                     '</body></html>'
                 );
-                $.PrivateBin.Alert.init();
                 window.crypto = new WebCrypto();
-                const result1 = $.PrivateBin.InitialCheck.init(),
+                $.PrivateBin.Alert.init();
+                $.Legacy.Check.init();
+                const result1 = $.Legacy.Check.getInit() && $.Legacy.Check.getStatus(),
                       result2 = secureProtocol === $('#httpnotice').hasClass('hidden');
                 clean();
                 return result1 && result2;

+ 3 - 3
js/test/CryptTool.js

@@ -19,7 +19,7 @@ describe('CryptTool', function () {
                     await new Promise(resolve => setTimeout(resolve, 300));
                     let clean = jsdom();
                     // ensure zlib is getting loaded
-                    $.PrivateBin.InitialCheck.init();
+                    $.PrivateBin.Controller.initZ();
                     window.crypto = new WebCrypto();
                     message = message.trim();
                     let cipherMessage = await $.PrivateBin.CryptTool.cipher(
@@ -182,7 +182,7 @@ describe('CryptTool', function () {
                 clean = jsdom();
             window.crypto = new WebCrypto();
             // ensure zlib is getting loaded
-            $.PrivateBin.InitialCheck.init();
+            $.PrivateBin.Controller.initZ();
             let cipherMessage = await $.PrivateBin.CryptTool.cipher(
                     'foo', 'bar', message, []
                 ),
@@ -227,7 +227,7 @@ conseq_or_bottom inv (interp (nth_iterate sBody n) (MemElem mem))
 `;
                     let clean = jsdom();
                     // ensure zlib is getting loaded
-                    $.PrivateBin.InitialCheck.init();
+                    $.PrivateBin.Controller.initZ();
                     window.crypto = new WebCrypto();
                     let cipherMessage = await $.PrivateBin.CryptTool.cipher(
                             key, password, message, []

+ 2 - 1
tpl/bootstrap.php

@@ -71,7 +71,8 @@ if ($MARKDOWN):
 endif;
 ?>
 		<script type="text/javascript" data-cfasync="false" src="js/purify-1.0.11.js" integrity="sha512-p7UyJuyBkhMcMgE4mDsgK0Lz70OvetLefua1oXs1OujWv9gOxh4xy8InFux7bZ4/DAZsTmO4rgVwZW9BHKaTaw==" crossorigin="anonymous"></script>
-		<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-nRdNcFKdKTE9s/s1U7Fb27A6oeQ9J9s4v1T0/GLZlLEuwLw10WPoohs6ER3xQYfQ8y7MqC5paewELmiK7lLCjA==" crossorigin="anonymous"></script>
+		<script type="text/javascript" data-cfasync="false" src="js/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-J1+DRECXhOb//xjTP78jdfpTFi4tLjUbltkC15gKyZ4qe/7pdhLevJJ/ok62jnogmCLlLkTDabmj2o5KnCvzww==" crossorigin="anonymous"></script>
+		<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-qJ7Lfyl7375UWjsItSmAUFwhvynGHUy9U1ldU2OCpFOr5YWhAluEIN0/8ztO7p+5DcljE3wYst1b0ZBiVBEnag==" crossorigin="anonymous"></script>
 		<!--[if IE]>
 		<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;}</style>
 		<![endif]-->

+ 2 - 1
tpl/page.php

@@ -49,7 +49,8 @@ if ($MARKDOWN):
 endif;
 ?>
 		<script type="text/javascript" data-cfasync="false" src="js/purify-1.0.11.js" integrity="sha512-p7UyJuyBkhMcMgE4mDsgK0Lz70OvetLefua1oXs1OujWv9gOxh4xy8InFux7bZ4/DAZsTmO4rgVwZW9BHKaTaw==" crossorigin="anonymous"></script>
-		<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-nRdNcFKdKTE9s/s1U7Fb27A6oeQ9J9s4v1T0/GLZlLEuwLw10WPoohs6ER3xQYfQ8y7MqC5paewELmiK7lLCjA==" crossorigin="anonymous"></script>
+		<script type="text/javascript" data-cfasync="false" src="js/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-J1+DRECXhOb//xjTP78jdfpTFi4tLjUbltkC15gKyZ4qe/7pdhLevJJ/ok62jnogmCLlLkTDabmj2o5KnCvzww==" crossorigin="anonymous"></script>
+		<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-qJ7Lfyl7375UWjsItSmAUFwhvynGHUy9U1ldU2OCpFOr5YWhAluEIN0/8ztO7p+5DcljE3wYst1b0ZBiVBEnag==" crossorigin="anonymous"></script>
 		<!--[if IE]>
 		<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;}</style>
 		<![endif]-->