diff --git a/History.md b/History.md index 39c19a1270..3f0aa261b5 100644 --- a/History.md +++ b/History.md @@ -17,6 +17,9 @@ fixing [#8021](https://github.com/meteor/meteor/issues/8021) and [#7662](https://github.com/meteor/meteor/issues/7662). +* Added support for frame-ancestors CSP option in browser-policy. + [#7970](https://github.com/meteor/meteor/pull/7970) + ## v1.4.2.3 * Style improvements for `meteor create --full`. diff --git a/packages/browser-policy-content/browser-policy-content.js b/packages/browser-policy-content/browser-policy-content.js index 49fb21ff3a..958fd18f7b 100644 --- a/packages/browser-policy-content/browser-policy-content.js +++ b/packages/browser-policy-content/browser-policy-content.js @@ -17,7 +17,7 @@ // disallowEval() // // For each type of content (script, object, image, media, font, connect, -// style), there are the following functions: +// style, frame, frame-ancestors), there are the following functions: // allowOrigin(origin): allows the type of content to be loaded // from the given origin // allowDataUrl(): allows the content to be loaded from data: URLs @@ -248,53 +248,55 @@ _.extend(BrowserPolicy.content, { // allowOrigin, allowData, allowself, and // disallow methods for each type of resource. -_.each(["script", "object", "img", "media", - "font", "connect", "style", "frame"], - function (resource) { - var directive = resource + "-src"; - var methodResource; - if (resource !== "img") { - methodResource = resource.charAt(0).toUpperCase() + - resource.slice(1); - } else { - methodResource = "Image"; - } - var allowMethodName = "allow" + methodResource + "Origin"; - var disallowMethodName = "disallow" + methodResource; - var allowDataMethodName = "allow" + methodResource + "DataUrl"; - var allowBlobMethodName = "allow" + methodResource + "BlobUrl"; - var allowSelfMethodName = "allow" + methodResource + "SameOrigin"; +var resources = [ + { methodResource: "Script", directive: "script-src" }, + { methodResource: "Object", directive: "object-src" }, + { methodResource: "Image", directive: "img-src" }, + { methodResource: "Media", directive: "media-src" }, + { methodResource: "Font", directive: "font-src" }, + { methodResource: "Connect", directive: "connect-src" }, + { methodResource: "Style", directive: "style-src" }, + { methodResource: "Frame", directive: "frame-src" }, + { methodResource: "FrameAncestors", directive: "frame-ancestors" } +]; +_.each(resources,
 function (resource) { + var directive = resource.directive;
 + var methodResource = resource.methodResource;
 + var allowMethodName = "allow" + methodResource + "Origin"; + var disallowMethodName = "disallow" + methodResource; + var allowDataMethodName = "allow" + methodResource + "DataUrl";
 + var allowBlobMethodName = "allow" + methodResource + "BlobUrl"; + var allowSelfMethodName = "allow" + methodResource + "SameOrigin"; - var disallow = function () { - cachedCsp = null; - cspSrcs[directive] = []; - }; - - BrowserPolicy.content[allowMethodName] = function (src) { - prepareForCspDirective(directive); - addSourceForDirective(directive, src); - }; - if (resource === "script") { - BrowserPolicy.content[disallowMethodName] = function () { - disallow(); - setWebAppInlineScripts(false); - }; - } else { - BrowserPolicy.content[disallowMethodName] = disallow; - } - BrowserPolicy.content[allowDataMethodName] = function () { - prepareForCspDirective(directive); - cspSrcs[directive].push("data:"); - }; - BrowserPolicy.content[allowBlobMethodName] = function () { - prepareForCspDirective(directive); - cspSrcs[directive].push("blob:"); - }; - BrowserPolicy.content[allowSelfMethodName] = function () { - prepareForCspDirective(directive); - cspSrcs[directive].push(keywords.self); - }; - }); + var disallow = function () { + cachedCsp = null; + cspSrcs[directive] = []; + }; + BrowserPolicy.content[allowMethodName] = function (src) { + prepareForCspDirective(directive); + addSourceForDirective(directive, src); + }; + if (resource === "script") { + BrowserPolicy.content[disallowMethodName] = function () { + disallow(); + setWebAppInlineScripts(false); + }; + } else { + BrowserPolicy.content[disallowMethodName] = disallow; + } + BrowserPolicy.content[allowDataMethodName] = function () { + prepareForCspDirective(directive); + cspSrcs[directive].push("data:"); + }; + BrowserPolicy.content[allowBlobMethodName] = function () { + prepareForCspDirective(directive); + cspSrcs[directive].push("blob:"); + }; + BrowserPolicy.content[allowSelfMethodName] = function () { + prepareForCspDirective(directive); + cspSrcs[directive].push(keywords.self); + }; +}); setDefaultPolicy(); diff --git a/packages/browser-policy-content/package.js b/packages/browser-policy-content/package.js index 4abffcb51e..a626f9ba88 100644 --- a/packages/browser-policy-content/package.js +++ b/packages/browser-policy-content/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Configure content security policies", - version: "1.0.12" + version: "1.0.13" }); Package.onUse(function (api) { diff --git a/packages/browser-policy/README.md b/packages/browser-policy/README.md index 45c59c9654..53cb941e32 100644 --- a/packages/browser-policy/README.md +++ b/packages/browser-policy/README.md @@ -66,7 +66,10 @@ that are allowed to frame your app. (This is a limitation of the X-Frame-Options header.) Example values of origin include "http://example.com" and "https://foo.example.com". This value of the X-Frame-Options header is not yet supported in Chrome or Safari -and will be ignored in those browsers. +and will be ignored in those browsers. If you need Chrome and/or Safari +support, or need to allow multiple domains to frame your application, +you can use the frame-ancestors CSP option via the +BrowserPolicy.content.allowFrameAncestorsOrigin() function @@ -126,7 +129,7 @@ Disallows inline CSS. Finally, you can configure a whitelist of allowed requests that various types of content can make. The following functions are defined for the content types -script, object, image, media, font, frame, style, and connect. +script, object, image, media, font, frame, frame-ancestors, style, and connect.
diff --git a/packages/browser-policy/browser-policy-test.js b/packages/browser-policy/browser-policy-test.js index 1a2ed512bd..19ca95c292 100644 --- a/packages/browser-policy/browser-policy-test.js +++ b/packages/browser-policy/browser-policy-test.js @@ -136,6 +136,13 @@ Tinytest.add("browser-policy - csp", function (test) { test.isTrue(cspsEqual(BrowserPolicy.content._constructCsp(), "default-src 'none'; frame-src https://foo.com; " + "object-src http://foo.com https://foo.com;")); + + // Check that frame-ancestors property is set correctly.
 + BrowserPolicy.content.allowFrameAncestorsOrigin("https://foo.com/");
 + test.isTrue(cspsEqual(BrowserPolicy.content._constructCsp(),
 + "default-src 'none'; frame-src https://foo.com; " +
 + "object-src http://foo.com https://foo.com; " +
 + "frame-ancestors https://foo.com;")); }); Tinytest.add("browser-policy - x-frame-options", function (test) { diff --git a/packages/browser-policy/package.js b/packages/browser-policy/package.js index 87e01b6e21..bada8728f1 100644 --- a/packages/browser-policy/package.js +++ b/packages/browser-policy/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Configure security policies enforced by the browser", - version: "1.0.9" + version: "1.0.10" }); Package.onUse(function (api) { diff --git a/packages/ddp-server/livedata_server.js b/packages/ddp-server/livedata_server.js index fcd2bf84e3..4a03a57ab1 100644 --- a/packages/ddp-server/livedata_server.js +++ b/packages/ddp-server/livedata_server.js @@ -302,6 +302,9 @@ var Session = function (server, version, socket, options) { }).run(); if (version !== 'pre1' && options.heartbeatInterval !== 0) { + // We no longer need the low level timeout because we have heartbeating. + socket.setWebsocketTimeout(0); + self.heartbeat = new DDPCommon.Heartbeat({ heartbeatInterval: options.heartbeatInterval, heartbeatTimeout: options.heartbeatTimeout, diff --git a/packages/ddp-server/stream_server.js b/packages/ddp-server/stream_server.js index e2d3771b2f..1996d9088c 100644 --- a/packages/ddp-server/stream_server.js +++ b/packages/ddp-server/stream_server.js @@ -86,6 +86,25 @@ StreamServer = function () { self._redirectWebsocketEndpoint(); self.server.on('connection', function (socket) { + // We want to make sure that if a client connects to us and does the initial + // Websocket handshake but never gets to the DDP handshake, that we + // eventually kill the socket. Once the DDP handshake happens, DDP + // heartbeating will work. And before the Websocket handshake, the timeouts + // we set at the server level in webapp_server.js will work. But + // faye-websocket calls setTimeout(0) on any socket it takes over, so there + // is an "in between" state where this doesn't happen. We work around this + // by explicitly setting the socket timeout to a relatively large time here, + // and setting it back to zero when we set up the heartbeat in + // livedata_server.js. + socket.setWebsocketTimeout = function (timeout) { + if ((socket.protocol === 'websocket' || + socket.protocol === 'websocket-raw') + && socket._session.recv) { + socket._session.recv.connection.setTimeout(timeout); + } + }; + socket.setWebsocketTimeout(45 * 1000); + socket.send = function (data) { socket.write(data); }; diff --git a/packages/minifier-js/package.js b/packages/minifier-js/package.js index 831c952eb2..a155e7e013 100644 --- a/packages/minifier-js/package.js +++ b/packages/minifier-js/package.js @@ -1,10 +1,10 @@ Package.describe({ summary: "JavaScript minifier", - version: "1.2.15" + version: "1.2.16" }); Npm.depends({ - "uglify-js": "2.7.0" + "uglify-js": "2.7.5" }); Npm.strip({ diff --git a/tools/cli/commands.js b/tools/cli/commands.js index b88977d16e..3632698a2e 100644 --- a/tools/cli/commands.js +++ b/tools/cli/commands.js @@ -744,6 +744,8 @@ main.registerCommand({ // the packages (or maybe an unpredictable subset based on what happens to be // in the template's versions file). + require("./default-npm-deps.js").install(appPath); + var appNameToDisplay = appPathAsEntered === "." ? "current directory" : `'${appPathAsEntered}'`; diff --git a/tools/cli/default-npm-deps.js b/tools/cli/default-npm-deps.js index 73baf9c6a7..95df83b24d 100644 --- a/tools/cli/default-npm-deps.js +++ b/tools/cli/default-npm-deps.js @@ -6,7 +6,7 @@ import { unlink, } from "../fs/files.js"; -const INSTALL_JOB_MESSAGE = "installing dependencies from package.json"; +const INSTALL_JOB_MESSAGE = "installing npm dependencies"; export function install(appDir) { const packageJsonPath = pathJoin(appDir, "package.json"); diff --git a/tools/isobuild/compiler-plugin.js b/tools/isobuild/compiler-plugin.js index b93100387d..482903b067 100644 --- a/tools/isobuild/compiler-plugin.js +++ b/tools/isobuild/compiler-plugin.js @@ -726,7 +726,8 @@ class ResourceSlot { if (_.isString(options.data)) { options.data = new Buffer(options.data); } else { - throw new Error("'data' option to addAsset must be a Buffer or String."); + throw new Error("'data' option to addAsset must be a Buffer or " + + "String: " + self.inputResource.path); } }