mirror of
https://github.com/cryptpad/cryptpad.git
synced 2026-09-14 11:05:41 +05:00
support modifying CSP headers at runtime
This commit is contained in:
parent
52529f1a65
commit
b40c81d088
@ -1,7 +1,8 @@
|
||||
var Default = module.exports;
|
||||
|
||||
Default.commonCSP = function (domain, sandbox) {
|
||||
domain = ' ' + domain;
|
||||
Default.commonCSP = function (Env) {
|
||||
domain = ' ' + Env.httpUnsafeOrigin;
|
||||
sandbox = Env.httpSafeOrigin;
|
||||
sandbox = (sandbox && sandbox !== domain? ' ' + sandbox: '');
|
||||
// Content-Security-Policy
|
||||
|
||||
@ -31,25 +32,25 @@ Default.commonCSP = function (domain, sandbox) {
|
||||
"media-src blob:",
|
||||
|
||||
// for accounts.cryptpad.fr authentication and cross-domain iframe sandbox
|
||||
"frame-ancestors *",
|
||||
Env.disableEmbedding? `frame-ancestors ${domain}${sandbox}`: "frame-ancestors *",
|
||||
"worker-src 'self'",
|
||||
""
|
||||
];
|
||||
};
|
||||
|
||||
Default.contentSecurity = function (domain, sandbox) {
|
||||
return (Default.commonCSP(domain, sandbox).join('; ') + "script-src 'self' resource: " + domain).replace(/\s+/g, ' ');
|
||||
Default.contentSecurity = function (Env) {
|
||||
return (Default.commonCSP(Env).join('; ') + "script-src 'self' resource: " + domain).replace(/\s+/g, ' ');
|
||||
};
|
||||
|
||||
Default.padContentSecurity = function (domain, sandbox) {
|
||||
return (Default.commonCSP(domain, sandbox).join('; ') + "script-src 'self' 'unsafe-eval' 'unsafe-inline' resource: " + domain).replace(/\s+/g, ' ');
|
||||
Default.padContentSecurity = function (Env) {
|
||||
return (Default.commonCSP(Env).join('; ') + "script-src 'self' 'unsafe-eval' 'unsafe-inline' resource: " + domain).replace(/\s+/g, ' ');
|
||||
};
|
||||
|
||||
Default.httpHeaders = function () {
|
||||
Default.httpHeaders = function (Env) {
|
||||
return {
|
||||
"X-XSS-Protection": "1; mode=block",
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Origin": Env.disableEmbedding? '': "*",
|
||||
"Permissions-policy":"interest-cohort=()"
|
||||
};
|
||||
};
|
||||
|
||||
10
lib/env.js
10
lib/env.js
@ -39,9 +39,19 @@ module.exports.create = function (config) {
|
||||
DEV_MODE: false,
|
||||
configCache: {},
|
||||
broadcastCache: {},
|
||||
|
||||
officeHeadersCache: undefined,
|
||||
standardHeadersCache: undefined,
|
||||
apiHeadersCache: undefined,
|
||||
|
||||
flushCache: function () {
|
||||
Env.configCache = {};
|
||||
Env.broadcastCache = {};
|
||||
|
||||
Env.officeHeadersCache = undefined;
|
||||
Env.standardHeadersCache = undefined;
|
||||
Env.apiHeadersCache = undefined;
|
||||
|
||||
Env.FRESH_KEY = +new Date();
|
||||
if (!(Env.DEV_MODE || Env.FRESH_MODE)) { Env.FRESH_MODE = true; }
|
||||
if (!Env.Log) { return; }
|
||||
|
||||
97
server.js
97
server.js
@ -48,9 +48,17 @@ var applyHeaderMap = function (res, map) {
|
||||
for (let header in map) { res.setHeader(header, map[header]); }
|
||||
};
|
||||
|
||||
var setHeaders = (function () {
|
||||
// load the default http headers unless the admin has provided their own via the config file
|
||||
var headers;
|
||||
var EXEMPT = [
|
||||
/^\/common\/onlyoffice\/.*\.html.*/,
|
||||
/^\/(sheet|presentation|doc)\/inner\.html.*/,
|
||||
/^\/unsafeiframe\/inner\.html.*$/,
|
||||
];
|
||||
|
||||
var getHeaders = function (Env, type) {
|
||||
var key = type + 'HeadersCache';
|
||||
if (Env[key]) { return Env[key]; }
|
||||
|
||||
var headers = {};
|
||||
|
||||
var custom = config.httpHeaders;
|
||||
// if the admin provided valid http headers then use them
|
||||
@ -58,11 +66,11 @@ var setHeaders = (function () {
|
||||
headers = Util.clone(custom);
|
||||
} else {
|
||||
// otherwise use the default
|
||||
headers = Default.httpHeaders();
|
||||
headers = Default.httpHeaders(Env);
|
||||
}
|
||||
|
||||
// next define the base Content Security Policy (CSP) headers
|
||||
if (typeof(config.contentSecurity) === 'string') {
|
||||
if (typeof(config.contentSecurity) === 'string') { // XXX deprecate this???
|
||||
headers['Content-Security-Policy'] = config.contentSecurity;
|
||||
if (!/;$/.test(headers['Content-Security-Policy'])) { headers['Content-Security-Policy'] += ';' }
|
||||
if (headers['Content-Security-Policy'].indexOf('frame-ancestors') === -1) {
|
||||
@ -73,47 +81,54 @@ var setHeaders = (function () {
|
||||
}
|
||||
} else {
|
||||
// use the default CSP headers constructed with your domain
|
||||
headers['Content-Security-Policy'] = Default.contentSecurity(Env.httpUnsafeOrigin, Env.httpSafeOrigin);
|
||||
headers['Content-Security-Policy'] = Default.contentSecurity(Env);
|
||||
}
|
||||
|
||||
const padHeaders = Util.clone(headers);
|
||||
if (typeof(config.padContentSecurity) === 'string') {
|
||||
padHeaders['Content-Security-Policy'] = config.padContentSecurity;
|
||||
//const padHeaders = Util.clone(headers);
|
||||
if (type === 'office') {
|
||||
if (typeof(config.padContentSecurity) === 'string') {
|
||||
headers['Content-Security-Policy'] = config.padContentSecurity; // XXX drop support for this
|
||||
} else {
|
||||
headers['Content-Security-Policy'] = Default.padContentSecurity(Env);
|
||||
}
|
||||
}
|
||||
/*
|
||||
headers['Content-Security-Policy'] = type === 'office'?
|
||||
Default.padContentSecurity(Env):
|
||||
Default.contentSecurity(Env);*/
|
||||
|
||||
if (Env.NO_SANDBOX) { // handles correct configuration for local development
|
||||
// https://stackoverflow.com/questions/11531121/add-duplicate-http-response-headers-in-nodejs
|
||||
headers["Cross-Origin-Resource-Policy"] = 'cross-origin';
|
||||
headers["Cross-Origin-Embedder-Policy"] = 'require-corp';
|
||||
}
|
||||
|
||||
// Don't set CSP headers on /api/ endpoints
|
||||
// because they aren't necessary and they cause problems
|
||||
// when duplicated by NGINX in production environments
|
||||
if (type === 'api') {
|
||||
Env[key] = headers;
|
||||
return headers;
|
||||
}
|
||||
|
||||
headers["Cross-Origin-Resource-Policy"] = 'cross-origin';
|
||||
Env[key] = headers;
|
||||
return headers;
|
||||
};
|
||||
|
||||
var setHeaders = function (req, res) {
|
||||
var type;
|
||||
if (EXEMPT.some(regex => regex.test(req.url))) {
|
||||
type = 'office';
|
||||
} else if (/^\/api\/(broadcast|config)/.test(req.url)) {
|
||||
type = 'api';
|
||||
} else {
|
||||
padHeaders['Content-Security-Policy'] = Default.padContentSecurity(Env.httpUnsafeOrigin, Env.httpSafeOrigin);
|
||||
type = 'standard';
|
||||
}
|
||||
if (Object.keys(headers).length) {
|
||||
return function (req, res) {
|
||||
if (Env.NO_SANDBOX) { // handles correct configuration for local development
|
||||
// https://stackoverflow.com/questions/11531121/add-duplicate-http-response-headers-in-nodejs
|
||||
applyHeaderMap(res, {
|
||||
"Cross-Origin-Resource-Policy": 'cross-origin',
|
||||
"Cross-Origin-Embedder-Policy": 'require-corp',
|
||||
});
|
||||
}
|
||||
|
||||
// Don't set CSP headers on /api/ endpoints
|
||||
// because they aren't necessary and they cause problems
|
||||
// when duplicated by NGINX in production environments
|
||||
if (/^\/api\/(broadcast|config)/.test(req.url)) { return; }
|
||||
|
||||
applyHeaderMap(res, {
|
||||
"Cross-Origin-Resource-Policy": 'cross-origin',
|
||||
});
|
||||
|
||||
// targeted CSP, generic policies, maybe custom headers
|
||||
const h = [
|
||||
/^\/common\/onlyoffice\/.*\.html.*/,
|
||||
/^\/(sheet|presentation|doc)\/inner\.html.*/,
|
||||
/^\/unsafeiframe\/inner\.html.*$/,
|
||||
].some((regex) => {
|
||||
return regex.test(req.url);
|
||||
}) ? padHeaders : headers;
|
||||
applyHeaderMap(res, h);
|
||||
};
|
||||
}
|
||||
return function () {};
|
||||
}());
|
||||
var h = getHeaders(Env, type);
|
||||
applyHeaderMap(res, h);
|
||||
};
|
||||
|
||||
(function () {
|
||||
if (!config.logFeedback) { return; }
|
||||
|
||||
@ -978,8 +978,8 @@ define([
|
||||
|
||||
'img-src': ["'self'", 'data:', 'blob:', $outer],
|
||||
'media-src': ['blob:'],
|
||||
//'frame-ancestors': ['*'], // XXX IFF you want to support remote embedding
|
||||
'worker-src': ["'self'"], // , $outer, $sandbox],
|
||||
'frame-ancestors': ApiConfig.disableEmbedding? [$outer, $sandbox]: ['*'],
|
||||
'worker-src': ["'self'"],
|
||||
});
|
||||
cb(result);
|
||||
});
|
||||
@ -1016,7 +1016,7 @@ define([
|
||||
],
|
||||
'img-src': ["'self'", 'data:', 'blob:', $outer],
|
||||
'media-src': ['blob:'],
|
||||
//'frame-ancestors': ['*'], // XXX IFF you want to support remote embedding
|
||||
'frame-ancestors': ApiConfig.disableEmbedding? [$outer, $sandbox]: ['*'],
|
||||
'worker-src': ["'self'"],//, $outer, $sandbox],
|
||||
});
|
||||
|
||||
@ -1026,7 +1026,7 @@ define([
|
||||
|
||||
assert(function (cb, msg) {
|
||||
var header = 'Access-Control-Allow-Origin';
|
||||
msg.appendChild(h('span', [
|
||||
msg.appendChild(h('span', [ // XXX update text to indicate that the value doesn't match their preference
|
||||
'Assets must be served with an ',
|
||||
code(header),
|
||||
' header with a value of ',
|
||||
@ -1035,6 +1035,14 @@ define([
|
||||
]));
|
||||
Tools.common_xhr('/', function (xhr) {
|
||||
var raw = xhr.getResponseHeader(header);
|
||||
|
||||
if (ApiConfig.disableEmbedding) {
|
||||
if ([null, ''].includes(raw)) { return void cb(true); }
|
||||
else {
|
||||
return void cb(raw === '*' || raw);
|
||||
}
|
||||
}
|
||||
|
||||
cb(raw === "*" || raw);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user