Bug 1017257 - Attach Loop CSP Policy r=sstamm,dmose
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1611,16 +1611,21 @@ pref("loop.ringtone", "chrome://browser/
pref("loop.retry_delay.start", 60000);
pref("loop.retry_delay.limit", 300000);
pref("loop.feedback.baseUrl", "https://input.mozilla.org/api/v1/feedback");
pref("loop.feedback.product", "Loop");
pref("loop.debug.loglevel", "Error");
pref("loop.debug.dispatcher", false);
pref("loop.debug.websocket", false);
pref("loop.debug.sdk", false);
+#ifdef DEBUG
+pref("loop.CSP", "default-src 'self' about: file: chrome: http://localhost:*; img-src 'self' data: http://www.gravatar.com/ about: file: chrome:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net http://localhost:* ws://localhost:*");
+#else
+pref("loop.CSP", "default-src 'self' about: file: chrome:; img-src 'self' data: http://www.gravatar.com/ about: file: chrome:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net");
+#endif
pref("loop.oauth.google.redirect_uri", "urn:ietf:wg:oauth:2.0:oob:auto");
pref("loop.oauth.google.scope", "https://www.google.com/m8/feeds");
// serverURL to be assigned by services team
pref("services.push.serverURL", "wss://push.services.mozilla.com/");
pref("social.sidebar.unload_timeout_ms", 10000);
--- a/browser/base/content/test/general/browser_devices_get_user_media_about_urls.js
+++ b/browser/base/content/test/general/browser_devices_get_user_media_about_urls.js
@@ -7,16 +7,17 @@ const kObservedTopics = [
"getUserMedia:revoke",
"getUserMedia:response:deny",
"getUserMedia:request",
"recording-device-events",
"recording-window-ended"
];
const PREF_PERMISSION_FAKE = "media.navigator.permission.fake";
+const PREF_LOOP_CSP = "loop.CSP";
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "MediaManagerService",
"@mozilla.org/mediaManagerService;1",
"nsIMediaManagerService");
var gTab;
@@ -157,30 +158,34 @@ fakeLoopAboutModule.prototype = {
Ci.nsIAboutModule.ALLOW_SCRIPT |
Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT;
}
};
let factory = XPCOMUtils._getFactory(fakeLoopAboutModule);
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+let originalLoopCsp = Services.prefs.getCharPref(PREF_LOOP_CSP);
registerCleanupFunction(function() {
gBrowser.removeCurrentTab();
kObservedTopics.forEach(topic => {
Services.obs.removeObserver(observer, topic);
});
Services.prefs.clearUserPref(PREF_PERMISSION_FAKE);
+ Services.prefs.setCharPref(PREF_LOOP_CSP, originalLoopCsp);
});
let gTests = [
{
desc: "getUserMedia about:loopconversation shouldn't prompt",
run: function checkAudioVideoLoop() {
+ Services.prefs.setCharPref(PREF_LOOP_CSP, "default-src 'unsafe-inline'");
+
let classID = Cc["@mozilla.org/uuid-generator;1"]
.getService(Ci.nsIUUIDGenerator).generateUUID();
registrar.registerFactory(classID, "",
"@mozilla.org/network/protocol/about;1?what=loopconversation",
factory);
yield loadPage("about:loopconversation");
@@ -193,16 +198,17 @@ let gTests = [
yield promisePopupNotification("webRTC-sharingDevices");
is(getMediaCaptureState(), "CameraAndMicrophone",
"expected camera and microphone to be shared");
yield closeStream();
registrar.unregisterFactory(classID, factory);
+ Services.prefs.setCharPref(PREF_LOOP_CSP, originalLoopCsp);
}
},
{
desc: "getUserMedia about:evil should prompt",
run: function checkAudioVideoNonLoop() {
let classID = Cc["@mozilla.org/uuid-generator;1"]
.getService(Ci.nsIUUIDGenerator).generateUUID();
--- a/browser/components/loop/run-all-loop-tests.sh
+++ b/browser/components/loop/run-all-loop-tests.sh
@@ -4,8 +4,13 @@
set -e
# Main tests
./mach xpcshell-test browser/components/loop/
./mach marionette-test browser/components/loop/manifest.ini
# The browser_parsable_css.js can fail if we add some css that isn't parsable.
./mach mochitest browser/components/loop/test/mochitest browser/base/content/test/general/browser_parsable_css.js
+
+# The check to make sure that the media devices can be used in Loop without
+# prompting is in browser_devices_get_user_media_about_urls.js. It's possible
+# to mess this up with CSP handling, and probably other changes, too.
+./mach mochitest browser/base/content/test/general/browser_devices_get_user_media_about_urls.js
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -2747,16 +2747,43 @@ AppendCSPFromHeader(nsIContentSecurityPo
("CSP refined with policy: \"%s\"",
NS_ConvertUTF16toUTF8(policy).get()));
}
#endif
}
return NS_OK;
}
+bool
+nsDocument::IsLoopDocument(nsIChannel *aChannel)
+{
+ nsCOMPtr<nsIURI> chanURI;
+ nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(chanURI));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ bool isAbout = false;
+ bool isLoop = false;
+ rv = chanURI->SchemeIs("about", &isAbout);
+ NS_ENSURE_SUCCESS(rv, false);
+ if (isAbout) {
+ nsCOMPtr<nsIURI> loopURI;
+ rv = NS_NewURI(getter_AddRefs(loopURI), "about:loopconversation");
+ NS_ENSURE_SUCCESS(rv, false);
+ rv = chanURI->EqualsExceptRef(loopURI, &isLoop);
+ NS_ENSURE_SUCCESS(rv, false);
+ if (!isLoop) {
+ rv = NS_NewURI(getter_AddRefs(loopURI), "about:looppanel");
+ NS_ENSURE_SUCCESS(rv, false);
+ rv = chanURI->EqualsExceptRef(loopURI, &isLoop);
+ NS_ENSURE_SUCCESS(rv, false);
+ }
+ }
+ return isLoop;
+}
+
nsresult
nsDocument::InitCSP(nsIChannel* aChannel)
{
nsCOMPtr<nsIContentSecurityPolicy> csp;
if (!CSPService::sCSPEnabled) {
#ifdef PR_LOGGING
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
("CSP is disabled, skipping CSP init for document %p", this));
@@ -2800,19 +2827,23 @@ nsDocument::InitCSP(nsIChannel* aChannel
appsService->GetDefaultCSPByLocalId(appId, appDefaultCSP);
if (!appDefaultCSP.IsEmpty()) {
applyAppDefaultCSP = true;
}
}
}
}
+ // Check if this is part of the Loop/Hello service
+ bool applyLoopCSP = IsLoopDocument(aChannel);
+
// If there's no CSP to apply, go ahead and return early
if (!applyAppDefaultCSP &&
!applyAppManifestCSP &&
+ !applyLoopCSP &&
cspHeaderValue.IsEmpty() &&
cspROHeaderValue.IsEmpty()) {
#ifdef PR_LOGGING
nsCOMPtr<nsIURI> chanURI;
aChannel->GetURI(getter_AddRefs(chanURI));
nsAutoCString aspec;
chanURI->GetAsciiSpec(aspec);
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
@@ -2875,16 +2906,27 @@ nsDocument::InitCSP(nsIChannel* aChannel
csp->AppendPolicy(appDefaultCSP, false);
}
// ----- if the doc is an app and specifies a CSP in its manifest, apply it.
if (applyAppManifestCSP) {
csp->AppendPolicy(appManifestCSP, false);
}
+ // ----- if the doc is part of Loop, apply the loop CSP
+ if (applyLoopCSP) {
+ nsAdoptingString loopCSP;
+ loopCSP = Preferences::GetString("loop.CSP");
+ NS_ASSERTION(loopCSP, "Missing loop.CSP preference");
+ // If the pref has been removed, we continue without setting a CSP
+ if (loopCSP) {
+ csp->AppendPolicy(loopCSP, false);
+ }
+ }
+
// ----- if there's a full-strength CSP header, apply it.
if (!cspHeaderValue.IsEmpty()) {
rv = AppendCSPFromHeader(csp, cspHeaderValue, false);
NS_ENSURE_SUCCESS(rv, rv);
}
// ----- if there's a report-only CSP header, apply it.
if (!cspROHeaderValue.IsEmpty()) {
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -1657,16 +1657,17 @@ private:
mozilla::dom::VisibilityState GetVisibilityState() const;
void NotifyStyleSheetAdded(nsIStyleSheet* aSheet, bool aDocumentSheet);
void NotifyStyleSheetRemoved(nsIStyleSheet* aSheet, bool aDocumentSheet);
void PostUnblockOnloadEvent();
void DoUnblockOnload();
nsresult CheckFrameOptions();
+ bool IsLoopDocument(nsIChannel* aChannel);
nsresult InitCSP(nsIChannel* aChannel);
void FlushCSPWebConsoleErrorQueue()
{
mCSPWebConsoleErrorQueue.Flush(this);
}
/**
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -249,16 +249,17 @@ user_pref("dom.mozApps.debug", true);
user_pref("browser.newtabpage.directory.source", 'data:application/json,{"testing":1}');
user_pref("browser.newtabpage.directory.ping", "");
// Enable Loop
user_pref("loop.enabled", true);
user_pref("loop.throttled", false);
user_pref("loop.oauth.google.URL", "http://%(server)s/browser/browser/components/loop/test/mochitest/google_service.sjs?action=");
user_pref("loop.oauth.google.getContactsURL", "http://%(server)s/browser/browser/components/loop/test/mochitest/google_service.sjs?action=contacts");
+user_pref("loop.CSP","default-src 'self' about: file: chrome: data: wss://* http://* https://*");
// Ensure UITour won't hit the network
user_pref("browser.uitour.pinnedTabUrl", "http://%(server)s/uitour-dummy/pinnedTab");
user_pref("browser.uitour.url", "http://%(server)s/uitour-dummy/tour");
user_pref("media.eme.enabled", true);
// Don't prompt about e10s