add some debugging advice to the checkup page

pull/1/head
ansuz 4 years ago
parent 10e9d90c56
commit b7975bb791

@ -58,6 +58,13 @@ html, body {
border: 1px solid red; border: 1px solid red;
background-color: @cp_alerts-danger-bg; background-color: @cp_alerts-danger-bg;
color: @cp_alerts-danger-text; color: @cp_alerts-danger-text;
code {
word-break: keep-all;
font-style: italic;
}
a {
color: @cryptpad_color_link;
}
} }
iframe { iframe {

@ -3,8 +3,9 @@ define([], function () {
var failMessages = []; var failMessages = [];
var passed = 0; var passed = 0;
var ASSERTS = []; var ASSERTS = [];
var MESSAGES = [];
var assert = function (test, msg) { var assert = function (test, msg) {
MESSAGES.push(msg || false);
ASSERTS.push(function (cb, i) { ASSERTS.push(function (cb, i) {
test(function (result) { test(function (result) {
if (result === true) { if (result === true) {
@ -17,7 +18,7 @@ define([], function () {
output: result, output: result,
}); });
} }
}); }, msg);
}); });
}; };

@ -19,38 +19,100 @@ define([
], function ($, ApiConfig, Assertions, h, Messages, DomReady, ], function ($, ApiConfig, Assertions, h, Messages, DomReady,
nThen, SFCommonO, Login, Hash, Util, Pinpad, nThen, SFCommonO, Login, Hash, Util, Pinpad,
NetConfig) { NetConfig) {
var assert = Assertions(); var Assert = Assertions();
var trimSlashes = function (s) { var trimSlashes = function (s) {
if (typeof(s) !== 'string') { return s; } if (typeof(s) !== 'string') { return s; }
return s.replace(/\/+$/, ''); return s.replace(/\/+$/, '');
}; };
var _alert = function (content) { var assert = function (f, msg) {
return h('span.advisory-text', content); Assert(f, msg || h('span.advisory-text'));
};
var CONFIG_PATH = function () {
return h('code', 'cryptpad/config/config.js');
};
var API_CONFIG_LINK = function () {
return h('a', {
href: '/api/config',
target: '_blank',
}, '/api/config');
};
var RESTART_WARNING = function () {
return h('span', [
'Changes to ',
CONFIG_PATH(),
' will require a server restart in order for ',
API_CONFIG_LINK(),
' to be updated.',
]);
}; };
var trimmedSafe = trimSlashes(ApiConfig.httpSafeOrigin); var trimmedSafe = trimSlashes(ApiConfig.httpSafeOrigin);
var trimmedUnsafe = trimSlashes(ApiConfig.httpUnsafeOrigin); var trimmedUnsafe = trimSlashes(ApiConfig.httpUnsafeOrigin);
assert(function (cb) { assert(function (cb, msg) {
msg.appendChild(h('span', [
"CryptPad's sandbox requires that both ",
h('code', 'httpUnsafeOrigin'),
' and ',
h('code', 'httpSafeOrigin'),
" be configured in ",
CONFIG_PATH(),
'. ',
RESTART_WARNING(),
]));
//console.error(trimmedSafe, trimmedUnsafe); //console.error(trimmedSafe, trimmedUnsafe);
cb(Boolean(trimmedSafe && trimmedUnsafe)); cb(Boolean(trimmedSafe && trimmedUnsafe));
}, _alert("Sandbox configuration: ensure that both httpUnsafeOrigin and httpSafeOrigin are defined")); });
assert(function (cb, msg) {
msg.appendChild(h('span', [
h('code', 'httpUnsafeOrigin'),
' and ',
h('code', 'httpSafeOrigin'),
' are equivalent. ',
"In order for CryptPad's security features to be as effective as intended they must be different. ",
"See ",
CONFIG_PATH(),
'. ',
RESTART_WARNING(),
]));
assert(function (cb) {
return void cb(trimmedSafe !== trimmedUnsafe); return void cb(trimmedSafe !== trimmedUnsafe);
}, _alert('Sandbox configuration: httpUnsafeOrigin !== httpSafeOrigin')); });
assert(function (cb, msg) {
msg.appendChild(h('span', [
h('code', 'httpUnsafeOrigin'),
' and ',
h('code', 'httpSafeOrigin'),
' must not contain trailing slashes. This can be configured in ',
CONFIG_PATH(),
'. ',
RESTART_WARNING(),
]));
cb(trimmedSafe === ApiConfig.httpSafeOrigin && trimmedUnsafe === ApiConfig.httpUnsafeOrigin);
});
assert(function (cb) { assert(function (cb, msg) {
cb(trimmedSafe === ApiConfig.httpSafeOrigin); msg.appendChild(h("span", [
}, "httpSafeOrigin must not have a trailing slash"); "It appears that you are trying to load this page via an origin other than its main domain (",
h('code', ApiConfig.httpUnsafeOrigin),
assert(function (cb) { "). See the ",
h('code', 'httpUnsafeOrigin'),
" option in ",
CONFIG_PATH(),
" which is exposed via ",
API_CONFIG_LINK(),
'.',
]));
var origin = window.location.origin; var origin = window.location.origin;
return void cb(ApiConfig.httpUnsafeOrigin === origin); return void cb(ApiConfig.httpUnsafeOrigin === origin);
}, _alert('Sandbox configuration: loading via httpUnsafeOrigin')); });
var checkAvailability = function (url, cb) { var checkAvailability = function (url, cb) {
$.ajax({ $.ajax({
@ -62,12 +124,38 @@ define([
}); });
}; };
assert(function (cb) { assert(function (cb, msg) {
msg.appendChild(h('span', [
"The main domain (configured via ",
h('code', 'httpUnsafeOrigin'),
' as ',
ApiConfig.httpUnsafeOrigin,
' in ',
CONFIG_PATH(),
' and exposed via ',
API_CONFIG_LINK(),
') could not be reached.',
]));
checkAvailability(trimmedUnsafe, cb); checkAvailability(trimmedUnsafe, cb);
}, _alert("Main domain is not available")); });
// Try loading an iframe on the safe domain // Try loading an iframe on the safe domain
assert(function (cb) { assert(function (cb, msg) {
msg.appendChild(h('span', [
"Your browser was not able to load an iframe using the origin specified as ",
h('code', "httpSafeOrigin"),
" (",
ApiConfig.httpSafeOrigin,
") in ",
CONFIG_PATH(),
". This can be caused by an invalid ",
h('code', 'httpUnsafeDomain'),
', invalid CSP configuration in your reverse proxy, invalid SSL certificates, and many other factors. ',
'More information about your particular error may be found in your browser console. ',
RESTART_WARNING(),
]));
var to; var to;
nThen(function (waitFor) { nThen(function (waitFor) {
DomReady.onReady(waitFor()); DomReady.onReady(waitFor());
@ -82,16 +170,22 @@ define([
clearTimeout(to); clearTimeout(to);
cb(true); cb(true);
}); });
}, _alert("Sandbox domain is not available")); });
// Test Websocket // Test Websocket
var evWSError = Util.mkEvent(true); var evWSError = Util.mkEvent(true);
assert(function (cb) { assert(function (_cb, msg) {
var cb = Util.once(Util.both(_cb, function (err) {
if (typeof(err) === 'string') {
msg.innerText = err;
}
}));
var ws = new WebSocket(NetConfig.getWebsocketURL()); var ws = new WebSocket(NetConfig.getWebsocketURL());
var to = setTimeout(function () { var to = setTimeout(function () {
console.error('Websocket TIMEOUT'); console.error('Websocket TIMEOUT');
evWSError.fire(); evWSError.fire();
cb('TIMEOUT (5 seconds)'); cb('Could not connect to the websocket server within 5 seconds.');
}, 5000); }, 5000);
ws.onopen = function () { ws.onopen = function () {
clearTimeout(to); clearTimeout(to);
@ -99,14 +193,25 @@ define([
}; };
ws.onerror = function (err) { ws.onerror = function (err) {
clearTimeout(to); clearTimeout(to);
console.error('Websocket error', err); console.error('[Websocket error]', err);
evWSError.fire(); evWSError.fire();
cb('WebSocket error: check your console'); cb('Unable to connect to the websocket server. More information may be available in your browser console ([Websocket error]).');
}; };
}, _alert("Websocket is not available")); });
// Test login block // Test login block
assert(function (cb) { assert(function (cb, msg) {
msg.appendChild(h('span', [
"Unable to create, retrieve, or remove encrypted credentials from the server. ",
"This is most commonly caused by a mismatch between the value of the ",
h('code', 'blockPath'),
' value configured in ',
CONFIG_PATH(),
" and the corresponding settings in your reverse proxy's configuration file,",
" but it can also be explained by a websocket error. ",
RESTART_WARNING(),
]));
var bytes = new Uint8Array(Login.requiredBytes); var bytes = new Uint8Array(Login.requiredBytes);
var opt = Login.allocateBytes(bytes); var opt = Login.allocateBytes(bytes);
@ -132,7 +237,7 @@ define([
// If WebSockets aren't working, don't wait forever here // If WebSockets aren't working, don't wait forever here
evWSError.reg(function () { evWSError.reg(function () {
waitFor.abort(); waitFor.abort();
cb("No WebSocket (test number 6)"); cb("No WebSocket available");
}); });
// Create proxy // Create proxy
Login.loadUserObject(opt, waitFor(function (err, rt) { Login.loadUserObject(opt, waitFor(function (err, rt) {
@ -200,28 +305,78 @@ define([
cb(true); cb(true);
}); });
}, _alert("Login block is not working (write/read/remove)")); });
var sheetURL = '/common/onlyoffice/v4/web-apps/apps/spreadsheeteditor/main/index.html';
assert(function (cb) { assert(function (cb, msg) {
var url = '/common/onlyoffice/v4/web-apps/apps/spreadsheeteditor/main/index.html'; msg.innerText = "Missing HTTP headers required for .xlsx export from sheets. ";
var url = sheetURL;
var expect = { var expect = {
'cross-origin-resource-policy': 'cross-origin', 'cross-origin-resource-policy': 'cross-origin',
'cross-origin-embedder-policy': 'require-corp', 'cross-origin-embedder-policy': 'require-corp',
//'cross-origin-opener-policy': 'same-origin', // FIXME this is in our nginx config but not server.js
}; };
$.ajax(url, { $.ajax(url, {
complete: function (xhr) { complete: function (xhr) {
cb(!Object.keys(expect).some(function (k) { cb(!Object.keys(expect).some(function (k) {
var response = xhr.getResponseHeader(k); var response = xhr.getResponseHeader(k);
console.log(k, response); if (response !== expect[k]) {
return response !== expect[k]; msg.appendChild(h('span', [
'A value of ',
h('code', expect[k]),
' was expected for the ',
h('code', k),
' HTTP header, but instead a value of "',
h('code', response),
'" was received.',
]));
return true; // returning true indicates that a value is incorrect
}
})); }));
}, },
}); });
}, _alert("Missing HTTP headers required for XLSX export")); });
assert(function (cb) { assert(function (cb, msg) {
msg.appendChild(h('span', [
"The spreadsheet editor's code was not served with the required Content-Security Policy headers. ",
"This is most often caused by incorrectly configured sandbox parameters (",
h('code', 'httpUnsafeOrigin'),
' and ',
h('code', 'httpSafeOrigin'),
' in ',
CONFIG_PATH,
"), or settings in your reverse proxy's configuration which don't match your application server's config. ",
RESTART_WARNING(),
]));
$.ajax(sheetURL, {
complete: function (xhr) {
var csp = xhr.getResponseHeader('Content-Security-Policy');
if (!/unsafe\-eval/.test(csp)) {
// OnlyOffice requires unsafe-eval
return cb(false);
}
if (!/unsafe\-inline/.test(csp)) {
// OnlyOffice also requires unsafe-inline
return cb(false);
}
//console.error('CSP', csp);
cb(true); cb(true);
},
});
});
assert(function (cb, msg) {
msg.appendChild(h('span', [
h('code', '/api/broadcast'),
" could not be loaded. This can be caused by an outdated application server or an incorrectly configured reverse proxy. ",
"Even if the most recent code has been downloaded it's possible the application server has not been restarted. ",
"Your browser console may provide more details as to why this resource could not be loaded. ",
]));
$.ajax('/api/broadcast', { $.ajax('/api/broadcast', {
dataType: 'text', dataType: 'text',
complete: function (xhr) { complete: function (xhr) {
@ -229,7 +384,7 @@ define([
cb(xhr.status === 200); cb(xhr.status === 200);
}, },
}); });
}, _alert("/api/broadcast is not available")); });
var row = function (cells) { var row = function (cells) {
return h('tr', cells.map(function (cell) { return h('tr', cells.map(function (cell) {
@ -249,7 +404,7 @@ define([
var completed = 0; var completed = 0;
var $progress = $('#cp-progress'); var $progress = $('#cp-progress');
assert.run(function (state) { Assert.run(function (state) {
var errors = state.errors; var errors = state.errors;
var failed = errors.length; var failed = errors.length;

Loading…
Cancel
Save