diff --git a/lib/defaults.js b/lib/defaults.js index 44cfef5f0..663d4bd02 100644 --- a/lib/defaults.js +++ b/lib/defaults.js @@ -1,8 +1,8 @@ var Default = module.exports; Default.commonCSP = function (Env) { - domain = ' ' + Env.httpUnsafeOrigin; - sandbox = Env.httpSafeOrigin; + var domain = ' ' + Env.httpUnsafeOrigin; + var sandbox = Env.httpSafeOrigin; sandbox = (sandbox && sandbox !== domain? ' ' + sandbox: ''); // Content-Security-Policy @@ -39,11 +39,11 @@ Default.commonCSP = function (Env) { }; Default.contentSecurity = function (Env) { - return (Default.commonCSP(Env).join('; ') + "script-src 'self' resource: " + domain).replace(/\s+/g, ' '); + return (Default.commonCSP(Env).join('; ') + "script-src 'self' resource: " + Env.httpUnsafeOrigin).replace(/\s+/g, ' '); }; Default.padContentSecurity = function (Env) { - return (Default.commonCSP(Env).join('; ') + "script-src 'self' 'unsafe-eval' 'unsafe-inline' resource: " + domain).replace(/\s+/g, ' '); + return (Default.commonCSP(Env).join('; ') + "script-src 'self' 'unsafe-eval' 'unsafe-inline' resource: " + Env.httpUnsafeOrigin).replace(/\s+/g, ' '); }; Default.httpHeaders = function (Env) { diff --git a/lib/env.js b/lib/env.js index 5d2855e92..4e718e0c2 100644 --- a/lib/env.js +++ b/lib/env.js @@ -21,14 +21,42 @@ var isValidPort = function (p) { return typeof(p) === 'number' && p < 65535; }; +var deriveSandboxOrigin = function (unsafe, port) { + var url = new URL(unsafe); + url.port = port; + return url.origin; +}; + module.exports.create = function (config) { + var httpUnsafeOrigin = canonicalizeOrigin(config.httpUnsafeOrigin); + + var httpSafeOrigin; + var NO_SANDBOX = false; + var httpSafePort; + var httpPort = isValidPort(config.httpPort)? config.httpPort: 3000; + + if (typeof(config.httpSafeOrigin) !== 'string') { + NO_SANDBOX = true; + if (typeof(config.httpSafePort) !== 'number') { httpSafePort = httpPort + 1; } + httpSafeOrigin = deriveSandboxOrigin(httpUnsafeOrigin, httpSafePort); + } + + var permittedEmbedders = config.permittedEmbedders; + if (typeof(permittedEmbedders) === 'string') { + permittedEmbedders = permittedEmbedders.trim(); + } + const Env = { + fileHost: config.fileHost, // XXX + NO_SANDBOX: NO_SANDBOX, + httpSafePort: httpSafePort, + version: Package.version, installMethod: config.installMethod || undefined, - httpUnsafeOrigin: canonicalizeOrigin(config.httpUnsafeOrigin), - httpSafeOrigin: canonicalizeOrigin(config.httpSafeOrigin), - permittedEmbedders: typeof(config.permittedEmbedders) === 'string'? config.permittedEmbedders: canonicalizeOrigin(config.httpSafeOrigin), + httpUnsafeOrigin: httpUnsafeOrigin, + httpSafeOrigin: httpSafeOrigin, + permittedEmbedders: typeof(permittedEmbedders) === 'string' && permittedEmbedders? permittedEmbedders: httpSafeOrigin, removeDonateButton: config.removeDonateButton, httpPort: isValidPort(config.httpPort)? config.httpPort: 3000, diff --git a/server.js b/server.js index 3e6da369f..73e4d2b00 100644 --- a/server.js +++ b/server.js @@ -23,29 +23,17 @@ var fancyURL = function (domain, path) { return false; }; -var deriveSandboxOrigin = function (unsafe, port) { - var url = new URL(unsafe); - url.port = port; - return url.origin; -}; - (function () { // you absolutely must provide an 'httpUnsafeOrigin' (a truthy string) - if (!Env.httpUnsafeOrigin || typeof(Env.httpUnsafeOrigin) !== 'string') { + if (typeof(Env.httpUnsafeOrigin) !== 'string' || !Env.httpUnsafeOrigin.trim()) { throw new Error("No 'httpUnsafeOrigin' provided"); } - - if (typeof(Env.httpSafeOrigin) !== 'string') { - Env.NO_SANDBOX = true; - if (typeof(Env.httpSafePort) !== 'number') { - Env.httpSafePort = Env.httpPort + 1; - } - Env.httpSafeOrigin = deriveSandboxOrigin(Env.httpUnsafeOrigin, Env.httpSafePort); - } }()); var applyHeaderMap = function (res, map) { - for (let header in map) { res.setHeader(header, map[header]); } + for (let header in map) { + if (typeof(map[header]) === 'string') { res.setHeader(header, map[header]); } + } }; var EXEMPT = [ @@ -54,6 +42,11 @@ var EXEMPT = [ /^\/unsafeiframe\/inner\.html.*$/, ]; +var cacheHeaders = function (Env, key, headers) { + if (Env.DEV_MODE) { return; } + Env[key] = headers; +}; + var getHeaders = function (Env, type) { var key = type + 'HeadersCache'; if (Env[key]) { return Env[key]; } @@ -83,12 +76,12 @@ var getHeaders = function (Env, type) { // because they aren't necessary and they cause problems // when duplicated by NGINX in production environments if (type === 'api') { - Env[key] = headers; + cacheHeaders(Env, key, headers); return headers; } headers["Cross-Origin-Resource-Policy"] = 'cross-origin'; - Env[key] = headers; + cacheHeaders(Env, key, headers); return headers; }; @@ -103,6 +96,7 @@ var setHeaders = function (req, res) { } var h = getHeaders(Env, type); + //console.log('PEWPEW', type, h); applyHeaderMap(res, h); }; @@ -140,7 +134,7 @@ app.use(function (req, res, next) { if (req.method === 'OPTIONS' && /\/blob\//.test(req.url)) { res.setHeader('Access-Control-Allow-Origin', Env.disableEmbedding? Env.permittedEmbedders: '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS'); - res.setHeader('Access-Control-Allow-Headers', 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range'); + res.setHeader('Access-Control-Allow-Headers', 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Access-Control-Allow-Origin'); res.setHeader('Access-Control-Max-Age', 1728000); res.setHeader('Content-Type', 'application/octet-stream; charset=utf-8'); res.setHeader('Content-Length', 0); @@ -241,6 +235,7 @@ var serveConfig = makeRouteCache(function (host) { restrictRegistration: Env.restrictRegistration, httpSafeOrigin: Env.httpSafeOrigin, disableEmbedding: Env.disableEmbedding, + fileHost: Env.fileHost, }, null, '\t'), '});' ].join(';\n') diff --git a/www/checkup/main.js b/www/checkup/main.js index 5a17eaf2c..039b27183 100644 --- a/www/checkup/main.js +++ b/www/checkup/main.js @@ -1045,7 +1045,10 @@ define([ code(header), ' should only match trusted domains.', ])); - return void cb(raw); + return void cb({ + header: raw, + expected: trimmedSafe, + }); } } @@ -1271,6 +1274,41 @@ define([ }); }); + assert(function (cb, msg) { + var url; + try { + url = new URL('/', trimmedUnsafe); + } catch (err) { + return void cb({ + error: err, + }); + } + + // XXX don't bother checking cors headers in dev environment + if (url.protocol !== 'https') { return void cb(true); } // XXX + + var header = 'Access-Control-Allow-Origin'; + msg.appendChild(h('span', [ + 'pewpew ', + code(header), // XXX + ])); + + deferredPostMessage({ + command: 'GET_HEADER', + content: { + url: url.href, + header: header, + }, + }, function (raw) { + if (raw === '*') { return void cb(true); } + if (raw === trimmedSafe) { return void cb(true); } + cb({ + response: raw, + disableEmbedding: ApiConfig.disableEmbedding, + }); + }); + }); + var serverToken; Tools.common_xhr('/', function (xhr) { serverToken = xhr.getResponseHeader('server');