add some debugging advice to the checkup page

This commit is contained in:
ansuz 2021-04-16 19:30:12 +05:30
parent 10e9d90c56
commit b7975bb791
3 changed files with 200 additions and 37 deletions

View File

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

View File

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

View File

@ -19,38 +19,100 @@ define([
], function ($, ApiConfig, Assertions, h, Messages, DomReady,
nThen, SFCommonO, Login, Hash, Util, Pinpad,
NetConfig) {
var assert = Assertions();
var Assert = Assertions();
var trimSlashes = function (s) {
if (typeof(s) !== 'string') { return s; }
return s.replace(/\/+$/, '');
};
var _alert = function (content) {
return h('span.advisory-text', content);
var assert = function (f, msg) {
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 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);
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);
}, _alert('Sandbox configuration: httpUnsafeOrigin !== httpSafeOrigin'));
});
assert(function (cb) {
cb(trimmedSafe === ApiConfig.httpSafeOrigin);
}, "httpSafeOrigin must not have a trailing slash");
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) {
msg.appendChild(h("span", [
"It appears that you are trying to load this page via an origin other than its main domain (",
h('code', ApiConfig.httpUnsafeOrigin),
"). See the ",
h('code', 'httpUnsafeOrigin'),
" option in ",
CONFIG_PATH(),
" which is exposed via ",
API_CONFIG_LINK(),
'.',
]));
var origin = window.location.origin;
return void cb(ApiConfig.httpUnsafeOrigin === origin);
}, _alert('Sandbox configuration: loading via httpUnsafeOrigin'));
});
var checkAvailability = function (url, cb) {
$.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);
}, _alert("Main domain is not available"));
});
// 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;
nThen(function (waitFor) {
DomReady.onReady(waitFor());
@ -82,16 +170,22 @@ define([
clearTimeout(to);
cb(true);
});
}, _alert("Sandbox domain is not available"));
});
// Test Websocket
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 to = setTimeout(function () {
console.error('Websocket TIMEOUT');
evWSError.fire();
cb('TIMEOUT (5 seconds)');
cb('Could not connect to the websocket server within 5 seconds.');
}, 5000);
ws.onopen = function () {
clearTimeout(to);
@ -99,14 +193,25 @@ define([
};
ws.onerror = function (err) {
clearTimeout(to);
console.error('Websocket error', err);
console.error('[Websocket error]', err);
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
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 opt = Login.allocateBytes(bytes);
@ -132,7 +237,7 @@ define([
// If WebSockets aren't working, don't wait forever here
evWSError.reg(function () {
waitFor.abort();
cb("No WebSocket (test number 6)");
cb("No WebSocket available");
});
// Create proxy
Login.loadUserObject(opt, waitFor(function (err, rt) {
@ -200,28 +305,78 @@ define([
cb(true);
});
}, _alert("Login block is not working (write/read/remove)"));
});
assert(function (cb) {
var url = '/common/onlyoffice/v4/web-apps/apps/spreadsheeteditor/main/index.html';
var sheetURL = '/common/onlyoffice/v4/web-apps/apps/spreadsheeteditor/main/index.html';
assert(function (cb, msg) {
msg.innerText = "Missing HTTP headers required for .xlsx export from sheets. ";
var url = sheetURL;
var expect = {
'cross-origin-resource-policy': 'cross-origin',
'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, {
complete: function (xhr) {
cb(!Object.keys(expect).some(function (k) {
var response = xhr.getResponseHeader(k);
console.log(k, response);
return response !== expect[k];
if (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, 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);
},
});
});
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. ",
]));
assert(function (cb) {
cb(true);
$.ajax('/api/broadcast', {
dataType: 'text',
complete: function (xhr) {
@ -229,7 +384,7 @@ define([
cb(xhr.status === 200);
},
});
}, _alert("/api/broadcast is not available"));
});
var row = function (cells) {
return h('tr', cells.map(function (cell) {
@ -249,7 +404,7 @@ define([
var completed = 0;
var $progress = $('#cp-progress');
assert.run(function (state) {
Assert.run(function (state) {
var errors = state.errors;
var failed = errors.length;