From 0c7f77f5ed7d8bd18fbb8daa7a17d46d3e5be6b5 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 20 May 2021 16:16:07 +0530 Subject: [PATCH] sketch out some more sandbox tests and note down some improvements --- www/checkup/app-checkup.less | 6 ++ www/checkup/main.js | 30 ++++-- www/checkup/sandbox/index.html | 11 +++ www/checkup/sandbox/main.js | 175 +++++++++++++++++++++++++++++++++ 4 files changed, 214 insertions(+), 8 deletions(-) create mode 100644 www/checkup/sandbox/index.html create mode 100644 www/checkup/sandbox/main.js diff --git a/www/checkup/app-checkup.less b/www/checkup/app-checkup.less index a475d5983..99999e2bb 100644 --- a/www/checkup/app-checkup.less +++ b/www/checkup/app-checkup.less @@ -12,6 +12,12 @@ html, body { color: @cryptpad_text_col; font-family: "IBM Plex Mono"; + iframe.sandbox-test { + display: block; + width: 100%; + height: 100%; + } + .report { font-size: 30px; max-width: 50%; diff --git a/www/checkup/main.js b/www/checkup/main.js index b17709a26..896156fc6 100644 --- a/www/checkup/main.js +++ b/www/checkup/main.js @@ -20,6 +20,7 @@ define([ ], function ($, ApiConfig, Assertions, h, Messages, DomReady, nThen, SFCommonO, Login, Hash, Util, Pinpad, NetConfig, Pages) { + var Assert = Assertions(); var trimSlashes = function (s) { if (typeof(s) !== 'string') { return s; } @@ -52,6 +53,13 @@ define([ var trimmedSafe = trimSlashes(ApiConfig.httpSafeOrigin); var trimmedUnsafe = trimSlashes(ApiConfig.httpUnsafeOrigin); +/* +// XXX display results from this iframe on this page + document.body.appendChild(h('iframe', { + class: 'sandbox-test', + src: trimmedSafe + '/checkup/sandbox/index.html', + })); +*/ assert(function (cb, msg) { msg.appendChild(h('span', [ @@ -117,7 +125,7 @@ define([ var checkAvailability = function (url, cb) { $.ajax({ - url: url, + url: url, // XXX bust cache data: {}, complete: function (xhr) { cb(xhr.status === 200); @@ -158,6 +166,7 @@ define([ ])); var to; + var obj; nThen(function (waitFor) { DomReady.onReady(waitFor()); }).nThen(function (waitFor) { @@ -165,8 +174,13 @@ define([ console.error('TIMEOUT loading iframe on the safe domain'); cb(false); }, 5000); - SFCommonO.initIframe(waitFor); + obj = SFCommonO.initIframe(waitFor); + }).nThen(function () { + SFCommonO.start({ + href: obj.href, + }); }).nThen(function () { + console.error("DONE?"); // Iframe is loaded clearTimeout(to); cb(true); @@ -344,7 +358,7 @@ define([ //'cross-origin-opener-policy': 'same-origin', // FIXME this is in our nginx config but not server.js }; - $.ajax(url, { + $.ajax(url, { // XXX bust cache complete: function (xhr) { cb(!Object.keys(expect).some(function (k) { var response = xhr.getResponseHeader(k); @@ -390,7 +404,7 @@ define([ RESTART_WARNING(), ])); - $.ajax(sheetURL, { + $.ajax(sheetURL, { // FIXME bust cache complete: function (xhr) { var csp = xhr.getResponseHeader('Content-Security-Policy'); if (!/unsafe\-eval/.test(csp)) { @@ -416,7 +430,7 @@ define([ "Your browser console may provide more details as to why this resource could not be loaded. ", ])); - $.ajax('/api/broadcast', { + $.ajax('/api/broadcast', { // XXX bust cache dataType: 'text', complete: function (xhr) { cb(xhr.status === 200); @@ -429,7 +443,7 @@ define([ }; var checkAPIHeaders = function (url, msg, cb) { - $.ajax(url, { + $.ajax(url, { // XXX bust cache dataType: 'text', complete: function (xhr) { var allHeaders = xhr.getAllResponseHeaders(); @@ -481,13 +495,13 @@ define([ var INCORRECT_HEADER_TEXT = ' was served with duplicated or incorrect headers. Compare your reverse-proxy configuration against the provided example.'; assert(function (cb, msg) { - var url = '/api/config'; + var url = '/api/config'; // XXX bust cache msg.innerText = url + INCORRECT_HEADER_TEXT; checkAPIHeaders(url, msg, cb); }); assert(function (cb, msg) { - var url = '/api/broadcast'; + var url = '/api/broadcast'; // XXX bust cache msg.innerText = url + INCORRECT_HEADER_TEXT; checkAPIHeaders(url, msg, cb); }); diff --git a/www/checkup/sandbox/index.html b/www/checkup/sandbox/index.html new file mode 100644 index 000000000..04a9502d3 --- /dev/null +++ b/www/checkup/sandbox/index.html @@ -0,0 +1,11 @@ + + + + + + + + +
+ + diff --git a/www/checkup/sandbox/main.js b/www/checkup/sandbox/main.js new file mode 100644 index 000000000..8066dc1b3 --- /dev/null +++ b/www/checkup/sandbox/main.js @@ -0,0 +1,175 @@ +define([ + 'jquery', + '/api/config', + '/assert/assertions.js', + '/common/hyperscript.js', + '/customize/messages.js', + '/common/dom-ready.js', + '/bower_components/nthen/index.js', + '/common/sframe-common-outer.js', + '/customize/login.js', + '/common/common-hash.js', + '/common/common-util.js', + '/common/pinpad.js', + '/common/outer/network-config.js', + '/customize/pages.js', + + '/bower_components/tweetnacl/nacl-fast.min.js', + 'css!/bower_components/components-font-awesome/css/font-awesome.min.css', + 'less!/checkup/app-checkup.less', +], function ($, ApiConfig, Assertions, h, Messages, DomReady, + nThen, SFCommonO, Login, Hash, Util, Pinpad, + NetConfig, Pages) { + var Assert = Assertions(); + var assert = function (f, msg) { + Assert(f, msg || h('span.advisory-text.cp-danger')); + }; + + var code = function (content) { + return h('code', content); + }; + + var getHeaders = function (url, cb) { + $.ajax(url + "?test=" + (+new Date()), { + dataType: 'text', + complete: function (xhr) { + var allHeaders = xhr.getAllResponseHeaders(); + return void cb(void 0, allHeaders, xhr); + }, + }); + }; + var parseCSP = function (CSP) { + //console.error(CSP); + var CSP_headers = {}; + CSP.split(";") + .forEach(function (rule) { + rule = (rule || "").trim(); + if (!rule) { return; } + var parts = rule.split(/\s/); + var first = parts[0]; + var rest = rule.slice(first.length + 1); + CSP_headers[first] = rest; + //console.error(rule.trim()); + console.info("[%s] '%s'", first, rest); + }); + return CSP_headers; + }; + + var hasUnsafeEval = function (CSP_headers) { + return /unsafe\-eval/.test(CSP_headers['script-src']); + }; + + var hasUnsafeInline = function (CSP_headers) { + return /unsafe\-inline/.test(CSP_headers['script-src']); + }; + + var hasOnlyOfficeHeaders = function (CSP_headers) { + if (!hasUnsafeEval(CSP_headers)) { + console.error("NO_UNSAFE_EVAL"); + console.log(CSP_headers); + return false; + } + if (!hasUnsafeInline(CSP_headers)) { + console.error("NO_UNSAFE_INLINE"); + return void false; + } + return true; + }; + + // XXX run these from /checkup/inner.js and report to /checkup/main.js + assert(function (cb, msg) { + var url = '/sheet/inner.html'; + msg.appendChild(h('span', [ + code(url), + ' has the wrong headers.', + ])); + getHeaders(url, function (err, headers, xhr) { + var CSP_headers = parseCSP(xhr.getResponseHeader('content-security-policy')); + cb(hasOnlyOfficeHeaders(CSP_headers)); + }); + }); + + assert(function (cb, msg) { + var url = '/common/onlyoffice/v4/web-apps/apps/spreadsheeteditor/main/index.html'; + msg.appendChild(h('span', [ + code(url), + ' has the wrong headers.', + ])); + getHeaders(url, function (err, headers, xhr) { + var CSP_headers = parseCSP(xhr.getResponseHeader('content-security-policy')); + cb(hasOnlyOfficeHeaders(CSP_headers)); + }); + }); + + var row = function (cells) { + return h('tr', cells.map(function (cell) { + return h('td', cell); + })); + }; + + var failureReport = function (obj) { + return h('div.error', [ + h('h5', obj.message), + h('table', [ + row(["Failed test number", obj.test + 1]), + row(["Returned value", obj.output]), + ]), + ]); + }; + + var completed = 0; + var $progress = $('#cp-progress'); + + var versionStatement = function () { + return h('p', [ + "This instance is running ", + h('span.cp-app-checkup-version',[ + "CryptPad", + ' ', + Pages.versionString, + ]), + '.', + ]); + }; + + Assert.run(function (state) { + var errors = state.errors; + var failed = errors.length; + + Messages.assert_numberOfTestsPassed = "{0} / {1} tests passed."; + + var statusClass = failed? 'failure': 'success'; + + var failedDetails = "Details found below"; + var successDetails = "This checkup only tests the most common configuration issues. You may still experience errors or incorrect behaviour."; + var details = h('p', failed? failedDetails: successDetails); + + var summary = h('div.summary.' + statusClass, [ + versionStatement(), + h('p', Messages._getKey('assert_numberOfTestsPassed', [ + state.passed, + state.total + ])), + details, + ]); + + var report = h('div.report', [ + summary, + h('div.failures', errors.map(failureReport)), + ]); + + $progress.remove(); + $('body').prepend(report); + }, function (i, total) { + console.log('test '+ i +' completed'); + completed++; + Messages.assert_numberOfTestsCompleted = "{0} / {1} tests completed."; + $progress.html('').append(h('div.report.pending.summary', [ + versionStatement(), + h('p', [ + h('i.fa.fa-spinner.fa-pulse'), + h('span', Messages._getKey('assert_numberOfTestsCompleted', [completed, total])) + ]) + ])); + }); +});