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.
This commit is contained in:
Emily Stark
2013-10-09 11:59:18 -07:00
parent 8883056bf7
commit de05d27cf3
4 changed files with 46 additions and 33 deletions

View File

@@ -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:");

View File

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

View File

@@ -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.)

View File

@@ -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##/,
"<script type='text/javascript'>__meteor_runtime_config__ = " +
@@ -572,3 +566,14 @@ WebAppInternals.bindToProxy = function (proxyConfig) {
};
runWebAppServer();
var inlineScriptsAllowed = true;
WebAppInternals.inlineScriptsAllowed = function () {
return inlineScriptsAllowed;
};
WebAppInternals.setInlineScriptsAllowed = function (value) {
inlineScriptsAllowed = value;
};