support modifying CSP headers at runtime

pull/1/head
ansuz 3 years ago
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=()"
};
};

@ -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; }

@ -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);
//const padHeaders = Util.clone(headers);
if (type === 'office') {
if (typeof(config.padContentSecurity) === 'string') {
padHeaders['Content-Security-Policy'] = config.padContentSecurity;
headers['Content-Security-Policy'] = config.padContentSecurity; // XXX drop support for this
} else {
padHeaders['Content-Security-Policy'] = Default.padContentSecurity(Env.httpUnsafeOrigin, Env.httpSafeOrigin);
headers['Content-Security-Policy'] = Default.padContentSecurity(Env);
}
if (Object.keys(headers).length) {
return function (req, res) {
}
/*
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
applyHeaderMap(res, {
"Cross-Origin-Resource-Policy": 'cross-origin',
"Cross-Origin-Embedder-Policy": 'require-corp',
});
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 (/^\/api\/(broadcast|config)/.test(req.url)) { return; }
if (type === 'api') {
Env[key] = headers;
return headers;
}
applyHeaderMap(res, {
"Cross-Origin-Resource-Policy": 'cross-origin',
});
headers["Cross-Origin-Resource-Policy"] = 'cross-origin';
Env[key] = headers;
return headers;
};
// 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);
};
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 {
type = 'standard';
}
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…
Cancel
Save