Merge branch 'testing' into soon

pull/1/head
ansuz 3 years ago
commit 57193b60f9

@ -96,14 +96,14 @@ server {
set $fontSrc "'self' data: ${main_domain}"; set $fontSrc "'self' data: ${main_domain}";
# images can be loaded from anywhere, though we'd like to deprecate this as it allows the use of images for tracking # images can be loaded from anywhere, though we'd like to deprecate this as it allows the use of images for tracking
set $imgSrc "'self' data: * blob: ${main_domain}"; set $imgSrc "'self' data: blob: ${main_domain} ${sandbox_domain}";
# frame-src specifies valid sources for nested browsing contexts. # frame-src specifies valid sources for nested browsing contexts.
# this prevents loading any iframes from anywhere other than the sandbox domain # this prevents loading any iframes from anywhere other than the sandbox domain
set $frameSrc "'self' ${sandbox_domain} blob:"; set $frameSrc "'self' ${sandbox_domain} blob:";
# specifies valid sources for loading media using video or audio # specifies valid sources for loading media using video or audio
set $mediaSrc "'self' data: * blob: ${main_domain}"; set $mediaSrc "'self' data: blob: ${main_domain} ${sandbox_domain}";
# defines valid sources for webworkers and nested browser contexts # defines valid sources for webworkers and nested browser contexts
# deprecated in favour of worker-src and frame-src # deprecated in favour of worker-src and frame-src

@ -2,6 +2,7 @@ var Default = module.exports;
Default.commonCSP = function (domain, sandbox) { Default.commonCSP = function (domain, sandbox) {
domain = ' ' + domain; domain = ' ' + domain;
sandbox = (sandbox && sandbox !== domain? ' ' + sandbox: '');
// Content-Security-Policy // Content-Security-Policy
return [ return [
@ -15,19 +16,19 @@ Default.commonCSP = function (domain, sandbox) {
* it is recommended that you configure these fields to match the * it is recommended that you configure these fields to match the
* domain which will serve your CryptPad instance. * domain which will serve your CryptPad instance.
*/ */
"child-src blob: *", "child-src 'self' blob: " + domain + sandbox,
// IE/Edge // IE/Edge
"frame-src blob: *", "frame-src 'self' blob: " + domain + sandbox,
/* this allows connections over secure or insecure websockets /* this allows connections over secure or insecure websockets
if you are deploying to production, you'll probably want to remove if you are deploying to production, you'll probably want to remove
the ws://* directive, and change '*' to your domain the ws://* directive, and change '*' to your domain
*/ */
"connect-src 'self' ws: wss: blob: " + domain + (sandbox && sandbox !== domain? ' ' + sandbox: ''), "connect-src 'self' ws: wss: blob: " + domain + sandbox,
// data: is used by codemirror // data: is used by codemirror
"img-src 'self' data: blob:" + domain, "img-src 'self' data: blob:" + domain,
"media-src * blob:", "media-src blob:",
// for accounts.cryptpad.fr authentication and cross-domain iframe sandbox // for accounts.cryptpad.fr authentication and cross-domain iframe sandbox
"frame-ancestors *", "frame-ancestors *",

@ -1,20 +1,81 @@
define(['/api/config'], function (ApiConfig) { define(['/api/config'], function (ApiConfig) {
/* The 'bounce app' provides a unified way to do the following things in CryptPad
1. remove the 'opener' attribute from the tab/window every time you navigate
2. detect and block malicious URLs after warning the user
3. inform users when they are navigating away from their cryptpad instance
*/
// when a URL is rejected we close the window
var reject = function () {
window.close();
};
// this app is intended to be loaded and used exclusively from the sandbox domain
// where stricter CSP blocks various attacks. Reject any other usage.
if (ApiConfig.httpSafeOrigin !== window.location.origin) { if (ApiConfig.httpSafeOrigin !== window.location.origin) {
window.alert('The bounce application must only be used from the sandbox domain, ' + window.alert('The bounce application must only be used from the sandbox domain, ' +
'please report this issue on https://github.com/xwiki-labs/cryptpad'); 'please report this issue on https://github.com/xwiki-labs/cryptpad');
return; return void reject();
} }
// Old/bad browsers lack the URL API, making it more difficult to validate and compare URLs.
// Warn and reject.
if (typeof(URL) !== 'function') {
window.alert("Your browser does not support functionality this page requires");
return void reject();
}
// remove the 'opener' to prevent 'reverse tabnabbing'.
window.opener = null;
// Parse the outer domain's root URL to facilitate comparisons.
// Reject everything if this fails to parse.
var host;
try {
host = new URL('', ApiConfig.httpUnsafeOrigin);
} catch (err) {
window.alert("This server is configured incorrectly. Details for its administrator can be found on its diagnostics page.");
return void reject();
}
// Decode the target URL that should have been provided through the document's hash.
// Reject if no URL was provided.
// Absolute URLs are easy to handle, other consider URLs relative to the outer domain.
var target;
try {
var bounceTo = decodeURIComponent(window.location.hash.slice(1)); var bounceTo = decodeURIComponent(window.location.hash.slice(1));
if (!bounceTo) { target = new URL(bounceTo, ApiConfig.httpUnsafeOrigin);
} catch (err) {
console.error(err);
window.alert('The bounce application must only be used with a valid href to visit'); window.alert('The bounce application must only be used with a valid href to visit');
return; return void reject();
} }
if (bounceTo.indexOf('javascript:') === 0 || // jshint ignore:line
bounceTo.indexOf('vbscript:') === 0 || // jshint ignore:line // Valid links should navigate to the normalized href
bounceTo.indexOf('data:') === 0) { var go = function () {
window.alert('Illegal bounce URL'); window.location.href = target.href;
return; };
// Local URLs don't require any warning and can navigate directly without user input.
if (target.host === host.host) { return void go(); }
// Everything else requires user input, so we load the platform's translations.
// FIXME: this seems to infer language preferences from the browser instead of the user's account preferences
require([
'/customize/messages.js',
], function (Messages) {
// The provided URL seems to be a malicious or invalid payload.
// Inform the user that we won't navigate and that the 'bounce tab' will be closed.
if (['javascript:', 'vbscript:', 'data:', 'blob:'].includes(target.protocol)) {
window.alert(Messages._getKey('bounce_danger', [target.href]));
return void reject();
} }
window.opener = null;
window.location.href = bounceTo; // The provided URL will navigate the user away from the outer domain.
var question = Messages._getKey('bounce_confirm', [host.hostname, target.href]);
// Confirm that they want to leave, then navigate or reject based on their choice.
var answer = window.confirm(question);
if (answer) { return void go(); }
reject();
});
}); });

@ -859,6 +859,43 @@ define([
}); });
}); });
assert(function (cb, msg) {
var directives = [
'img-src',
'media-src',
'child-src',
'frame-src'
];
msg.appendChild(h('span', [
"This instance's ",
code("Content-Security-Policy"),
" headers are unnecessarily permissive.",
h('br'),
h('br'),
" Review the recommended settings for ",
code('img-src'), ', ',
code('media-src'), ', ',
code('child-src'), ', and ',
code('frame-src'),
" in the provided NGINX configuration file for an example of how to set these headers correctly.",
]));
$.ajax(cacheBuster('/'), {
dataType: 'text',
complete: function (xhr) {
var CSP = parseCSP(xhr.getResponseHeader('content-security-policy'));
// check that the relevant CSP directives are defined
// and that none of them permit general remote content via '*'
if (directives.every(function (k) {
return typeof(CSP[k]) === 'string' && !/ \* /.test(CSP[k]);
})) {
return void cb(true);
}
cb(CSP);
},
});
});
/* /*
assert(function (cb, msg) { assert(function (cb, msg) {
setWarningClass(msg); setWarningClass(msg);

Loading…
Cancel
Save