@ -90,6 +90,12 @@ define([
console . error ( err ) ;
console . error ( err ) ;
}
}
var debugOrigins = {
httpUnsafeOrigin : trimmedUnsafe ,
httpSafeOrigin : trimmedSafe ,
currentOrigin : window . location . origin ,
} ;
assert ( function ( cb , msg ) {
assert ( function ( cb , msg ) {
msg . appendChild ( h ( 'span' , [
msg . appendChild ( h ( 'span' , [
"CryptPad's sandbox requires that both " ,
"CryptPad's sandbox requires that both " ,
@ -103,7 +109,7 @@ define([
] ) ) ;
] ) ) ;
//console.error(trimmedSafe, trimmedUnsafe);
//console.error(trimmedSafe, trimmedUnsafe);
cb ( Boolean ( trimmedSafe && trimmedUnsafe ) ) ;
cb ( Boolean ( trimmedSafe && trimmedUnsafe ) || debugOrigins ) ;
} ) ;
} ) ;
assert ( function ( cb , msg ) {
assert ( function ( cb , msg ) {
@ -119,7 +125,7 @@ define([
RESTART _WARNING ( ) ,
RESTART _WARNING ( ) ,
] ) ) ;
] ) ) ;
return void cb ( trimmedSafe !== trimmedUnsafe ) ;
return void cb ( trimmedSafe !== trimmedUnsafe || trimmedUnsafe ) ;
} ) ;
} ) ;
assert ( function ( cb , msg ) {
assert ( function ( cb , msg ) {
@ -132,7 +138,10 @@ define([
'. ' ,
'. ' ,
RESTART _WARNING ( ) ,
RESTART _WARNING ( ) ,
] ) ) ;
] ) ) ;
cb ( trimmedSafe === ApiConfig . httpSafeOrigin && trimmedUnsafe === ApiConfig . httpUnsafeOrigin ) ;
var result = trimmedSafe === ApiConfig . httpSafeOrigin &&
trimmedUnsafe === ApiConfig . httpUnsafeOrigin ;
cb ( result || debugOrigins ) ;
} ) ;
} ) ;
assert ( function ( cb , msg ) {
assert ( function ( cb , msg ) {
@ -149,7 +158,7 @@ define([
'.' ,
'.' ,
] ) ) ;
] ) ) ;
var origin = window . location . origin ;
var origin = window . location . origin ;
return void cb ( ApiConfig . httpUnsafeOrigin === origin ) ;
return void cb ( ApiConfig . httpUnsafeOrigin === origin || debugOrigins ) ;
} ) ;
} ) ;
var checkAvailability = function ( url , cb ) {
var checkAvailability = function ( url , cb ) {
@ -157,7 +166,7 @@ define([
url : cacheBuster ( url ) ,
url : cacheBuster ( url ) ,
data : { } ,
data : { } ,
complete : function ( xhr ) {
complete : function ( xhr ) {
cb ( xhr . status === 200 ) ;
cb ( xhr . status === 200 || xhr . status ) ;
} ,
} ,
} ) ;
} ) ;
} ;
} ;
@ -200,8 +209,8 @@ define([
} ) . nThen ( function ( waitFor ) {
} ) . nThen ( function ( waitFor ) {
to = setTimeout ( function ( ) {
to = setTimeout ( function ( ) {
console . error ( 'TIMEOUT loading iframe on the safe domain' ) ;
console . error ( 'TIMEOUT loading iframe on the safe domain' ) ;
cb ( false ) ;
cb ( 'TIMEOUT' ) ;
} , 5 000) ;
} , 10 000) ;
SFCommonO . initIframe ( waitFor ) ;
SFCommonO . initIframe ( waitFor ) ;
} ) . nThen ( function ( ) {
} ) . nThen ( function ( ) {
// Iframe is loaded
// Iframe is loaded
@ -230,7 +239,7 @@ define([
console . error ( 'Websocket TIMEOUT' ) ;
console . error ( 'Websocket TIMEOUT' ) ;
evWSError . fire ( ) ;
evWSError . fire ( ) ;
cb ( timeoutErr ) ;
cb ( timeoutErr ) ;
} , 5 000) ;
} , 10 000) ;
ws . onopen = function ( ) {
ws . onopen = function ( ) {
clearTimeout ( to ) ;
clearTimeout ( to ) ;
cb ( true ) ;
cb ( true ) ;
@ -389,9 +398,8 @@ define([
'cross-origin-embedder-policy' : 'require-corp' ,
'cross-origin-embedder-policy' : 'require-corp' ,
} ;
} ;
$ . ajax ( url , {
Tools . common _xhr ( sheetURL , function ( xhr ) {
complete : function ( xhr ) {
var result = ! Object . keys ( expect ) . some ( function ( k ) {
cb ( ! Object . keys ( expect ) . some ( function ( k ) {
var response = xhr . getResponseHeader ( k ) ;
var response = xhr . getResponseHeader ( k ) ;
if ( response !== expect [ k ] ) {
if ( response !== expect [ k ] ) {
msg . appendChild ( h ( 'span' , [
msg . appendChild ( h ( 'span' , [
@ -405,8 +413,8 @@ define([
] ) ) ;
] ) ) ;
return true ; // returning true indicates that a value is incorrect
return true ; // returning true indicates that a value is incorrect
}
}
} ) ) ;
} ) ;
} ,
cb ( result || xhr . getAllResponseHeaders ( ) ) ;
} ) ;
} ) ;
} ) ;
} ) ;
@ -433,14 +441,12 @@ define([
] ) ) ;
] ) ) ;
} ;
} ;
$ . ajax ( '/?' + ( + new Date ( ) ) , {
Tools . common _xhr ( '/' , function ( xhr ) {
complete : function ( xhr ) {
var header = xhr . getResponseHeader ( 'permissions-policy' ) || '' ;
var header = xhr . getResponseHeader ( 'permissions-policy' ) || '' ;
var rules = header . split ( ',' ) ;
var rules = header . split ( ',' ) ;
if ( rules . includes ( 'interest-cohort=()' ) ) { return void cb ( true ) ; }
if ( rules . includes ( 'interest-cohort=()' ) ) { return void cb ( true ) ; }
printMessage ( JSON . stringify ( header ) ) ;
printMessage ( JSON . stringify ( header ) ) ;
cb ( header ) ;
cb ( header ) ;
} ,
} ) ;
} ) ;
} ) ;
} ) ;
@ -452,18 +458,14 @@ define([
"Your browser console may provide more details as to why this resource could not be loaded. " ,
"Your browser console may provide more details as to why this resource could not be loaded. " ,
] ) ) ;
] ) ) ;
$ . ajax ( cacheBuster ( '/api/broadcast' ) , {
Tools . common _xhr ( '/api/broadcast' , function ( xhr ) {
dataType : 'text' ,
var status = xhr . status ;
complete : function ( xhr ) {
cb ( status === 200 || status ) ;
cb ( xhr . status === 200 ) ;
} ,
} ) ;
} ) ;
} ) ;
} ) ;
var checkAPIHeaders = function ( url , msg , cb ) {
var checkAPIHeaders = function ( url , msg , cb ) {
$ . ajax ( cacheBuster ( url ) , {
Tools . common _xhr ( url , function ( xhr ) {
dataType : 'text' ,
complete : function ( xhr ) {
var allHeaders = xhr . getAllResponseHeaders ( ) ;
var allHeaders = xhr . getAllResponseHeaders ( ) ;
var headers = { } ;
var headers = { } ;
var duplicated = allHeaders . split ( '\n' ) . some ( function ( header ) {
var duplicated = allHeaders . split ( '\n' ) . some ( function ( header ) {
@ -487,7 +489,7 @@ define([
Object . keys ( expect ) . forEach ( function ( k ) {
Object . keys ( expect ) . forEach ( function ( k ) {
var response = xhr . getResponseHeader ( k ) ;
var response = xhr . getResponseHeader ( k ) ;
var expected = expect [ k ] ;
var expected = expect [ k ] ;
if ( response !== expected ) {
if ( response === expected ) { return ; }
incorrect = true ;
incorrect = true ;
msg . appendChild ( h ( 'p' , [
msg . appendChild ( h ( 'p' , [
'The ' ,
'The ' ,
@ -500,13 +502,8 @@ define([
code ( expected ) ,
code ( expected ) ,
"' as expected." ,
"' as expected." ,
] ) ) ;
] ) ) ;
}
} ) ;
} ) ;
cb ( ( ! duplicated && ! incorrect ) || allHeaders ) ;
if ( duplicated || incorrect ) { console . debug ( allHeaders ) ; }
cb ( ! duplicated && ! incorrect ) ;
} ,
} ) ;
} ) ;
} ;
} ;
@ -625,6 +622,7 @@ define([
} ) ;
} ) ;
var parseCSP = function ( CSP ) {
var parseCSP = function ( CSP ) {
if ( ! CSP ) { return { } ; }
//console.error(CSP);
//console.error(CSP);
var CSP _headers = { } ;
var CSP _headers = { } ;
CSP . split ( ";" )
CSP . split ( ";" )
@ -683,7 +681,7 @@ define([
} ,
} ,
} , function ( content ) {
} , function ( content ) {
var CSP _headers = parseCSP ( content ) ;
var CSP _headers = parseCSP ( content ) ;
cb ( hasOnlyOfficeHeaders ( CSP _headers ) ) ;
cb ( hasOnlyOfficeHeaders ( CSP _headers ) || CSP _headers ) ;
} ) ;
} ) ;
} ) ;
} ) ;
@ -698,7 +696,7 @@ define([
} ,
} ,
} , function ( content ) {
} , function ( content ) {
var CSP _headers = parseCSP ( content ) ;
var CSP _headers = parseCSP ( content ) ;
cb ( hasOnlyOfficeHeaders ( CSP _headers ) ) ;
cb ( hasOnlyOfficeHeaders ( CSP _headers ) || CSP _headers ) ;
} ) ;
} ) ;
} ) ;
} ) ;
@ -817,31 +815,7 @@ define([
'. ' ,
'. ' ,
RESTART _WARNING ( ) ,
RESTART _WARNING ( ) ,
] ) ) ;
] ) ) ;
cb ( isHTTPS ( trimmedUnsafe ) && isHTTPS ( trimmedSafe ) ) ;
cb ( isHTTPS ( trimmedUnsafe ) && isHTTPS ( trimmedSafe ) || debugOrigins ) ;
} ) ;
assert ( function ( cb , msg ) { // FIXME this test has been superceded, but the descriptive text is still useful
// check that the sandbox domain is included in connect-src
msg . appendChild ( h ( 'span' , [
"This instance's " ,
code ( "Content-Security-Policy" ) ,
" headers do not include the sandboxed domain (" ,
code ( trimmedSafe ) ,
") in " ,
code ( "connect-src" ) ,
". This can cause problems with fonts when printing office documents." ,
" This is probably due to an incorrectly configured reverse proxy." ,
" See the provided NGINX configuration file for an example of how to set this header correctly." ,
] ) ) ;
Tools . common _xhr ( '/' , function ( xhr ) {
var CSP = parseCSP ( xhr . getResponseHeader ( 'content-security-policy' ) ) ;
var connect = ( CSP && CSP [ 'connect-src' ] ) || "" ;
if ( connect . includes ( trimmedSafe ) ) {
return void cb ( true ) ;
}
cb ( CSP ) ;
} ) ;
} ) ;
} ) ;
assert ( function ( cb , msg ) {
assert ( function ( cb , msg ) {
@ -904,6 +878,22 @@ define([
} ) ;
} ) ;
* /
* /
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.' ,
} ;
var validateCSP = function ( raw , msg , expected ) {
var validateCSP = function ( raw , msg , expected ) {
var CSP = parseCSP ( raw ) ;
var CSP = parseCSP ( raw ) ;
var checkRule = function ( attr , rules ) {
var checkRule = function ( attr , rules ) {
@ -917,28 +907,30 @@ define([
}
}
return v . trim ( ) ;
return v . trim ( ) ;
} ;
} ;
if ( Object . keys ( expected ) . some ( function ( dir ) {
var failed ;
Object . keys ( expected ) . forEach ( function ( dir ) {
var result = checkRule ( dir , expected [ dir ] ) ;
var result = checkRule ( dir , expected [ dir ] ) ;
if ( result ) {
if ( ! failed && result ) { failed = true ; }
if ( ! result ) { return ; }
msg . appendChild ( h ( 'p' , [
msg . appendChild ( h ( 'p' , [
'A value of ' ,
'A value of ' ,
code ( '"' + expected [ dir ] . filter ( Boolean ) . join ( ' ' ) + '"' ) ,
code ( '"' + expected [ dir ] . filter ( Boolean ) . join ( ' ' ) + '"' ) ,
' was expected for the ' ,
' was expected for the ' ,
code ( dir ) ,
code ( dir ) ,
' directive.' ,
' directive.' ,
CSP _DESCRIPTIONS [ dir ]
] ) ) ;
] ) ) ;
/ *
console . log ( 'BAD_HEADER:' , {
console . log ( 'BAD_HEADER:' , {
rule : dir ,
rule : dir ,
expected : expected [ dir ] ,
expected : expected [ dir ] ,
result : result ,
result : result ,
} ) ;
} ) ;
}
* /
} ) ;
if ( failed ) { return parseCSP ( raw ) ; }
return result ;
} ) ) {
return parseCSP ( raw ) ;
}
return true ;
return true ;
} ;
} ;
@ -966,8 +958,8 @@ define([
'default-src' : [ "'none'" ] ,
'default-src' : [ "'none'" ] ,
'style-src' : [ "'unsafe-inline'" , "'self'" , $outer ] ,
'style-src' : [ "'unsafe-inline'" , "'self'" , $outer ] ,
'font-src' : [ "'self'" , 'data:' , $outer ] ,
'font-src' : [ "'self'" , 'data:' , $outer ] ,
'child-src' : [ $outer ] , //["'self'", 'blob:', $outer, $sandbox],
'child-src' : [ $outer ] ,
'frame-src' : [ "'self'" , 'blob:' , /*$outer, */ $sandbox ] ,
'frame-src' : [ "'self'" , 'blob:' , $sandbox ] ,
'script-src' : [ "'self'" , 'resource:' , $outer ,
'script-src' : [ "'self'" , 'resource:' , $outer ,
"'unsafe-eval'" ,
"'unsafe-eval'" ,
"'unsafe-inline'" ,
"'unsafe-inline'" ,
@ -1009,8 +1001,8 @@ define([
'default-src' : [ "'none'" ] ,
'default-src' : [ "'none'" ] ,
'style-src' : [ "'unsafe-inline'" , "'self'" , $outer ] ,
'style-src' : [ "'unsafe-inline'" , "'self'" , $outer ] ,
'font-src' : [ "'self'" , 'data:' , $outer ] ,
'font-src' : [ "'self'" , 'data:' , $outer ] ,
'child-src' : [ $outer ] , //["'self'", 'blob:', $outer, $sandbox],
'child-src' : [ $outer ] ,
'frame-src' : [ "'self'" , 'blob:' , /*$outer,*/ $sandbox ] ,
'frame-src' : [ "'self'" , 'blob:' , $sandbox ] ,
'script-src' : [ "'self'" , 'resource:' , $outer ] ,
'script-src' : [ "'self'" , 'resource:' , $outer ] ,
'connect-src' : [
'connect-src' : [
"'self'" ,
"'self'" ,
@ -1122,54 +1114,10 @@ define([
} ) ;
} ) ;
} ) ;
} ) ;
/ *
var serverToken ;
assert ( function ( cb , msg ) {
Tools . common _xhr ( '/' , function ( xhr ) {
setWarningClass ( msg ) ;
serverToken = xhr . getResponseHeader ( 'server' ) ;
$ . ajax ( cacheBuster ( '/' ) , {
dataType : 'text' ,
complete : function ( xhr ) {
var serverToken = xhr . getResponseHeader ( 'server' ) ;
if ( serverToken === null ) { return void cb ( true ) ; }
var lowered = ( serverToken || '' ) . toLowerCase ( ) ;
var family ;
[ 'Apache' , 'Caddy' , 'NGINX' ] . some ( function ( pattern ) {
if ( lowered . indexOf ( pattern . toLowerCase ( ) ) !== - 1 ) {
family = pattern ;
return true ;
}
} ) ;
var text = [
"This instance is set to respond with an HTTP " ,
code ( "server" ) ,
" header. This information can make it easier for attackers to find and exploit known vulnerabilities. " ,
] ;
if ( family === 'NGINX' ) { // FIXME incorrect instructions for HTTP2. needs a recompile?
msg . appendChild ( h ( 'span' , text . concat ( [
"This can be addressed by setting " ,
code ( "server_tokens off" ) ,
" in your global NGINX config."
] ) ) ) ;
return void cb ( serverToken ) ;
}
// handle other
msg . appendChild ( h ( 'span' , text . concat ( [
"In this case, it appears that the host server is running " ,
code ( serverToken ) ,
" instead of " ,
code ( "NGINX" ) ,
" as recommended. As such, you may not benefit from the latest security enhancements that are tested and maintained by the CryptPad development team." ,
] ) ) ) ;
cb ( serverToken ) ;
}
} ) ;
} ) ;
} ) ;
* /
var row = function ( cells ) {
var row = function ( cells ) {
return h ( 'tr' , cells . map ( function ( cell ) {
return h ( 'tr' , cells . map ( function ( cell ) {
@ -1223,6 +1171,15 @@ define([
] ) ;
] ) ;
} ;
} ;
var serverStatement = function ( token ) {
if ( [ null , undefined ] . includes ( token ) ) { return undefined ; }
return h ( 'p.cp-notice-other' , [
"Page content was served by " ,
code ( '"' + token + '"' ) ,
'.' ,
] ) ;
} ;
Assert . run ( function ( state ) {
Assert . run ( function ( state ) {
var errors = state . errors ;
var errors = state . errors ;
var failed = errors . length ;
var failed = errors . length ;
@ -1237,6 +1194,7 @@ define([
var summary = h ( 'div.summary.' + statusClass , [
var summary = h ( 'div.summary.' + statusClass , [
versionStatement ( ) ,
versionStatement ( ) ,
serverStatement ( serverToken ) ,
browserStatement ( ) ,
browserStatement ( ) ,
h ( 'p' , Messages . _getKey ( 'assert_numberOfTestsPassed' , [
h ( 'p' , Messages . _getKey ( 'assert_numberOfTestsPassed' , [
state . passed ,
state . passed ,