2021-02-23 16:53:34 +08:00
define ( [
'jquery' ,
'/api/config' ,
'/assert/assertions.js' ,
'/common/hyperscript.js' ,
'/customize/messages.js' ,
2021-03-02 23:05:18 +08:00
'/common/dom-ready.js' ,
2023-01-07 19:49:46 +08:00
'/components/nthen/index.js' ,
2021-02-24 16:23:59 +08:00
'/common/sframe-common-outer.js' ,
2021-03-02 23:05:18 +08:00
'/customize/login.js' ,
'/common/common-hash.js' ,
'/common/common-util.js' ,
'/common/pinpad.js' ,
2021-03-02 23:45:52 +08:00
'/common/outer/network-config.js' ,
2021-05-05 14:38:20 +08:00
'/customize/pages.js' ,
2021-08-02 20:06:51 +08:00
'/checkup/checkup-tools.js' ,
2022-02-14 14:50:00 +08:00
'/customize/application_config.js' ,
2021-02-23 16:53:34 +08:00
2023-01-07 00:32:23 +08:00
'/components/tweetnacl/nacl-fast.min.js' ,
2023-01-07 15:27:22 +08:00
'css!/components/components-font-awesome/css/font-awesome.min.css' ,
2021-05-03 17:16:26 +08:00
'less!/checkup/app-checkup.less' ,
2021-03-02 23:05:18 +08:00
] , function ( $ , ApiConfig , Assertions , h , Messages , DomReady ,
2021-03-02 23:45:52 +08:00
nThen , SFCommonO , Login , Hash , Util , Pinpad ,
2022-02-15 16:33:56 +08:00
NetConfig , Pages , Tools , AppConfig ) {
2022-05-03 20:50:18 +08:00
window . CHECKUP _MAIN _LOADED = true ;
2021-04-16 22:00:12 +08:00
var Assert = Assertions ( ) ;
2021-02-24 16:23:59 +08:00
var trimSlashes = function ( s ) {
if ( typeof ( s ) !== 'string' ) { return s ; }
return s . replace ( /\/+$/ , '' ) ;
} ;
2021-04-16 22:00:12 +08:00
var assert = function ( f , msg ) {
2021-05-03 17:16:26 +08:00
Assert ( f , msg || h ( 'span.advisory-text.cp-danger' ) ) ;
2021-04-16 22:00:12 +08:00
} ;
2021-05-31 19:00:47 +08:00
var code = function ( content ) {
return h ( 'code' , content ) ;
} ;
2021-04-16 22:00:12 +08:00
var CONFIG _PATH = function ( ) {
2021-05-31 19:00:47 +08:00
return code ( 'cryptpad/config/config.js' ) ;
2021-04-16 22:00:12 +08:00
} ;
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.' ,
] ) ;
2021-02-24 16:23:59 +08:00
} ;
2021-05-31 19:00:47 +08:00
var link = function ( href , text ) {
return h ( 'a' , {
href : href ,
rel : 'noopener noreferrer' ,
target : '_blank' ,
} , text ) ;
} ;
var setWarningClass = function ( msg ) {
$ ( msg ) . removeClass ( 'cp-danger' ) . addClass ( 'cp-warning' ) ;
} ;
2022-02-10 15:30:16 +08:00
var cacheBuster = Tools . cacheBuster ;
2021-05-21 23:05:48 +08:00
2021-02-24 16:23:59 +08:00
var trimmedSafe = trimSlashes ( ApiConfig . httpSafeOrigin ) ;
var trimmedUnsafe = trimSlashes ( ApiConfig . httpUnsafeOrigin ) ;
2022-02-14 14:15:44 +08:00
var fileHost = ApiConfig . fileHost ;
2022-05-11 15:42:12 +08:00
var accounts _api = ApiConfig . accounts _api || AppConfig . accounts _api || undefined ;
2022-02-14 14:15:44 +08:00
2022-03-23 16:05:17 +08:00
var getAPIPlaceholderPath = function ( relative ) {
var absolute ;
try {
absolute = new URL ( relative , ApiConfig . fileHost || ApiConfig . httpUnsafeOrigin ) . href ;
} catch ( err ) {
absolute = relative ;
}
return absolute ;
} ;
var blobPlaceholderPath = getAPIPlaceholderPath ( '/blob/placeholder.txt' ) ;
var blockPlaceholderPath = getAPIPlaceholderPath ( '/block/placeholder.txt' ) ;
2022-02-14 14:15:44 +08:00
var API _URL ;
try {
API _URL = new URL ( NetConfig . getWebsocketURL ( window . location . origin ) , trimmedUnsafe ) ;
} catch ( err ) {
console . error ( err ) ;
2022-02-15 16:33:56 +08:00
}
2021-02-24 16:23:59 +08:00
2022-02-14 14:50:00 +08:00
var ACCOUNTS _URL ;
try {
if ( typeof ( AppConfig . upgradeURL ) === 'string' ) {
ACCOUNTS _URL = new URL ( AppConfig . upgradeURL , trimmedUnsafe ) . origin ;
}
} catch ( err ) {
console . error ( err ) ;
}
2022-02-18 15:56:42 +08:00
var debugOrigins = {
httpUnsafeOrigin : trimmedUnsafe ,
httpSafeOrigin : trimmedSafe ,
currentOrigin : window . location . origin ,
} ;
2021-04-16 22:00:12 +08:00
assert ( function ( cb , msg ) {
msg . appendChild ( h ( 'span' , [
"CryptPad's sandbox requires that both " ,
2021-05-31 19:00:47 +08:00
code ( 'httpUnsafeOrigin' ) ,
2021-04-16 22:00:12 +08:00
' and ' ,
2021-05-31 19:00:47 +08:00
code ( 'httpSafeOrigin' ) ,
2021-04-16 22:00:12 +08:00
" be configured in " ,
CONFIG _PATH ( ) ,
'. ' ,
RESTART _WARNING ( ) ,
] ) ) ;
2021-02-24 16:23:59 +08:00
//console.error(trimmedSafe, trimmedUnsafe);
2022-02-18 15:56:42 +08:00
cb ( Boolean ( trimmedSafe && trimmedUnsafe ) || debugOrigins ) ;
2021-04-16 22:00:12 +08:00
} ) ;
assert ( function ( cb , msg ) {
msg . appendChild ( h ( 'span' , [
2021-05-31 19:00:47 +08:00
code ( 'httpUnsafeOrigin' ) ,
2021-04-16 22:00:12 +08:00
' and ' ,
2021-05-31 19:00:47 +08:00
code ( 'httpSafeOrigin' ) ,
2021-04-16 22:00:12 +08:00
' are equivalent. ' ,
"In order for CryptPad's security features to be as effective as intended they must be different. " ,
"See " ,
CONFIG _PATH ( ) ,
'. ' ,
RESTART _WARNING ( ) ,
] ) ) ;
2021-02-23 16:53:34 +08:00
2022-02-18 15:56:42 +08:00
return void cb ( trimmedSafe !== trimmedUnsafe || trimmedUnsafe ) ;
2021-04-16 22:00:12 +08:00
} ) ;
assert ( function ( cb , msg ) {
msg . appendChild ( h ( 'span' , [
2021-05-31 19:00:47 +08:00
code ( 'httpUnsafeOrigin' ) ,
2021-04-16 22:00:12 +08:00
' and ' ,
2021-05-31 19:00:47 +08:00
code ( 'httpSafeOrigin' ) ,
2021-04-16 22:00:12 +08:00
' must not contain trailing slashes. This can be configured in ' ,
CONFIG _PATH ( ) ,
'. ' ,
RESTART _WARNING ( ) ,
] ) ) ;
2022-02-18 15:56:42 +08:00
var result = trimmedSafe === ApiConfig . httpSafeOrigin &&
trimmedUnsafe === ApiConfig . httpUnsafeOrigin ;
cb ( result || debugOrigins ) ;
2021-04-16 22:00:12 +08:00
} ) ;
2021-02-23 16:53:34 +08:00
2021-04-16 22:00:12 +08:00
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 (" ,
2021-05-31 19:00:47 +08:00
code ( ApiConfig . httpUnsafeOrigin ) ,
2021-03-19 17:50:33 +08:00
2021-04-16 22:00:12 +08:00
"). See the " ,
2021-05-31 19:00:47 +08:00
code ( 'httpUnsafeOrigin' ) ,
2021-04-16 22:00:12 +08:00
" option in " ,
CONFIG _PATH ( ) ,
" which is exposed via " ,
API _CONFIG _LINK ( ) ,
'.' ,
] ) ) ;
2021-03-18 14:56:05 +08:00
var origin = window . location . origin ;
2022-02-18 15:56:42 +08:00
return void cb ( ApiConfig . httpUnsafeOrigin === origin || debugOrigins ) ;
2021-04-16 22:00:12 +08:00
} ) ;
2021-02-24 16:23:59 +08:00
var checkAvailability = function ( url , cb ) {
$ . ajax ( {
2021-05-21 23:05:48 +08:00
url : cacheBuster ( url ) ,
2021-03-02 23:05:18 +08:00
data : { } ,
2021-02-24 16:23:59 +08:00
complete : function ( xhr ) {
2022-02-18 15:56:42 +08:00
cb ( xhr . status === 200 || xhr . status ) ;
2021-02-24 16:23:59 +08:00
} ,
} ) ;
} ;
2021-04-16 22:00:12 +08:00
assert ( function ( cb , msg ) {
msg . appendChild ( h ( 'span' , [
"The main domain (configured via " ,
2021-05-31 19:00:47 +08:00
code ( 'httpUnsafeOrigin' ) ,
2021-04-16 22:00:12 +08:00
' as ' ,
ApiConfig . httpUnsafeOrigin ,
' in ' ,
CONFIG _PATH ( ) ,
' and exposed via ' ,
API _CONFIG _LINK ( ) ,
') could not be reached.' ,
] ) ) ;
2021-02-24 16:23:59 +08:00
checkAvailability ( trimmedUnsafe , cb ) ;
2021-04-16 22:00:12 +08:00
} ) ;
2021-02-24 16:23:59 +08:00
2021-03-02 23:05:18 +08:00
// Try loading an iframe on the safe domain
2021-04-16 22:00:12 +08:00
assert ( function ( cb , msg ) {
msg . appendChild ( h ( 'span' , [
"Your browser was not able to load an iframe using the origin specified as " ,
2021-05-31 19:00:47 +08:00
code ( "httpSafeOrigin" ) ,
2021-04-16 22:00:12 +08:00
" (" ,
ApiConfig . httpSafeOrigin ,
") in " ,
CONFIG _PATH ( ) ,
". This can be caused by an invalid " ,
2021-05-31 19:00:47 +08:00
code ( 'httpUnsafeDomain' ) ,
2021-04-16 22:00:12 +08:00
', 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 ( ) ,
] ) ) ;
2021-03-02 23:05:18 +08:00
var to ;
nThen ( function ( waitFor ) {
DomReady . onReady ( waitFor ( ) ) ;
} ) . nThen ( function ( waitFor ) {
to = setTimeout ( function ( ) {
2021-03-03 00:48:38 +08:00
console . error ( 'TIMEOUT loading iframe on the safe domain' ) ;
2022-02-18 15:56:42 +08:00
cb ( 'TIMEOUT' ) ;
} , 10000 ) ;
2021-05-21 23:05:48 +08:00
SFCommonO . initIframe ( waitFor ) ;
2021-05-20 18:46:07 +08:00
} ) . nThen ( function ( ) {
2021-03-02 23:05:18 +08:00
// Iframe is loaded
clearTimeout ( to ) ;
2021-05-27 16:47:32 +08:00
console . log ( "removing sandbox iframe" ) ;
$ ( 'iframe#sbox-iframe' ) . remove ( ) ;
2021-03-02 23:05:18 +08:00
cb ( true ) ;
} ) ;
2021-04-16 22:00:12 +08:00
} ) ;
2021-03-02 23:05:18 +08:00
2021-05-27 16:47:32 +08:00
var shared _websocket ;
2021-03-02 23:45:52 +08:00
// Test Websocket
var evWSError = Util . mkEvent ( true ) ;
2021-04-16 22:00:12 +08:00
assert ( function ( _cb , msg ) {
2021-04-19 21:09:00 +08:00
var timeoutErr = 'Could not connect to the websocket server within 5 seconds.' ;
var cb = Util . once ( Util . both ( _cb , function ( status ) {
if ( status === true ) { return ; }
msg . appendChild ( h ( 'span#websocket' , [
status || 'Unknown websocket error' ,
] ) ) ;
2021-04-16 22:00:12 +08:00
} ) ) ;
2021-05-27 17:03:03 +08:00
var ws = new WebSocket ( NetConfig . getWebsocketURL ( ) ) ;
shared _websocket = ws ;
2021-03-02 23:45:52 +08:00
var to = setTimeout ( function ( ) {
console . error ( 'Websocket TIMEOUT' ) ;
evWSError . fire ( ) ;
2021-04-19 21:09:00 +08:00
cb ( timeoutErr ) ;
2022-02-18 15:56:42 +08:00
} , 10000 ) ;
2021-03-02 23:45:52 +08:00
ws . onopen = function ( ) {
clearTimeout ( to ) ;
cb ( true ) ;
} ;
ws . onerror = function ( err ) {
clearTimeout ( to ) ;
2021-04-16 22:00:12 +08:00
console . error ( '[Websocket error]' , err ) ;
2021-03-02 23:45:52 +08:00
evWSError . fire ( ) ;
2021-04-16 22:00:12 +08:00
cb ( 'Unable to connect to the websocket server. More information may be available in your browser console ([Websocket error]).' ) ;
2021-03-02 23:45:52 +08:00
} ;
2021-04-16 22:00:12 +08:00
} ) ;
2021-03-02 23:45:52 +08:00
// Test login block
2021-05-27 16:47:32 +08:00
var shared _realtime ;
2021-04-19 21:09:00 +08:00
assert ( function ( _cb , msg ) {
var websocketErr = "No WebSocket available" ;
var cb = Util . once ( Util . both ( _cb , function ( status ) {
if ( status === true ) { return ; }
if ( status === websocketErr ) {
msg . appendChild ( h ( 'span' , [
websocketErr ,
' See ' ,
h ( 'a' , {
href : '#websocket' ,
} , 'the related websocket error' ) ,
] ) ) ;
return ;
}
// else
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 " ,
2021-05-31 19:00:47 +08:00
code ( 'blockPath' ) ,
2021-04-19 21:09:00 +08:00
' 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 ( ) ,
] ) ) ;
} ) ) ;
2021-04-16 22:00:12 +08:00
2021-07-12 15:54:32 +08:00
// time out after 30 seconds
setTimeout ( function ( ) {
cb ( 'TIMEOUT' ) ;
} , 30000 ) ;
2021-03-02 23:05:18 +08:00
var bytes = new Uint8Array ( Login . requiredBytes ) ;
var opt = Login . allocateBytes ( bytes ) ;
var blockUrl = Login . Block . getBlockUrl ( opt . blockKeys ) ;
var blockRequest = Login . Block . serialize ( "{}" , opt . blockKeys ) ;
var removeRequest = Login . Block . remove ( opt . blockKeys ) ;
2021-05-12 16:48:26 +08:00
console . warn ( 'Testing block URL (%s). One 404 is normal.' , blockUrl ) ;
2021-03-02 23:05:18 +08:00
var userHash = '/2/drive/edit/000000000000000000000000' ;
var secret = Hash . getSecrets ( 'drive' , userHash ) ;
opt . keys = secret . keys ;
opt . channelHex = secret . channel ;
2021-04-26 21:01:33 +08:00
var RT , rpc , exists , restricted ;
2021-03-02 23:05:18 +08:00
nThen ( function ( waitFor ) {
2021-03-03 00:48:38 +08:00
Util . fetch ( blockUrl , waitFor ( function ( err ) {
2021-03-02 23:05:18 +08:00
if ( err ) { return ; } // No block found
exists = true ;
} ) ) ;
} ) . nThen ( function ( waitFor ) {
2021-03-02 23:45:52 +08:00
// If WebSockets aren't working, don't wait forever here
evWSError . reg ( function ( ) {
waitFor . abort ( ) ;
2021-04-19 21:09:00 +08:00
cb ( websocketErr ) ;
2021-03-02 23:45:52 +08:00
} ) ;
2021-03-02 23:05:18 +08:00
// Create proxy
Login . loadUserObject ( opt , waitFor ( function ( err , rt ) {
if ( err ) {
waitFor . abort ( ) ;
console . error ( "Can't create new channel. This may also be a websocket issue." ) ;
return void cb ( false ) ;
}
2021-05-27 16:47:32 +08:00
shared _realtime = RT = rt ;
2021-03-02 23:05:18 +08:00
var proxy = rt . proxy ;
proxy . edPublic = opt . edPublic ;
proxy . edPrivate = opt . edPrivate ;
proxy . curvePublic = opt . curvePublic ;
proxy . curvePrivate = opt . curvePrivate ;
rt . realtime . onSettle ( waitFor ( ) ) ;
} ) ) ;
} ) . nThen ( function ( waitFor ) {
// Init RPC
Pinpad . create ( RT . network , RT . proxy , waitFor ( function ( e , _rpc ) {
if ( e ) {
waitFor . abort ( ) ;
console . error ( "Can't initialize RPC" , e ) ; // INVALID_KEYS
return void cb ( false ) ;
}
rpc = _rpc ;
} ) ) ;
} ) . nThen ( function ( waitFor ) {
// Write block
if ( exists ) { return ; }
rpc . writeLoginBlock ( blockRequest , waitFor ( function ( e ) {
2021-04-26 21:01:33 +08:00
// we should tolerate restricted registration
// and proceed to clean up after any data we've created
if ( e === 'E_RESTRICTED' ) {
restricted = true ;
return void cb ( true ) ;
}
2021-03-02 23:05:18 +08:00
if ( e ) {
waitFor . abort ( ) ;
console . error ( "Can't write login block" , e ) ;
return void cb ( false ) ;
}
} ) ) ;
} ) . nThen ( function ( waitFor ) {
2021-04-26 21:01:33 +08:00
if ( restricted ) { return ; }
2021-03-02 23:05:18 +08:00
// Read block
2021-03-03 00:48:38 +08:00
Util . fetch ( blockUrl , waitFor ( function ( e ) {
2021-03-02 23:05:18 +08:00
if ( e ) {
waitFor . abort ( ) ;
console . error ( "Can't read login block" , e ) ;
return void cb ( false ) ;
}
} ) ) ;
} ) . nThen ( function ( waitFor ) {
// Remove block
rpc . removeLoginBlock ( removeRequest , waitFor ( function ( e ) {
2021-04-26 21:01:33 +08:00
if ( restricted ) { return ; } // an ENOENT is expected in the case of restricted registration, but we call this anyway to clean up any mess from previous tests.
2021-03-02 23:05:18 +08:00
if ( e ) {
waitFor . abort ( ) ;
console . error ( "Can't remove login block" , e ) ;
console . error ( blockRequest ) ;
return void cb ( false ) ;
}
} ) ) ;
} ) . nThen ( function ( waitFor ) {
rpc . removeOwnedChannel ( secret . channel , waitFor ( function ( e ) {
if ( e ) {
waitFor . abort ( ) ;
console . error ( "Can't remove channel" , e ) ;
return void cb ( false ) ;
}
} ) ) ;
} ) . nThen ( function ( ) {
cb ( true ) ;
} ) ;
2021-04-16 22:00:12 +08:00
} ) ;
2021-02-24 16:23:59 +08:00
2021-11-02 19:42:44 +08:00
var sheetURL = '/common/onlyoffice/v5/web-apps/apps/spreadsheeteditor/main/index.html' ;
2021-04-16 22:00:12 +08:00
assert ( function ( cb , msg ) {
msg . innerText = "Missing HTTP headers required for .xlsx export from sheets. " ;
2021-04-02 22:00:48 +08:00
var expect = {
'cross-origin-resource-policy' : 'cross-origin' ,
'cross-origin-embedder-policy' : 'require-corp' ,
} ;
2022-02-18 15:56:42 +08:00
Tools . common _xhr ( sheetURL , function ( xhr ) {
var result = ! Object . keys ( expect ) . some ( function ( k ) {
var response = xhr . getResponseHeader ( k ) ;
if ( response !== expect [ k ] ) {
msg . appendChild ( h ( 'span' , [
'A value of ' ,
code ( expect [ k ] ) ,
' was expected for the ' ,
code ( k ) ,
' HTTP header, but instead a value of "' ,
code ( response ) ,
'" was received.' ,
] ) ) ;
return true ; // returning true indicates that a value is incorrect
}
} ) ;
cb ( result || xhr . getAllResponseHeaders ( ) ) ;
2021-04-02 22:00:48 +08:00
} ) ;
2021-04-16 22:00:12 +08:00
} ) ;
assert ( function ( cb , msg ) {
2021-05-28 18:04:27 +08:00
setWarningClass ( msg ) ;
2021-06-17 11:39:04 +08:00
var printMessage = function ( value ) {
msg . appendChild ( h ( 'span' , [
"This instance hasn't opted out of participation in Google's " ,
code ( 'FLoC' ) ,
" targeted advertizing network. " ,
"This can be done by setting a " ,
code ( 'permissions-policy' ) ,
" HTTP header with a value of " ,
code ( '"interest-cohort=()"' ) ,
" in the configuration of its reverse proxy instead of the current value (" ,
code ( value ) ,
"). See the provided NGINX configuration file for an example. " ,
h ( 'p' , [
link ( "https://www.eff.org/deeplinks/2021/04/am-i-floced-launch" , 'Learn more' ) ,
] ) ,
] ) ) ;
} ;
2022-02-18 15:56:42 +08:00
Tools . common _xhr ( '/' , function ( xhr ) {
var header = xhr . getResponseHeader ( 'permissions-policy' ) || '' ;
var rules = header . split ( ',' ) ;
if ( rules . includes ( 'interest-cohort=()' ) ) { return void cb ( true ) ; }
printMessage ( JSON . stringify ( header ) ) ;
cb ( header ) ;
2021-05-07 20:23:15 +08:00
} ) ;
} ) ;
2021-04-16 22:00:12 +08:00
assert ( function ( cb , msg ) {
msg . appendChild ( h ( 'span' , [
2021-05-31 19:00:47 +08:00
code ( '/api/broadcast' ) ,
2021-04-16 22:00:12 +08:00
" 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. " ,
] ) ) ;
2021-04-02 22:00:48 +08:00
2022-02-18 15:56:42 +08:00
Tools . common _xhr ( '/api/broadcast' , function ( xhr ) {
var status = xhr . status ;
cb ( status === 200 || status ) ;
2021-04-12 20:19:22 +08:00
} ) ;
2021-04-16 22:00:12 +08:00
} ) ;
2021-04-12 20:19:22 +08:00
2021-05-18 14:55:53 +08:00
var checkAPIHeaders = function ( url , msg , cb ) {
2022-02-18 15:56:42 +08:00
Tools . common _xhr ( url , function ( xhr ) {
var allHeaders = xhr . getAllResponseHeaders ( ) ;
var headers = { } ;
var duplicated = allHeaders . split ( '\n' ) . some ( function ( header ) {
var duplicate ;
header . replace ( /([^:]+):(.*)/ , function ( all , type , value ) {
type = type . trim ( ) ;
if ( typeof ( headers [ type ] ) !== 'undefined' ) {
duplicate = true ;
2021-04-30 12:32:48 +08:00
}
2022-02-18 15:56:42 +08:00
headers [ type ] = value . trim ( ) ;
2021-04-30 12:32:48 +08:00
} ) ;
2022-02-18 15:56:42 +08:00
return duplicate ;
} ) ;
2021-04-30 12:32:48 +08:00
2022-02-18 15:56:42 +08:00
var expect = {
'cross-origin-resource-policy' : 'cross-origin' ,
'cross-origin-embedder-policy' : 'require-corp' ,
} ;
var incorrect = false ;
Object . keys ( expect ) . forEach ( function ( k ) {
var response = xhr . getResponseHeader ( k ) ;
var expected = expect [ k ] ;
if ( response === expected ) { return ; }
incorrect = true ;
msg . appendChild ( h ( 'p' , [
'The ' ,
code ( k ) ,
' header for ' ,
code ( url ) ,
" is '" ,
code ( response ) ,
"' instead of '" ,
code ( expected ) ,
"' as expected." ,
] ) ) ;
} ) ;
cb ( ( ! duplicated && ! incorrect ) || allHeaders ) ;
2021-04-30 12:04:21 +08:00
} ) ;
} ;
2021-04-30 12:32:48 +08:00
var INCORRECT _HEADER _TEXT = ' was served with duplicated or incorrect headers. Compare your reverse-proxy configuration against the provided example.' ;
2021-04-30 12:04:21 +08:00
assert ( function ( cb , msg ) {
2021-05-21 23:05:48 +08:00
var url = '/api/config' ;
2021-04-30 12:32:48 +08:00
msg . innerText = url + INCORRECT _HEADER _TEXT ;
2021-05-18 14:55:53 +08:00
checkAPIHeaders ( url , msg , cb ) ;
2021-04-30 12:04:21 +08:00
} ) ;
assert ( function ( cb , msg ) {
2021-05-21 23:05:48 +08:00
var url = '/api/broadcast' ;
2021-04-30 12:32:48 +08:00
msg . innerText = url + INCORRECT _HEADER _TEXT ;
2021-05-18 14:55:53 +08:00
checkAPIHeaders ( url , msg , cb ) ;
2021-04-30 12:04:21 +08:00
} ) ;
2021-05-03 17:16:26 +08:00
assert ( function ( cb , msg ) {
var email = ApiConfig . adminEmail ;
if ( typeof ( email ) === 'string' && email && email !== 'i.did.not.read.my.config@cryptpad.fr' ) {
return void cb ( true ) ;
}
setWarningClass ( msg ) ;
msg . appendChild ( h ( 'span' , [
'This instance does not provide a valid ' ,
2021-05-31 19:00:47 +08:00
code ( 'adminEmail' ) ,
2021-05-03 18:39:38 +08:00
' which can make it difficult to contact its adminstrator to report vulnerabilities or abusive content.' ,
2021-05-27 16:47:32 +08:00
" This can be configured on your instance's admin panel. Use the provided " ,
code ( "Flush cache'" ) ,
" button for this change to take effect for all users." ,
2021-05-03 17:16:26 +08:00
] ) ) ;
cb ( email ) ;
} ) ;
assert ( function ( cb , msg ) {
var support = ApiConfig . supportMailbox ;
setWarningClass ( msg ) ;
msg . appendChild ( h ( 'span' , [
2021-05-03 18:39:38 +08:00
"This instance's encrypted support ticket functionality has not been enabled. This can make it difficult for its users to safely report issues that concern sensitive information. " ,
2021-05-28 18:04:27 +08:00
"This can be configured via the admin panel's " ,
2021-05-31 19:00:47 +08:00
code ( 'Support' ) ,
2021-05-28 18:04:27 +08:00
" tab." ,
2021-05-03 17:16:26 +08:00
] ) ) ;
2021-05-03 18:39:38 +08:00
cb ( support && typeof ( support ) === 'string' && support . length === 44 ) ;
2021-05-03 17:16:26 +08:00
} ) ;
assert ( function ( cb , msg ) {
var adminKeys = ApiConfig . adminKeys ;
2021-05-03 18:39:38 +08:00
if ( Array . isArray ( adminKeys ) && adminKeys . length >= 1 && typeof ( adminKeys [ 0 ] ) === 'string' && adminKeys [ 0 ] . length === 44 ) {
2021-05-03 17:16:26 +08:00
return void cb ( true ) ;
}
setWarningClass ( msg ) ;
msg . appendChild ( h ( 'span' , [
2021-05-03 18:39:38 +08:00
"This instance has not been configured to support web administration. This can be enabled by adding a registered user's public signing key to the " ,
2021-05-31 19:00:47 +08:00
code ( 'adminKeys' ) ,
2021-05-03 18:39:38 +08:00
' array in ' ,
CONFIG _PATH ( ) ,
'. ' ,
RESTART _WARNING ( ) ,
2021-05-03 17:16:26 +08:00
] ) ) ;
cb ( false ) ;
} ) ;
2021-05-21 23:05:48 +08:00
var response = Util . response ( function ( err ) {
console . error ( 'SANDBOX_ERROR' , err ) ;
} ) ;
var sandboxIframe = h ( 'iframe' , {
class : 'sandbox-test' ,
src : cacheBuster ( trimmedSafe + '/checkup/sandbox/index.html' ) ,
} ) ;
document . body . appendChild ( sandboxIframe ) ;
var sandboxIframeReady = Util . mkEvent ( true ) ;
setTimeout ( function ( ) {
sandboxIframeReady . fire ( "TIMEOUT" ) ;
} , 10 * 1000 ) ;
var postMessage = function ( content , cb ) {
try {
var txid = Util . uid ( ) ;
content . txid = txid ;
response . expect ( txid , cb , 15000 ) ;
sandboxIframe . contentWindow . postMessage ( JSON . stringify ( content ) , '*' ) ;
} catch ( err ) {
console . error ( err ) ;
}
} ;
2021-05-26 21:35:19 +08:00
var deferredPostMessage = function ( content , _cb ) {
var cb = Util . once ( Util . mkAsync ( _cb ) ) ;
nThen ( function ( w ) {
sandboxIframeReady . reg ( w ( function ( err ) {
if ( ! err ) { return ; }
w . abort ( ) ;
cb ( err ) ;
} ) ) ;
} ) . nThen ( function ( ) {
postMessage ( content , cb ) ;
} ) ;
} ;
2021-05-21 23:05:48 +08:00
window . addEventListener ( 'message' , function ( event ) {
try {
var msg = JSON . parse ( event . data ) ;
if ( msg . command === 'READY' ) { return void sandboxIframeReady . fire ( ) ; }
2021-05-27 16:47:32 +08:00
if ( msg . q === "READY" ) { return ; } // ignore messages from the usual sandboxed iframe
2021-05-21 23:05:48 +08:00
var txid = msg . txid ;
if ( ! txid ) { return console . log ( "no handler for " , txid ) ; }
response . handle ( txid , msg . content ) ;
} catch ( err ) {
console . error ( event ) ;
console . error ( err ) ;
}
} ) ;
var parseCSP = function ( CSP ) {
2022-02-18 15:56:42 +08:00
if ( ! CSP ) { return { } ; }
2021-05-21 23:05:48 +08:00
//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 ;
} ;
var CSP _WARNING = function ( url ) {
return h ( 'span' , [
code ( url ) ,
' does not have the required ' ,
code ( "'content-security-policy'" ) ,
' headers set. This is most often related to incorrectly configured sandbox domains or reverse proxies.' ,
] ) ;
} ;
2022-02-10 18:59:48 +08:00
assert ( function ( _cb , msg ) { // FIXME possibly superseded by more advanced CSP tests?
2021-05-21 23:05:48 +08:00
var url = '/sheet/inner.html' ;
var cb = Util . once ( Util . mkAsync ( _cb ) ) ;
msg . appendChild ( CSP _WARNING ( url ) ) ;
2021-05-26 21:35:19 +08:00
deferredPostMessage ( {
command : 'GET_HEADER' ,
content : {
url : url ,
header : 'content-security-policy' ,
} ,
} , function ( content ) {
var CSP _headers = parseCSP ( content ) ;
2022-02-18 15:56:42 +08:00
cb ( hasOnlyOfficeHeaders ( CSP _headers ) || CSP _headers ) ;
2021-05-21 23:05:48 +08:00
} ) ;
} ) ;
2022-02-10 18:59:48 +08:00
assert ( function ( cb , msg ) { // FIXME possibly superseded by more advanced CSP tests?
2021-11-02 19:42:44 +08:00
var url = '/common/onlyoffice/v5/web-apps/apps/spreadsheeteditor/main/index.html' ;
2021-05-21 23:05:48 +08:00
msg . appendChild ( CSP _WARNING ( url ) ) ;
2021-05-26 21:35:19 +08:00
deferredPostMessage ( {
command : 'GET_HEADER' ,
content : {
url : url ,
header : 'content-security-policy' ,
} ,
} , function ( content ) {
var CSP _headers = parseCSP ( content ) ;
2022-02-18 15:56:42 +08:00
cb ( hasOnlyOfficeHeaders ( CSP _headers ) || CSP _headers ) ;
2021-05-21 23:05:48 +08:00
} ) ;
} ) ;
2021-10-20 14:49:01 +08:00
/ *
2021-05-21 23:05:48 +08:00
assert ( function ( cb , msg ) {
var url = '/sheet/inner.html' ;
msg . appendChild ( h ( 'span' , [
code ( url ) ,
' does not have the required ' ,
code ( "'cross-origin-opener-policy'" ) ,
' headers set.' ,
] ) ) ;
2021-05-26 21:35:19 +08:00
deferredPostMessage ( {
command : 'GET_HEADER' ,
content : {
url : url ,
header : 'cross-origin-opener-policy' ,
} ,
} , function ( content ) {
cb ( content === 'same-origin' ) ;
2021-05-21 23:05:48 +08:00
} ) ;
} ) ;
2021-10-20 14:49:01 +08:00
* /
2021-05-21 23:05:48 +08:00
2021-08-02 20:06:51 +08:00
var safariGripe = function ( ) {
return h ( 'p.cp-notice-other' , 'This is expected because Safari and platforms that use its engine lack commonly supported functionality.' ) ;
} ;
var browserIssue = function ( ) {
return h ( 'p.cp-notice-other' , 'This test checks for the presence of features in your browser and is not necessarily caused by server misconfiguration.' ) ;
} ;
assert ( function ( cb , msg ) {
cb = Util . once ( cb ) ;
setWarningClass ( msg ) ;
var notice = h ( 'span' , [
h ( 'p' , 'It appears that some features required for Office file format conversion are not present.' ) ,
2021-08-02 20:29:53 +08:00
Tools . isSafari ( ) ? safariGripe ( ) : undefined ,
browserIssue ( ) ,
2021-08-02 20:06:51 +08:00
] ) ;
msg . appendChild ( notice ) ;
var expected = [
'Atomics' ,
'SharedArrayBuffer' ,
'WebAssembly' ,
[ 'WebAssembly' , 'Memory' ] ,
[ 'WebAssembly' , 'instantiate' ] ,
[ 'WebAssembly' , 'instantiateStreaming' ] ,
[ 'Buffer' , 'from' ] ,
'SharedWorker' ,
'worker' ,
'crossOriginIsolated' ,
] ;
var responses = { } ;
nThen ( function ( w ) {
deferredPostMessage ( {
command : 'CHECK_JS_APIS' ,
content : {
globals : expected ,
} ,
} , w ( function ( response ) {
Util . extend ( responses , response ) ;
} ) ) ;
deferredPostMessage ( {
command : 'FANCY_API_CHECKS' ,
content : {
} ,
} , w ( function ( response ) {
Util . extend ( responses , response ) ;
} ) ) ;
} ) . nThen ( function ( ) {
if ( ! responses . Atomics || ! responses . WebAssembly ) {
return void cb ( responses ) ;
}
if ( responses . SharedArrayBuffer || responses . SharedArrayBufferFallback ) {
return cb ( true ) ;
}
return void cb ( response ) ;
} ) ;
} ) ;
2021-06-15 18:35:17 +08:00
var isHTTPS = function ( host ) {
return /^https:\/\// . test ( host ) ;
} ;
var isOnion = function ( host ) {
return /\.onion$/ . test ( host ) ;
} ;
2021-07-27 07:48:39 +08:00
var isLocalhost = function ( host ) {
return /^http:\/\/localhost/ . test ( host ) ;
} ;
2021-06-15 18:35:17 +08:00
assert ( function ( cb , msg ) {
// provide an exception for development instances
2021-07-27 07:48:39 +08:00
if ( isLocalhost ( trimmedUnsafe ) && isLocalhost ( window . location . href ) ) { return void cb ( true ) ; }
2021-06-15 18:35:17 +08:00
// if both the main and sandbox domains are onion addresses
// then the HTTPS requirement is unnecessary
if ( isOnion ( trimmedUnsafe ) && isOnion ( trimmedSafe ) ) { return void cb ( true ) ; }
// otherwise expect that both inner and outer domains use HTTPS
msg . appendChild ( h ( 'span' , [
"Both " ,
code ( 'httpUnsafeOrigin' ) ,
' and ' ,
code ( 'httpSafeOrigin' ) ,
' should be accessed via HTTPS for production use. ' ,
"This can be configured via " ,
CONFIG _PATH ( ) ,
'. ' ,
RESTART _WARNING ( ) ,
] ) ) ;
2022-02-18 15:56:42 +08:00
cb ( isHTTPS ( trimmedUnsafe ) && isHTTPS ( trimmedSafe ) || debugOrigins ) ;
2021-10-19 16:52:06 +08:00
} ) ;
2022-02-03 17:31:29 +08:00
assert ( function ( cb , msg ) {
2022-02-10 18:59:48 +08:00
msg . appendChild ( h ( 'span' , [
2022-02-10 15:30:16 +08:00
code ( '/api/config' ) ,
" returned an HTTP status code other than " ,
code ( '200' ) ,
' when accessed from the sandbox domain.' ,
] ) ) ;
2022-02-03 17:31:29 +08:00
deferredPostMessage ( {
command : 'CHECK_HTTP_STATUS' ,
content : {
url : cacheBuster ( '/api/config' ) ,
} ,
} , function ( content ) {
cb ( content === 200 || content ) ;
} ) ;
} ) ;
2022-02-14 14:15:44 +08:00
assert ( function ( cb , msg ) {
msg . appendChild ( h ( 'span' , [
"An invalid " ,
code ( "fileHost" ) ,
" value was provided by " ,
code ( '/api/config' ) ,
'.' ,
] ) ) ;
// it's OK not to provide a 'fileHost' value
if ( typeof ( fileHost ) === 'undefined' ) { return void cb ( true ) ; }
// if one is provided, we expect it to be HTTPS
if ( ! isHTTPS ( fileHost ) ) { return void cb ( fileHost ) ; }
// Otherwise I guess it's OK?
cb ( true ) ;
} ) ;
assert ( function ( cb , msg ) {
msg . appendChild ( h ( 'span' , [
'This instance is configured to use an invalid websocket URL.' ,
] ) ) ;
if ( ! API _URL ) { return void cb ( 'INVALID_WEBSOCKET' ) ; }
if ( isHTTPS ( trimmedUnsafe ) && API _URL . protocol !== 'wss:' ) {
return void cb ( "PROTOCOL_MISMATCH" ) ;
}
return void cb ( true ) ;
} ) ;
2022-02-10 15:30:16 +08:00
/ *
assert ( function ( cb , msg ) {
msg . appendChild ( h ( 'span' , [
'all headers' ,
] ) ) ;
Tools . common _xhr ( '/' , function ( xhr ) {
var all _headers = xhr . getAllResponseHeaders ( ) . split ( /\r|\n/ ) . filter ( Boolean ) ;
cb ( all _headers ) ;
} ) ;
} ) ;
* /
2022-12-16 18:22:30 +08:00
var parseResponseHeaders = xhr => {
var H = { } ;
xhr . getAllResponseHeaders ( )
. split ( /\r|\n/ )
. filter ( Boolean )
. forEach ( line => {
line . replace ( /([^:]+):(.*)/ , ( all , key , value ) => {
2022-12-16 18:28:19 +08:00
H [ key ] = value . trim ( ) ;
2022-12-16 18:22:30 +08:00
} ) ;
} ) ;
return H ;
} ;
2022-02-18 15:56:42 +08:00
var CSP _DESCRIPTIONS = {
'default-src' : '' ,
'style-src' : '' ,
'font-src' : '' ,
'child-src' : '' ,
'frame-src' : '' ,
'script-src' : '' ,
'connect-src' : "This rule restricts which URLs can be loaded by scripts. Overly permissive settings can allow users to be tracking using external resources, while overly restrictive settings may block pages from loading entirely." ,
'img-src' : '' ,
'media-src' : '' ,
'worker-src' : '' ,
'manifest-src' : '' ,
'frame-ancestors' : ' This rule determines which sites can embed content from this instance in an iframe.' ,
} ;
2022-02-10 16:17:22 +08:00
var validateCSP = function ( raw , msg , expected ) {
2022-02-10 15:30:16 +08:00
var CSP = parseCSP ( raw ) ;
var checkRule = function ( attr , rules ) {
2022-02-10 16:17:22 +08:00
var v = CSP [ attr ] ;
2022-02-10 15:30:16 +08:00
// return `true` if you fail this test...
2022-02-10 16:17:22 +08:00
if ( typeof ( v ) !== 'string' || ! v ) { return true ; }
2022-02-10 15:30:16 +08:00
var l = rules . length ;
for ( var i = 0 ; i < l ; i ++ ) {
2022-02-10 16:41:14 +08:00
if ( typeof ( rules [ i ] ) !== 'undefined' && ! v . includes ( rules [ i ] ) ) { return true ; }
2022-02-10 16:17:22 +08:00
v = v . replace ( rules [ i ] , '' ) ;
2022-02-10 15:30:16 +08:00
}
2022-02-10 16:17:22 +08:00
return v . trim ( ) ;
2022-02-10 15:30:16 +08:00
} ;
2022-02-18 15:56:42 +08:00
var failed ;
Object . keys ( expected ) . forEach ( function ( dir ) {
2022-02-10 15:30:16 +08:00
var result = checkRule ( dir , expected [ dir ] ) ;
2022-02-18 15:56:42 +08:00
if ( ! failed && result ) { failed = true ; }
if ( ! result ) { return ; }
msg . appendChild ( h ( 'p' , [
'A value of ' ,
code ( '"' + expected [ dir ] . filter ( Boolean ) . join ( ' ' ) + '"' ) ,
' was expected for the ' ,
code ( dir ) ,
' directive.' ,
CSP _DESCRIPTIONS [ dir ]
] ) ) ;
/ *
console . log ( 'BAD_HEADER:' , {
rule : dir ,
expected : expected [ dir ] ,
result : result ,
} ) ;
* /
} ) ;
2022-02-10 16:41:14 +08:00
2022-02-18 15:56:42 +08:00
if ( failed ) { return parseCSP ( raw ) ; }
2022-02-10 15:30:16 +08:00
return true ;
} ;
assert ( function ( _cb , msg ) {
var url = '/sheet/inner.html' ;
var cb = Util . once ( Util . mkAsync ( _cb ) ) ;
2022-02-10 16:17:22 +08:00
msg . appendChild ( h ( 'span' , [
code ( trimmedUnsafe + url ) ,
' was served with incorrect ' ,
code ( 'Content-Security-Policy' ) ,
' headers.' ,
] ) ) ;
//msg.appendChild(CSP_WARNING(url));
2022-02-10 15:30:16 +08:00
deferredPostMessage ( {
command : 'GET_HEADER' ,
content : {
url : url ,
header : 'content-security-policy' ,
} ,
} , function ( raw ) {
var $outer = trimmedUnsafe ;
var $sandbox = trimmedSafe ;
2022-02-10 16:17:22 +08:00
var result = validateCSP ( raw , msg , {
2022-02-10 15:30:16 +08:00
'default-src' : [ "'none'" ] ,
'style-src' : [ "'unsafe-inline'" , "'self'" , $outer ] ,
'font-src' : [ "'self'" , 'data:' , $outer ] ,
2022-02-18 15:56:42 +08:00
'child-src' : [ $outer ] ,
'frame-src' : [ "'self'" , 'blob:' , $sandbox ] ,
2022-02-10 15:30:16 +08:00
'script-src' : [ "'self'" , 'resource:' , $outer ,
2022-02-10 18:59:48 +08:00
"'unsafe-eval'" ,
"'unsafe-inline'" ,
2022-02-10 15:30:16 +08:00
] ,
'connect-src' : [
"'self'" ,
'blob:' ,
$outer ,
$sandbox ,
2022-02-14 14:15:44 +08:00
API _URL . origin ,
isHTTPS ( fileHost ) ? fileHost : undefined ,
2022-02-14 14:50:00 +08:00
// support for cryptpad.fr configuration
2022-05-11 15:42:12 +08:00
accounts _api ,
2022-02-14 14:50:00 +08:00
! [ trimmedUnsafe , trimmedSafe ] . includes ( ACCOUNTS _URL ) ? ACCOUNTS _URL : undefined ,
2022-02-10 15:30:16 +08:00
] ,
'img-src' : [ "'self'" , 'data:' , 'blob:' , $outer ] ,
'media-src' : [ 'blob:' ] ,
2022-04-04 15:01:40 +08:00
'frame-ancestors' : ApiConfig . enableEmbedding ? [ "'self'" , window . location . protocol , 'vector:' ] : [ "'self'" , $outer ] ,
2022-02-18 16:24:33 +08:00
'worker-src' : [ "'self'" ] ,
2022-02-10 15:30:16 +08:00
} ) ;
cb ( result ) ;
} ) ;
} ) ;
assert ( function ( cb , msg ) {
var header = 'content-security-policy' ;
msg . appendChild ( h ( 'span' , [
2022-02-10 16:17:22 +08:00
code ( trimmedUnsafe + '/' ) ,
' was served with incorrect ' ,
code ( 'Content-Security-Policy' ) ,
' headers.' ,
2022-02-10 15:30:16 +08:00
] ) ) ;
Tools . common _xhr ( '/' , function ( xhr ) {
var raw = xhr . getResponseHeader ( header ) ;
var $outer = trimmedUnsafe ;
var $sandbox = trimmedSafe ;
2022-02-10 16:17:22 +08:00
var result = validateCSP ( raw , msg , {
2022-02-10 15:30:16 +08:00
'default-src' : [ "'none'" ] ,
'style-src' : [ "'unsafe-inline'" , "'self'" , $outer ] ,
'font-src' : [ "'self'" , 'data:' , $outer ] ,
2022-02-18 15:56:42 +08:00
'child-src' : [ $outer ] ,
'frame-src' : [ "'self'" , 'blob:' , $sandbox ] ,
2022-02-10 15:30:16 +08:00
'script-src' : [ "'self'" , 'resource:' , $outer ] ,
'connect-src' : [
"'self'" ,
'blob:' ,
$outer ,
$sandbox ,
2022-02-14 14:15:44 +08:00
API _URL . origin ,
isHTTPS ( fileHost ) ? fileHost : undefined ,
2022-05-11 15:42:12 +08:00
accounts _api ,
2022-02-14 15:08:54 +08:00
! [ trimmedUnsafe , trimmedSafe ] . includes ( ACCOUNTS _URL ) ? ACCOUNTS _URL : undefined ,
2022-02-10 15:30:16 +08:00
] ,
'img-src' : [ "'self'" , 'data:' , 'blob:' , $outer ] ,
'media-src' : [ 'blob:' ] ,
2022-04-04 15:01:40 +08:00
'frame-ancestors' : ApiConfig . enableEmbedding ? [ "'self'" , window . location . protocol , 'vector:' ] : [ "'self'" , $outer ] ,
2022-02-10 17:20:15 +08:00
'worker-src' : [ "'self'" ] , //, $outer, $sandbox],
2022-02-10 15:30:16 +08:00
} ) ;
cb ( result ) ;
} ) ;
} ) ;
2022-02-18 18:43:47 +08:00
/ * O n l y t w o u s e - c a s e s a r e c u r r e n t l y s u p p o r t e d :
1. remote embedding is enabled , and fully permissive
2. remote embedding is disabled , so media - tags can only be loaded on your instance
Support for selectively enabling embedding on remote sites is far more complicated
and will need funding .
* /
2022-03-15 16:05:49 +08:00
var checkAllowedOrigins = function ( raw , url , msg , cb ) {
2022-02-10 18:59:48 +08:00
var header = 'Access-Control-Allow-Origin' ;
2022-03-15 16:05:49 +08:00
var expected ;
2022-03-24 15:13:16 +08:00
if ( ! ApiConfig . enableEmbedding ) {
2022-03-15 16:05:49 +08:00
expected = trimmedSafe ;
msg . appendChild ( h ( 'span' , [
2022-03-24 15:13:16 +08:00
'This instance has not been configured to enable support for embedding assets and documents in third-party websites. ' ,
'In order for this setting to be effective while still permitting encrypted media to load locally the ' ,
2022-03-15 16:05:49 +08:00
code ( header ) ,
' should only match trusted domains.' ,
' Under most circumstances it is sufficient to permit only the sandbox domain to load assets.' ,
" Remote embedding can be enabled via the admin panel." ,
] ) ) ;
} else {
expected = '*' ;
2022-02-18 18:39:02 +08:00
msg . appendChild ( h ( 'span' , [
2022-03-15 16:05:49 +08:00
"This instance has been configured to permit embedding assets and documents in third-party websites." ,
2022-03-24 15:13:16 +08:00
'In order for this setting to be effective, assets must be served with an ' ,
2022-02-18 18:39:02 +08:00
code ( header ) ,
' header with a value of ' ,
code ( "'*'" ) ,
2022-03-24 15:13:16 +08:00
'. Remote embedding can be disabled via the admin panel.' ,
2022-02-18 18:39:02 +08:00
] ) ) ;
2022-03-15 16:05:49 +08:00
}
if ( raw === expected ) { return void cb ( true ) ; }
cb ( {
url : url ,
response : raw ,
2022-03-24 15:13:16 +08:00
enableEmbedding : ApiConfig . enableEmbedding ,
2022-03-15 16:05:49 +08:00
} ) ;
} ;
2022-02-18 18:39:02 +08:00
2022-03-15 16:05:49 +08:00
assert ( function ( cb , msg ) {
var header = 'Access-Control-Allow-Origin' ;
var url = new URL ( '/' , trimmedUnsafe ) . href ;
Tools . common _xhr ( url , function ( xhr ) {
var raw = xhr . getResponseHeader ( header ) ;
checkAllowedOrigins ( raw , url , msg , cb ) ;
2022-02-10 15:30:16 +08:00
} ) ;
} ) ;
assert ( function ( cb , msg ) {
2022-02-10 18:59:48 +08:00
var header = 'Cross-Origin-Embedder-Policy' ;
2022-02-10 15:30:16 +08:00
msg . appendChild ( h ( 'span' , [
2022-02-10 18:59:48 +08:00
"Assets must be served with a " ,
code ( header ) ,
' value of ' ,
code ( 'require-corp' ) ,
" to enable browser features required for client-side document conversion." ,
2022-02-10 15:30:16 +08:00
] ) ) ;
Tools . common _xhr ( '/' , function ( xhr ) {
var raw = xhr . getResponseHeader ( header ) ;
2022-02-10 18:59:48 +08:00
cb ( raw === 'require-corp' || raw ) ;
2022-02-10 15:30:16 +08:00
} ) ;
} ) ;
assert ( function ( cb , msg ) {
2022-02-10 18:59:48 +08:00
var header = 'Cross-Origin-Resource-Policy' ;
2022-02-10 15:30:16 +08:00
msg . appendChild ( h ( 'span' , [
2022-02-10 18:59:48 +08:00
"Assets must be served with a " ,
code ( header ) ,
' value of ' ,
code ( 'cross-origin' ) ,
" to enable browser features required for client-side document conversion." ,
2022-02-10 15:30:16 +08:00
] ) ) ;
Tools . common _xhr ( '/' , function ( xhr ) {
var raw = xhr . getResponseHeader ( header ) ;
2022-02-10 18:59:48 +08:00
cb ( raw === 'cross-origin' || raw ) ;
2022-02-10 15:30:16 +08:00
} ) ;
} ) ;
assert ( function ( cb , msg ) {
var header = 'X-Content-Type-Options' ;
msg . appendChild ( h ( 'span' , [
2022-02-10 18:59:48 +08:00
"Assets should be served with an " ,
code ( header ) ,
' header with a value of ' ,
code ( 'nosniff' ) ,
'.' ,
2022-02-10 15:30:16 +08:00
] ) ) ;
Tools . common _xhr ( '/' , function ( xhr ) {
var raw = xhr . getResponseHeader ( header ) ;
2022-02-10 18:59:48 +08:00
cb ( raw === 'nosniff' || raw ) ;
2022-02-10 15:30:16 +08:00
} ) ;
} ) ;
assert ( function ( cb , msg ) {
var header = 'Cache-Control' ;
msg . appendChild ( h ( 'span' , [
2022-02-10 15:42:07 +08:00
'Assets requested without a version parameter should be served with a ' ,
code ( 'no-cache' ) ,
' value for the ' ,
code ( "Cache-Control" ) ,
' header.' ,
2022-02-10 15:30:16 +08:00
] ) ) ;
// Cache-Control should be 'no-cache' unless the URL includes ver=
Tools . common _xhr ( '/' , function ( xhr ) {
var raw = xhr . getResponseHeader ( header ) ;
2022-02-10 18:59:48 +08:00
cb ( raw === 'no-cache' || raw ) ;
2022-02-10 15:30:16 +08:00
} ) ;
} ) ;
assert ( function ( cb , msg ) {
var header = 'Cache-Control' ;
msg . appendChild ( h ( 'span' , [
2022-02-10 15:42:07 +08:00
'Assets requested with a version parameter should be served with a long-lived ' ,
code ( 'Cache-Control' ) ,
' header.' ,
2022-02-10 15:30:16 +08:00
] ) ) ;
// Cache-Control should be 'max-age=<number>' if the URL includes 'ver='
2022-02-10 15:54:45 +08:00
Tools . common _xhr ( '/customize/messages.js?ver=' + ( + new Date ( ) ) , function ( xhr ) {
2022-02-10 15:30:16 +08:00
var raw = xhr . getResponseHeader ( header ) ;
2022-02-10 18:59:48 +08:00
cb ( /max\-age=\d+$/ . test ( raw ) || raw ) ;
2022-02-10 15:30:16 +08:00
} ) ;
} ) ;
2022-03-10 14:03:41 +08:00
var POLICY _ADVISORY = " This link will be included in the home page footer and 'About CryptPad' menu. It's advised that you either provide one or disable registration." ;
2022-07-26 18:59:08 +08:00
var APPCONFIG _DOCS _LINK = function ( key , href ) {
2022-03-10 14:03:41 +08:00
return h ( 'span' , [
" See " ,
h ( 'a' , {
2022-09-09 19:57:09 +08:00
href : href || 'https://docs.cryptpad.org/en/admin_guide/customization.html#application-config' ,
2022-03-10 14:03:41 +08:00
target : "_blank" ,
rel : 'noopener noreferrer' ,
} , "the relevant documentation" ) ,
" about how to customize CryptPad's " ,
code ( key ) ,
' value.' ,
] ) ;
} ;
2022-07-26 18:59:08 +08:00
var TERMS _DOCS _LINK = function ( key ) {
2022-09-09 19:57:09 +08:00
return APPCONFIG _DOCS _LINK ( key , 'https://docs.cryptpad.org/en/admin_guide/customization.html#links-to-terms-of-service-privacy-policy-and-imprint-pages' ) ;
2022-07-26 18:59:08 +08:00
} ;
2022-02-24 18:07:05 +08:00
var isValidInfoURL = function ( url ) {
if ( ! url || typeof ( url ) !== 'string' ) { return false ; }
try {
var parsed = new URL ( url , ApiConfig . httpUnsafeOrigin ) ;
// check that the URL parsed and that they haven't simply linked to
// '/' or '.' or something silly like that.
return ! [
ApiConfig . httpUnsafeOrigin ,
ApiConfig . httpUnsafeOrigin + '/' ,
] . includes ( parsed . href ) ;
} catch ( err ) {
return false ;
}
} ;
2022-03-10 14:03:41 +08:00
// check if they provide terms of service
2022-02-24 18:07:05 +08:00
assert ( function ( cb , msg ) {
if ( ApiConfig . restrictRegistration ) { return void cb ( true ) ; }
var url = Pages . customURLs . terms ;
setWarningClass ( msg ) ;
msg . appendChild ( h ( 'span' , [
2022-03-10 14:03:41 +08:00
'No terms of service were specified.' ,
2022-02-24 18:07:05 +08:00
POLICY _ADVISORY ,
2022-07-26 18:59:08 +08:00
TERMS _DOCS _LINK ( 'terms' ) ,
2022-02-24 18:07:05 +08:00
] ) ) ;
2022-03-10 14:03:41 +08:00
cb ( isValidInfoURL ( url ) || url ) ;
2022-02-24 18:07:05 +08:00
} ) ;
2022-03-10 14:03:41 +08:00
// check if they provide legal data
2022-02-24 18:07:05 +08:00
assert ( function ( cb , msg ) {
2022-03-11 16:19:34 +08:00
if ( true ) { return void cb ( true ) ; } // XXX stubbed while we determine whether this is necessary
2022-02-24 18:07:05 +08:00
if ( ApiConfig . restrictRegistration ) { return void cb ( true ) ; }
var url = Pages . customURLs . imprint ;
setWarningClass ( msg ) ;
msg . appendChild ( h ( 'span' , [
2022-03-10 14:03:41 +08:00
'No legal entity data was specified.' ,
2022-02-24 18:07:05 +08:00
POLICY _ADVISORY ,
2022-07-26 18:59:08 +08:00
TERMS _DOCS _LINK ( 'imprint' ) ,
2022-02-24 18:07:05 +08:00
] ) ) ;
2022-03-10 14:03:41 +08:00
cb ( isValidInfoURL ( url ) || url ) ;
2022-02-24 18:07:05 +08:00
} ) ;
2022-03-10 14:03:41 +08:00
// check if they provide a privacy policy
2022-02-24 18:07:05 +08:00
assert ( function ( cb , msg ) {
if ( ApiConfig . restrictRegistration ) { return void cb ( true ) ; }
var url = Pages . customURLs . privacy ;
setWarningClass ( msg ) ;
msg . appendChild ( h ( 'span' , [
2022-03-10 14:03:41 +08:00
'No privacy policy was specified.' ,
2022-02-24 18:07:05 +08:00
POLICY _ADVISORY ,
2022-07-26 18:59:08 +08:00
TERMS _DOCS _LINK ( 'privacy' ) ,
2022-02-24 18:07:05 +08:00
] ) ) ;
2022-03-10 14:03:41 +08:00
cb ( isValidInfoURL ( url ) || url ) ;
2022-02-24 18:07:05 +08:00
} ) ;
2022-03-10 14:03:41 +08:00
// check if they provide a link to source code
2022-02-24 18:07:05 +08:00
assert ( function ( cb , msg ) {
if ( ApiConfig . restrictRegistration ) { return void cb ( true ) ; }
var url = Pages . customURLs . source ;
setWarningClass ( msg ) ;
msg . appendChild ( h ( 'span' , [
2022-03-10 14:03:41 +08:00
'No source code link was specified.' ,
2022-02-24 18:07:05 +08:00
POLICY _ADVISORY ,
2022-03-10 14:03:41 +08:00
APPCONFIG _DOCS _LINK ( 'source' ) ,
] ) ) ;
cb ( isValidInfoURL ( url ) || url ) ;
} ) ;
2022-03-08 20:50:11 +08:00
2022-03-23 16:05:17 +08:00
assert ( function ( cb , msg ) {
var fullPath = blobPlaceholderPath ;
2022-03-08 20:50:11 +08:00
msg . appendChild ( h ( 'span' , [
"A placeholder file was expected to be available at " ,
code ( fullPath ) ,
", but it was not found." ,
" This commonly indicates a mismatch between the API server's " ,
code ( 'blobPath' ) ,
" value and the path that the webserver or reverse proxy is attempting to serve." ,
" This misconfiguration will cause errors with uploaded files and CryptPad's office editors (sheet, presentation, document)." ,
2022-02-24 18:07:05 +08:00
] ) ) ;
2022-03-08 20:50:11 +08:00
Tools . common _xhr ( fullPath , xhr => {
cb ( xhr . status === 200 || xhr . status ) ;
} ) ;
} ) ;
assert ( function ( cb , msg ) {
2022-03-23 16:05:17 +08:00
var fullPath = blockPlaceholderPath ;
2022-03-08 20:50:11 +08:00
msg . appendChild ( h ( 'span' , [
"A placeholder file was expected to be available at " ,
code ( fullPath ) ,
", but it was not found." ,
" This commonly indicates a mismatch between the API server's " ,
code ( 'blockPath' ) ,
" value and the path that the webserver or reverse proxy is attempting to serve." ,
" This misconfiguration will cause errors with login, registration, and password change." ,
] ) ) ;
Tools . common _xhr ( fullPath , xhr => {
cb ( xhr . status === 200 || xhr . status ) ;
} ) ;
2022-02-24 18:07:05 +08:00
} ) ;
2022-03-14 19:39:22 +08:00
assert ( function ( cb , msg ) {
var url ;
try {
url = new URL ( '/' , trimmedUnsafe ) ;
} catch ( err ) {
2022-03-15 16:05:49 +08:00
// if your configuration is bad enough that this throws
// then other tests should detect it. Let's just bail out
return void cb ( true ) ;
2022-03-14 19:39:22 +08:00
}
2022-03-15 16:05:49 +08:00
// xhr.getResponseHeader and similar APIs don't behave as expected in insecure cross-origin contexts
// which prevents us from inspecting headers in a development context. We bail out early
// and assume it passed. The proper test will run as normal in production
if ( url . protocol !== 'https' ) { return void cb ( true ) ; }
2022-03-14 19:39:22 +08:00
var header = 'Access-Control-Allow-Origin' ;
deferredPostMessage ( {
command : 'GET_HEADER' ,
content : {
url : url . href ,
header : header ,
} ,
} , function ( raw ) {
2022-03-15 16:05:49 +08:00
checkAllowedOrigins ( raw , url . href , msg , cb ) ;
2022-03-14 19:39:22 +08:00
} ) ;
} ) ;
2022-03-22 16:57:07 +08:00
assert ( function ( cb , msg ) {
msg . appendChild ( h ( 'span' , [
"The CryptPad development team recommends running at least NodeJS " ,
code ( "v16.14.2" ) ,
". Which can be installed and updated via " ,
h ( 'a' , {
href : 'https://github.com/nvm-sh/nvm' ,
rel : 'noopener noreferer' ,
target : '_blank' ,
} , 'NVM' ) ,
'.' ,
] ) ) ;
cb ( ! ApiConfig . shouldUpdateNode ) ;
} ) ;
2022-03-23 16:05:17 +08:00
assert ( function ( cb , msg ) {
var header = 'X-Content-Type-Options' ;
msg . appendChild ( h ( 'span' , [
"Content served from the " ,
code ( '/blob/' ) ,
" directory is expected to have a " ,
code ( header ) ,
" header with a value of " ,
code ( 'nosniff' ) ,
'.' ,
] ) ) ;
Tools . common _xhr ( blobPlaceholderPath , xhr => {
var xcto = xhr . getResponseHeader ( 'x-content-type-options' ) ;
cb ( xcto === 'nosniff' || {
path : blobPlaceholderPath ,
value : xcto ,
} ) ;
} ) ;
} ) ;
assert ( function ( cb , msg ) {
var header = 'X-Content-Type-Options' ;
msg . appendChild ( h ( 'span' , [
"Content served from the " ,
code ( '/block/' ) ,
" directory is expected to have a " ,
code ( header ) ,
" header with a value of " ,
code ( 'nosniff' ) ,
'.' ,
] ) ) ;
Tools . common _xhr ( blockPlaceholderPath , xhr => {
var xcto = xhr . getResponseHeader ( 'x-content-type-options' ) ;
cb ( xcto === 'nosniff' || {
path : blockPlaceholderPath ,
value : xcto ,
} ) ;
} ) ;
} ) ;
2022-05-03 20:50:18 +08:00
assert ( function ( cb , msg ) {
var url = '/api/instance' ;
msg . appendChild ( h ( 'span' , [
link ( url , url ) ,
" did not load as expected. This is most likely caused by a missing directive in your reverse proxy or an outdated version of the API server." ,
] ) ) ;
require ( [
` optional! ${ url } ` ,
] , function ( Instance ) {
// if the URL fails to load then an empty object will be returned
// this can be interpreted as a failure, even though the rest of the platform should still work
if ( ! Object . keys ( Instance ) . length ) {
return void cb ( Instance ) ;
}
cb ( true ) ;
} ) ;
} ) ;
2022-05-10 15:43:02 +08:00
assert ( function ( cb , msg ) {
if ( ! ApiConfig . listMyInstance ) { return void cb ( true ) ; }
msg . appendChild ( h ( 'span' , [
"The administrators of this instance have opted in to inclusion in " ,
link ( 'https://cryptpad.org/instances/' , 'the public instance directory' ) ,
' but have not configured at least one of the expected ' ,
code ( 'description' ) ,
' or ' ,
code ( 'location' ) ,
' text fields via the instance admin panel.' ,
] ) ) ;
var expected = [
'description' ,
'location' ,
//'name',
// 'notice',
] ;
var url = '/api/instance' ;
require ( [
` optional! ${ url } ` ,
] , function ( Instance ) {
var good = expected . every ( function ( k ) {
var val = Instance [ k ] ;
return ( val && typeof ( val ) === 'object' && typeof ( val . default ) === 'string' && val . default . trim ( ) ) ;
} ) ;
return void cb ( good || Instance ) ;
} ) ;
} ) ;
2022-09-05 22:09:03 +08:00
[
'/' ,
2022-09-06 15:54:44 +08:00
'/index.html' ,
'/contact.html' ,
2022-09-05 22:09:03 +08:00
'/code/' ,
'/pad/index.html' ,
] . forEach ( url => {
2022-09-08 17:35:15 +08:00
assert ( function ( cb , msg ) {
2022-09-05 22:09:03 +08:00
try {
url = new URL ( url , ApiConfig . httpUnsafeOrigin ) . href ;
} catch ( err ) {
console . error ( err ) ;
}
2022-09-01 16:37:30 +08:00
2022-09-05 22:09:03 +08:00
Tools . common _xhr ( url , xhr => {
xhr . done ( res => {
var dom = new DOMParser ( ) . parseFromString ( res , 'text/html' ) ;
var sels = [
'og:url' ,
'og:type' ,
'og:title' ,
'og:description' ,
'og:image' ,
'twitter:card' ,
] ;
var missing = [ ] ;
sels . forEach ( sel => {
var selector = ` meta[property=" ${ sel } "] ` ;
var el = dom . querySelector ( selector ) ;
if ( ! el ) { missing . push ( selector ) ; }
} ) ;
if ( ! missing . length ) { return void cb ( true ) ; }
setWarningClass ( msg ) ;
msg . appendChild ( h ( 'span' , [
h ( 'p' , [
link ( url , url ) ,
' is missing several attributes which provide better previews on social media sites and messengers. ' ,
2022-09-06 15:54:44 +08:00
"The administrator of this instance can generate them with " , code ( 'npm run build' ) , '.' ,
2022-09-05 22:09:03 +08:00
] ) ,
h ( 'p' , "Missing attributes: " ) ,
h ( 'ul' , missing . map ( q => h ( 'li' , h ( 'code' , q ) ) ) ) ,
] ) ) ;
cb ( false ) ;
2022-09-01 16:37:30 +08:00
} ) ;
} ) ;
} ) ;
} ) ;
2022-09-22 16:41:22 +08:00
assert ( function ( cb , msg ) {
// public instances are expected to be open for registration
// if this is not a public instance, pass this test immediately
if ( ! ApiConfig . listMyInstance ) { return cb ( true ) ; }
// if it's public but registration is not registricted, that's also a pass
if ( ! ApiConfig . restrictRegistration ) { return void cb ( true ) ; }
setWarningClass ( msg ) ;
msg . appendChild ( h ( 'span' , [
"The administrators of this instance have opted in to inclusion in " ,
link ( 'https://cryptpad.org/instances/' , 'the public instance directory' ) ,
' but have disabled registration, which is expected to be open.' ,
h ( 'br' ) ,
h ( 'br' ) ,
" Registration can be reopened using the instance's admin panel." ,
] ) ) ;
cb ( false ) ;
} ) ;
2022-10-05 17:38:49 +08:00
var compareCustomized = function ( a , b , cb ) {
var getText = ( url , done ) => {
Tools . common _xhr ( url , xhr => {
xhr . done ( done ) ;
} ) ;
} ;
var A , B ;
nThen ( w => {
getText ( a , w ( res => {
A = res ;
} ) ) ;
getText ( b , w ( res => {
B = res ;
} ) ) ;
} ) . nThen ( ( ) => {
cb ( void 0 , A === B ) ;
} ) ;
} ;
var CUSTOMIZATIONS = [ ] ;
// check whether some important pages have been customized
assert ( function ( cb /*, msg */ ) {
nThen ( function ( w ) {
// add whatever custom pages you want here
[
'application_config.js' ,
'pages.js' ,
2023-01-11 15:41:12 +08:00
'pages/index.js' ,
2022-10-05 17:38:49 +08:00
] . forEach ( resource => {
// sort this above errors and warnings and style in a neutral color.
var A = ` /customize.dist/ ${ resource } ` ;
var B = ` /customize/ ${ resource } ` ;
compareCustomized ( A , B , w ( ( err , same ) => {
if ( err || same ) { return ; }
CUSTOMIZATIONS . push ( resource ) ;
} ) ) ;
} ) ;
} ) . nThen ( function ( ) {
// Implementing these checks as a test was an easy way to ensure that
// they completed before the final report was shown. It's intentional
// that this always passes
cb ( true ) ;
} ) ;
} ) ;
2022-02-18 15:56:42 +08:00
var serverToken ;
2022-10-05 17:38:49 +08:00
assert ( function ( cb , msg ) {
Tools . common _xhr ( '/' , function ( xhr ) {
serverToken = xhr . getResponseHeader ( 'server' ) ;
msg . appendChild ( h ( 'span' , [
` Due to its use of ` ,
h ( 'em' , ` CloudFlare ` ) ,
` this instance may be inaccessible by users of the Tor network, and generally less secure because of the additional point of failure where code can be intercepted and modified by bad actors. ` ,
] ) ) ;
//if (1) { return void cb(false || {serverToken}); }
cb ( ! /cloudflare/i . test ( serverToken ) || {
serverToken ,
} ) ;
} ) ;
2021-06-23 10:24:28 +08:00
} ) ;
2022-12-16 18:22:30 +08:00
assert ( function ( cb , msg ) {
// provide an exception for development instances
if ( isLocalhost ( trimmedUnsafe ) && isLocalhost ( window . location . href ) ) {
return void cb ( true ) ;
}
msg . appendChild ( h ( 'span' , [
'This instance is not configured to require HTTP Strict Transport Security (HSTS) - which instructs clients to only interact with it over a secure connection.' ,
] ) ) ;
Tools . common _xhr ( '/' , function ( xhr ) {
var H = parseResponseHeaders ( xhr ) ;
var HSTS = H [ 'strict-transport-security' ] ;
// check for a numerical value of max-age
2022-12-17 00:44:02 +08:00
if ( /max\-age=\d+/ . test ( HSTS ) ) {
2022-12-16 18:22:30 +08:00
return void cb ( true ) ;
}
// else call back with the value
cb ( HSTS ) ;
} ) ;
} ) ;
2021-02-24 16:23:59 +08:00
var row = function ( cells ) {
return h ( 'tr' , cells . map ( function ( cell ) {
return h ( 'td' , cell ) ;
} ) ) ;
} ;
var failureReport = function ( obj ) {
2021-06-17 11:39:04 +08:00
var printableValue = obj . output ;
try {
2021-08-02 20:06:51 +08:00
printableValue = JSON . stringify ( obj . output , null , ' ' ) ;
2021-06-17 11:39:04 +08:00
} catch ( err ) {
console . error ( err ) ;
}
2022-10-14 20:47:19 +08:00
return h ( ` div.error.cp-test-status. ${ obj . type } ` , [
2021-02-24 16:23:59 +08:00
h ( 'h5' , obj . message ) ,
2021-08-02 20:35:46 +08:00
h ( 'div.table-container' ,
h ( 'table' , [
2022-10-05 17:38:49 +08:00
row ( [ "Test number" , obj . test + 1 ] ) ,
2021-08-02 20:35:46 +08:00
row ( [ "Returned value" , h ( 'pre' , code ( printableValue ) ) ] ) ,
2021-08-03 14:45:30 +08:00
] )
2021-08-02 20:35:46 +08:00
) ,
2021-02-24 16:23:59 +08:00
] ) ;
} ;
2021-02-23 16:53:34 +08:00
2021-03-02 23:45:52 +08:00
var completed = 0 ;
var $progress = $ ( '#cp-progress' ) ;
2021-05-05 14:38:20 +08:00
var versionStatement = function ( ) {
2021-08-13 18:22:14 +08:00
return h ( 'p.cp-notice-version' , [
2021-05-05 14:38:20 +08:00
"This instance is running " ,
h ( 'span.cp-app-checkup-version' , [
"CryptPad" ,
' ' ,
Pages . versionString ,
] ) ,
'.' ,
] ) ;
} ;
2021-08-02 20:06:51 +08:00
var browserStatement = function ( ) {
var name = Tools . guessBrowser ( ) ;
if ( ! name ) { return ; }
return h ( 'p.cp-notice-browser' , [
"You appear to be using a " ,
h ( 'span.cp-app-checkup-browser' , name ) ,
2021-08-10 22:18:15 +08:00
' browser on ' ,
h ( 'span.underline' , Tools . guessOS ( ) ) ,
' to view this page.' ,
2021-08-02 20:06:51 +08:00
] ) ;
} ;
2022-02-18 15:56:42 +08:00
var serverStatement = function ( token ) {
if ( [ null , undefined ] . includes ( token ) ) { return undefined ; }
return h ( 'p.cp-notice-other' , [
"Page content was served by " ,
code ( '"' + token + '"' ) ,
'.' ,
] ) ;
} ;
2021-04-16 22:00:12 +08:00
Assert . run ( function ( state ) {
2022-10-05 17:38:49 +08:00
var isWarning = function ( x ) {
return x && /cp\-warning/ . test ( x . getAttribute ( 'class' ) ) ;
} ;
var isInfo = x => x && /cp\-info/ . test ( x . getAttribute ( 'class' ) ) ;
var errors = state . errors ; // TODO anomalies might be better?
var categories = {
error : 0 ,
info : 0 ,
warning : 0 ,
} ;
errors . forEach ( obj => {
if ( isWarning ( obj . message ) ) {
obj . type = 'warning' ;
} else if ( isInfo ( obj . message ) ) {
obj . type = 'info' ;
state . passed ++ ;
} else {
obj . type = 'error' ;
}
Util . inc ( categories , obj . type ) ;
} ) ;
2021-02-23 16:53:34 +08:00
var failed = errors . length ;
Messages . assert _numberOfTestsPassed = "{0} / {1} tests passed." ;
2022-10-05 17:38:49 +08:00
var statusClass ;
if ( categories . error !== 0 ) {
statusClass = 'failure' ;
} else if ( categories . warning !== 0 ) {
statusClass = 'failure' ;
} else if ( categories . info !== 0 ) {
statusClass = 'neutral' ;
} else {
statusClass = 'success' ;
}
2021-02-24 16:23:59 +08:00
2021-04-19 21:09:00 +08:00
var failedDetails = "Details found below" ;
2021-05-05 14:38:20 +08:00
var successDetails = "This checkup only tests the most common configuration issues. You may still experience errors or incorrect behaviour." ;
2021-08-02 20:06:51 +08:00
var details = h ( 'p.cp-notice-details' , failed ? failedDetails : successDetails ) ;
2021-04-19 21:09:00 +08:00
2022-10-05 17:38:49 +08:00
var sortMethod = function ( a , b ) {
if ( a . type === 'info' && b . type !== 'info' ) {
return 1 ;
}
if ( a . type === 'warning' && b . type !== 'warning' ) {
return 1 ;
}
return a . test - b . test ;
} ;
var customizations ;
if ( CUSTOMIZATIONS . length ) {
customizations = h ( 'div.cp-notice-customizations' , [
h ( 'p' , ` The following assets have been customized for this instance: ` ) ,
h ( 'ul' , CUSTOMIZATIONS . map ( asset => {
var href = ` /customize/ ${ asset } ` ;
return h ( 'li' , [
h ( 'a' , {
2023-01-11 15:41:12 +08:00
href : ` ${ href } ? ${ + new Date ( ) } ` ,
2022-10-05 17:38:49 +08:00
target : '_blank' ,
} , href ) ,
] ) ;
} ) ) ,
h ( 'p' , ` Unexpected behaviour could be related to these changes. If you are this instance's administrator, please try temporarily disabling them before submitting a bug report. ` ) ,
] ) ;
}
2021-02-24 16:23:59 +08:00
var summary = h ( 'div.summary.' + statusClass , [
2021-05-05 14:38:20 +08:00
versionStatement ( ) ,
2022-02-18 15:56:42 +08:00
serverStatement ( serverToken ) ,
2021-08-02 20:06:51 +08:00
browserStatement ( ) ,
2022-10-05 17:38:49 +08:00
customizations ,
2021-02-24 16:23:59 +08:00
h ( 'p' , Messages . _getKey ( 'assert_numberOfTestsPassed' , [
2021-02-23 16:53:34 +08:00
state . passed ,
state . total
2021-02-24 16:23:59 +08:00
] ) ) ,
2021-04-19 21:09:00 +08:00
details ,
2021-02-24 16:23:59 +08:00
] ) ;
var report = h ( 'div.report' , [
summary ,
2022-03-10 14:03:41 +08:00
h ( 'div.failures' , errors . sort ( sortMethod ) . map ( failureReport ) ) ,
2021-02-24 16:23:59 +08:00
] ) ;
2021-03-02 23:45:52 +08:00
$progress . remove ( ) ;
2021-02-24 16:23:59 +08:00
$ ( 'body' ) . prepend ( report ) ;
2021-05-27 16:47:32 +08:00
try {
console . log ( 'closing shared websocket' ) ;
shared _websocket . close ( ) ;
} catch ( err ) { console . error ( err ) ; }
try {
console . log ( 'closing shared realtime' ) ;
shared _realtime . network . disconnect ( ) ;
} catch ( err ) { console . error ( err ) ; }
2021-03-02 23:45:52 +08:00
} , function ( i , total ) {
console . log ( 'test ' + i + ' completed' ) ;
completed ++ ;
Messages . assert _numberOfTestsCompleted = "{0} / {1} tests completed." ;
$progress . html ( '' ) . append ( h ( 'div.report.pending.summary' , [
2021-05-05 14:38:20 +08:00
versionStatement ( ) ,
2021-03-02 23:45:52 +08:00
h ( 'p' , [
h ( 'i.fa.fa-spinner.fa-pulse' ) ,
h ( 'span' , Messages . _getKey ( 'assert_numberOfTestsCompleted' , [ completed , total ] ) )
] )
] ) ) ;
2021-02-23 16:53:34 +08:00
} ) ;
} ) ;