From de05d27cf381ccfcc4b96ae120d71f49f81e0907 Mon Sep 17 00:00:00 2001 From: Emily Stark Date: Wed, 9 Oct 2013 11:59:18 -0700 Subject: [PATCH] Make browser-policy-content tell webapp about inline script policy. This avoids the need for webapp to probe for browser-policy without weak-depending on it. --- .../browser-policy-content.js | 42 +++++++++++-------- packages/browser-policy-content/package.js | 2 +- .../browser-policy/browser-policy-test.js | 14 ++++--- packages/webapp/webapp_server.js | 21 ++++++---- 4 files changed, 46 insertions(+), 33 deletions(-) diff --git a/packages/browser-policy-content/browser-policy-content.js b/packages/browser-policy-content/browser-policy-content.js index 09d4cb7ee9..b9ef18c27b 100644 --- a/packages/browser-policy-content/browser-policy-content.js +++ b/packages/browser-policy-content/browser-policy-content.js @@ -94,6 +94,11 @@ var setDefaultPolicy = function () { "style-src 'self' 'unsafe-inline';"); }; +var setWebAppInlineScripts = function (value) { + if (! BrowserPolicy._runningTest()) + WebAppInternals.setInlineScriptsAllowed(value); +}; + _.extend(BrowserPolicy.content, { // Exported for tests and browser-policy-common. _constructCsp: function () { @@ -123,6 +128,9 @@ _.extend(BrowserPolicy.content, { setPolicy: function (csp) { cachedCsp = null; parseCsp(csp); + setWebAppInlineScripts( + BrowserPolicy.content._keywordAllowed("script-src", unsafeInline) + ); }, _keywordAllowed: function (directive, keyword) { @@ -130,29 +138,17 @@ _.extend(BrowserPolicy.content, { _.indexOf(cspSrcs[directive], keyword) !== -1); }, - // Used by webapp to determine whether we need an extra round trip for - // __meteor_runtime_config__. If we're in a test run, we should always return - // true, since CSP headers are never sent on tests -- unless the - // _calledFromTests flag is set, in which case a test is testing what - // inlineScriptsAllowed() would return if we weren't in a test. Wphew. - // XXX maybe this test interface could be cleaned up - inlineScriptsAllowed: function (_calledFromTests) { - if (BrowserPolicy._runningTest() && ! _calledFromTests) - return true; - - return BrowserPolicy.content._keywordAllowed("script-src", - unsafeInline); - }, - // Helpers for creating content security policies allowInlineScripts: function () { prepareForCspDirective("script-src"); cspSrcs["script-src"].push(unsafeInline); + setWebAppInlineScripts(true); }, disallowInlineScripts: function () { prepareForCspDirective("script-src"); removeCspSrc("script-src", unsafeInline); + setWebAppInlineScripts(false); }, allowEval: function () { prepareForCspDirective("script-src"); @@ -189,6 +185,7 @@ _.extend(BrowserPolicy.content, { cspSrcs = { "default-src": [] }; + setWebAppInlineScripts(false); } }); @@ -210,14 +207,23 @@ _.each(["script", "object", "img", "media", var allowDataMethodName = "allow" + methodResource + "DataUrl"; var allowSelfMethodName = "allow" + methodResource + "SameOrigin"; + var disallow = function () { + cachedCsp = null; + cspSrcs[directive] = []; + }; + BrowserPolicy.content[allowMethodName] = function (src) { prepareForCspDirective(directive); cspSrcs[directive].push(src); }; - BrowserPolicy.content[disallowMethodName] = function () { - cachedCsp = null; - cspSrcs[directive] = []; - }; + 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:"); diff --git a/packages/browser-policy-content/package.js b/packages/browser-policy-content/package.js index 542951fb4c..49d29a7a17 100644 --- a/packages/browser-policy-content/package.js +++ b/packages/browser-policy-content/package.js @@ -5,5 +5,5 @@ Package.describe({ Package.on_use(function (api) { api.imply(["browser-policy-common"], "server"); api.add_files("browser-policy-content.js", "server"); - api.use(["underscore", "browser-policy-common"], "server"); + api.use(["underscore", "browser-policy-common", "webapp"], "server"); }); diff --git a/packages/browser-policy/browser-policy-test.js b/packages/browser-policy/browser-policy-test.js index 08757a3235..e8b00b20a1 100644 --- a/packages/browser-policy/browser-policy-test.js +++ b/packages/browser-policy/browser-policy-test.js @@ -27,7 +27,7 @@ Tinytest.add("browser-policy - csp", function (test) { BrowserPolicy.content._reset(); // Default policy test.isTrue(cspsEqual(BrowserPolicy.content._constructCsp(), defaultCsp)); - test.isTrue(BrowserPolicy.content.inlineScriptsAllowed(true /* tests-only flag */)); + test.isTrue(BrowserPolicy.content._keywordAllowed("script-src", "'unsafe-inline'")); // Redundant whitelisting (inline scripts already allowed in default policy) BrowserPolicy.content.allowInlineScripts(); @@ -38,7 +38,7 @@ Tinytest.add("browser-policy - csp", function (test) { test.isTrue(cspsEqual(BrowserPolicy.content._constructCsp(), "default-src 'self'; script-src 'self'; " + "connect-src * 'self'; img-src data: 'self'; style-src 'self' 'unsafe-inline';")); - test.isFalse(BrowserPolicy.content.inlineScriptsAllowed(true)); + test.isFalse(BrowserPolicy.content._keywordAllowed("script-src", "'unsafe-inline'")); // Allow eval BrowserPolicy.content.allowEval(); @@ -59,26 +59,26 @@ Tinytest.add("browser-policy - csp", function (test) { // Disallow everything BrowserPolicy.content.disallowAll(); test.isTrue(cspsEqual(BrowserPolicy.content._constructCsp(), "default-src 'none';")); - test.isFalse(BrowserPolicy.content.inlineScriptsAllowed(true)); + test.isFalse(BrowserPolicy.content._keywordAllowed("script-src", "'unsafe-inline'")); // Put inline scripts back in BrowserPolicy.content.allowInlineScripts(); test.isTrue(cspsEqual(BrowserPolicy.content._constructCsp(), "default-src 'none'; script-src 'unsafe-inline';")); - test.isTrue(BrowserPolicy.content.inlineScriptsAllowed(true)); + test.isTrue(BrowserPolicy.content._keywordAllowed("script-src", "'unsafe-inline'")); // Add 'self' to all content types BrowserPolicy.content.allowSameOriginForAll(); test.isTrue(cspsEqual(BrowserPolicy.content._constructCsp(), "default-src 'self'; script-src 'self' 'unsafe-inline';")); - test.isTrue(BrowserPolicy.content.inlineScriptsAllowed(true)); + test.isTrue(BrowserPolicy.content._keywordAllowed("script-src", "'unsafe-inline'")); // Disallow all content except same-origin scripts BrowserPolicy.content.disallowAll(); BrowserPolicy.content.allowScriptSameOrigin(); test.isTrue(cspsEqual(BrowserPolicy.content._constructCsp(), "default-src 'none'; script-src 'self';")); - test.isFalse(BrowserPolicy.content.inlineScriptsAllowed(true)); + test.isFalse(BrowserPolicy.content._keywordAllowed("script-src", "'unsafe-inline'")); // Starting with all content same origin, disallowScript() and then allow // inline scripts. Result should be that that only inline scripts can execute, @@ -89,9 +89,11 @@ Tinytest.add("browser-policy - csp", function (test) { BrowserPolicy.content.disallowScript(); test.isTrue(cspsEqual(BrowserPolicy.content._constructCsp(), "default-src 'self'; script-src 'none';")); + test.isFalse(BrowserPolicy.content._keywordAllowed("script-src", "'unsafe-inline'")); BrowserPolicy.content.allowInlineScripts(); test.isTrue(cspsEqual(BrowserPolicy.content._constructCsp(), "default-src 'self'; script-src 'unsafe-inline';")); + test.isTrue(BrowserPolicy.content._keywordAllowed("script-src", "'unsafe-inline'")); // Starting with all content same origin, allow inline scripts. (Should result // in both same origin and inline scripts allowed.) diff --git a/packages/webapp/webapp_server.js b/packages/webapp/webapp_server.js index f801dc669e..2e72e366ed 100644 --- a/packages/webapp/webapp_server.js +++ b/packages/webapp/webapp_server.js @@ -231,11 +231,8 @@ var runWebAppServer = function () { return; } - var browserPolicyPackage = Package["browser-policy-common"]; if (pathname === "/meteor_runtime_config.js" && - browserPolicyPackage && - browserPolicyPackage.BrowserPolicy.content && - ! browserPolicyPackage.BrowserPolicy.content.inlineScriptsAllowed()) { + ! WebAppInternals.inlineScriptsAllowed()) { res.writeHead(200, { 'Content-type': 'application/javascript' }); res.write("__meteor_runtime_config__ = " + JSON.stringify(__meteor_runtime_config__) + ";"); @@ -403,10 +400,7 @@ var runWebAppServer = function () { // Include __meteor_runtime_config__ in the app html, as an inline script if // it's not forbidden by CSP. - var browserPolicyPackage = Package["browser-policy-common"]; - if (! browserPolicyPackage || - ! browserPolicyPackage.BrowserPolicy.content || - browserPolicyPackage.BrowserPolicy.content.inlineScriptsAllowed()) { + if (WebAppInternals.inlineScriptsAllowed()) { boilerplateHtml = boilerplateHtml.replace( /##RUNTIME_CONFIG##/, "