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

pull/508/head
El RIDO 2019-09-14 09:41:52 +02:00
parent a363b2ff95
commit 63426d6f8b
No known key found for this signature in database
GPG Key ID: 0F5C940A6BD81F92
7 changed files with 299 additions and 163 deletions

View File

@ -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
js/legacy.js Normal file
View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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, []

View File

@ -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]-->

View File

@ -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]-->