Bug 824652 - crypto.generateCRMFRequest bypasses CSP (allows script execution from a string, without unsafe-eval) r=bsmith r=khuey r=keeler
authorDavid Dahl <ddahl@mozilla.com>
Tue, 06 Aug 2013 21:46:05 -0500
changeset 154526 8dc0904666fdd8bf6a9d37ab155d35a7676a5622
parent 154525 91f4bd08f12b416f38b5bda9c9bee77a24b3253d
child 154527 fba5c857adb61a3adf43720750bee656dfd1a3cd
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmith, khuey, keeler
bugs824652
milestone26.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 824652 - crypto.generateCRMFRequest bypasses CSP (allows script execution from a string, without unsafe-eval) r=bsmith r=khuey r=keeler
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
content/base/test/Makefile.in
content/base/test/file_CSP_evalscript_main.js
content/base/test/file_CSP_evalscript_main_allowed.js
content/base/test/file_CSP_evalscript_main_allowed_getCRMFRequest.js
content/base/test/file_CSP_evalscript_main_getCRMFRequest.html
content/base/test/file_CSP_evalscript_main_getCRMFRequest.html^headers^
content/base/test/file_CSP_evalscript_main_getCRMFRequest.js
content/base/test/file_CSP_evalscript_main_spec_compliant_allowed_getCRMFRequest.html
content/base/test/file_CSP_evalscript_main_spec_compliant_allowed_getCRMFRequest.html^headers^
content/base/test/file_CSP_evalscript_main_spec_compliant_getCRMFRequest.html
content/base/test/file_CSP_evalscript_main_spec_compliant_getCRMFRequest.html^headers^
content/base/test/file_CSP_evalscript_no_CSP_at_all.html
content/base/test/file_CSP_evalscript_no_CSP_at_all.html^headers^
content/base/test/file_CSP_evalscript_no_CSP_at_all.js
content/base/test/test_CSP_evalscript.html
content/base/test/test_CSP_evalscript_getCRMFRequest.html
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
security/manager/ssl/src/nsCrypto.cpp
testing/mochitest/android.json
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -39,16 +39,17 @@ class nsAutoScriptBlockerSuppressNodeRem
 class nsDragEvent;
 class nsEvent;
 class nsEventListenerManager;
 class nsHtml5StringParser;
 class nsIChannel;
 class nsIConsoleService;
 class nsIContent;
 class nsIContentPolicy;
+class nsIContentSecurityPolicy;
 class nsIDocShell;
 class nsIDocument;
 class nsIDocumentLoaderFactory;
 class nsIDocumentObserver;
 class nsIDOMDocument;
 class nsIDOMDocumentFragment;
 class nsIDOMEvent;
 class nsIDOMHTMLFormElement;
@@ -466,16 +467,22 @@ public:
    * Get the cache security manager service. Can return null if the layout
    * module has been shut down.
    */
   static nsIScriptSecurityManager* GetSecurityManager()
   {
     return sSecurityManager;
   }
 
+  /**
+   * Get the ContentSecurityPolicy for a JS context.
+   **/
+  static bool GetContentSecurityPolicy(JSContext* aCx,
+                                       nsIContentSecurityPolicy** aCSP);
+
   // Returns the subject principal. Guaranteed to return non-null. May only
   // be called when nsContentUtils is initialized.
   static nsIPrincipal* GetSubjectPrincipal();
 
   // Returns the principal of the given JS object. This should never be null
   // for any object in the XPConnect runtime.
   //
   // In general, being interested in the principal of an object is enough to
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -6021,16 +6021,44 @@ nsContentUtils::FindInternalContentViewe
       *aLoaderType = TYPE_CONTENT;
     }
     return docFactory.forget();
   }
 
   return nullptr;
 }
 
+bool
+nsContentUtils::GetContentSecurityPolicy(JSContext* aCx,
+                                         nsIContentSecurityPolicy** aCSP)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // Get the security manager
+  nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
+
+  if (!ssm) {
+    NS_ERROR("Failed to get security manager service");
+    return false;
+  }
+
+  nsCOMPtr<nsIPrincipal> subjectPrincipal = ssm->GetCxSubjectPrincipal(aCx);
+  NS_ASSERTION(subjectPrincipal, "Failed to get subjectPrincipal");
+
+  nsCOMPtr<nsIContentSecurityPolicy> csp;
+  nsresult rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
+  if (NS_FAILED(rv)) {
+    NS_ERROR("CSP: Failed to get CSP from principal.");
+    return false;
+  }
+
+  csp.forget(aCSP);
+  return true;
+}
+
 // static
 bool
 nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern,
                                   nsIDocument* aDocument)
 {
   NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aDocument->GetWindow());
   NS_ENSURE_TRUE(sgo, true);
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -359,16 +359,28 @@ MOCHITEST_FILES_B = \
 		file_CSP_evalscript_main.html \
 		file_CSP_evalscript_main.html^headers^ \
 		file_CSP_evalscript_main.js \
 		file_CSP_evalscript_main_allowed.js \
 		file_CSP_evalscript_main_spec_compliant.html \
 	 	file_CSP_evalscript_main_spec_compliant.html^headers^ \
 		file_CSP_evalscript_main_spec_compliant_allowed.html \
 		file_CSP_evalscript_main_spec_compliant_allowed.html^headers^ \
+		test_CSP_evalscript_getCRMFRequest.html \
+		file_CSP_evalscript_main_getCRMFRequest.html \
+		file_CSP_evalscript_main_getCRMFRequest.html^headers^ \
+		file_CSP_evalscript_main_getCRMFRequest.js \
+		file_CSP_evalscript_main_allowed_getCRMFRequest.js \
+		file_CSP_evalscript_main_spec_compliant_getCRMFRequest.html \
+	 	file_CSP_evalscript_main_spec_compliant_getCRMFRequest.html^headers^ \
+		file_CSP_evalscript_main_spec_compliant_allowed_getCRMFRequest.html \
+		file_CSP_evalscript_main_spec_compliant_allowed_getCRMFRequest.html^headers^ \
+		file_CSP_evalscript_no_CSP_at_all.html \
+		file_CSP_evalscript_no_CSP_at_all.html^headers^ \
+		file_CSP_evalscript_no_CSP_at_all.js \
 		test_CSP_inlinestyle.html \
 		file_CSP_inlinestyle_main.html \
 		file_CSP_inlinestyle_main.html^headers^ \
 		file_CSP_inlinestyle_main_spec_compliant.html \
 		file_CSP_inlinestyle_main_spec_compliant.html^headers^ \
 		file_CSP_inlinestyle_main_spec_compliant_allowed.html \
 		file_CSP_inlinestyle_main_spec_compliant_allowed.html^headers^ \
 		file_csp_bug768029.html \
--- a/content/base/test/file_CSP_evalscript_main.js
+++ b/content/base/test/file_CSP_evalscript_main.js
@@ -25,17 +25,16 @@ var onevalblocked = (function(window) {
       window.parent.scriptBlocked(shouldrun, what, data);
       logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
     };})(window);
 
 
 // Defer until document is loaded so that we can write the pretty result boxes
 // out.
 addEventListener('load', function() {
-
   // setTimeout(String) test -- mutate something in the window._testResults
   // obj, then check it.
   {
     var str_setTimeoutWithStringRan = 'onevalexecuted(false, "setTimeout(String)", "setTimeout with a string was enabled.");';
     function fcn_setTimeoutWithStringCheck() {
       if (this._testResults["setTimeout(String)"] !== "ran") {
         onevalblocked(false, "setTimeout(String)",
                       "setTimeout with a string was blocked");
--- a/content/base/test/file_CSP_evalscript_main_allowed.js
+++ b/content/base/test/file_CSP_evalscript_main_allowed.js
@@ -23,17 +23,16 @@ var onevalblocked = (function(window) {
       window.parent.scriptBlocked(shouldrun, what, data);
       logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
     };})(window);
 
 
 // Defer until document is loaded so that we can write the pretty result boxes
 // out.
 addEventListener('load', function() {
-
   // setTimeout(String) test  -- should pass
   try {
     setTimeout('onevalexecuted(true, "setTimeout(String)", "setTimeout with a string was enabled.");', 10);
   } catch (e) {
     onevalblocked(true, "setTimeout(String)",
                   "setTimeout with a string was blocked");
   }
 
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_main_allowed_getCRMFRequest.js
@@ -0,0 +1,42 @@
+// some javascript for the CSP eval() tests
+// all of these evals should succeed, as the document loading this script
+// has script-src 'self' 'unsafe-eval'
+
+function logResult(str, passed) {
+  var elt = document.createElement('div');
+  var color = passed ? "#cfc;" : "#fcc";
+  elt.setAttribute('style', 'background-color:' + color + '; width:100%; border:1px solid black; padding:3px; margin:4px;');
+  elt.innerHTML = str;
+  document.body.appendChild(elt);
+}
+
+// callback for when stuff is allowed by CSP
+var onevalexecuted = (function(window) {
+    return function(shouldrun, what, data) {
+      window.parent.scriptRan(shouldrun, what, data);
+      logResult((shouldrun ? "PASS: " : "FAIL: ") + what + " : " + data, shouldrun);
+    };})(window);
+
+// callback for when stuff is blocked
+var onevalblocked = (function(window) {
+    return function(shouldrun, what, data) {
+      window.parent.scriptBlocked(shouldrun, what, data);
+      logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
+    };})(window);
+
+
+// Defer until document is loaded so that we can write the pretty result boxes
+// out.
+addEventListener('load', function() {
+  // test that allows crypto.generateCRMFRequest eval to run
+  try {
+      var script =
+        'console.log("dynamic script passed to crypto.generateCRMFRequest should execute")';
+      crypto.generateCRMFRequest('CN=0', 0, 0, null, script, 384, null, 'rsa-dual-use');
+      onevalexecuted(true, "eval(script) inside crypto.generateCRMFRequest",
+                     "eval executed during crypto.generateCRMFRequest");
+  } catch (e) {
+    onevalblocked(true, "eval(script) inside crypto.generateCRMFRequest",
+                  "eval was blocked during crypto.generateCRMFRequest");
+  }
+}, false);
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_main_getCRMFRequest.html
@@ -0,0 +1,12 @@
+<html>
+  <head>
+    <title>CSP eval script tests</title>
+    <script type="application/javascript"
+             src="file_CSP_evalscript_main_getCRMFRequest.js"></script>
+  </head>
+  <body>
+
+    Foo.
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_main_getCRMFRequest.html^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-cache
+X-Content-Security-Policy: default-src 'self'
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_main_getCRMFRequest.js
@@ -0,0 +1,48 @@
+// some javascript for the CSP eval() tests
+
+function logResult(str, passed) {
+  var elt = document.createElement('div');
+  var color = passed ? "#cfc;" : "#fcc";
+  elt.setAttribute('style', 'background-color:' + color + '; width:100%; border:1px solid black; padding:3px; margin:4px;');
+  elt.innerHTML = str;
+  document.body.appendChild(elt);
+}
+
+window._testResults = {};
+
+// callback for when stuff is allowed by CSP
+var onevalexecuted = (function(window) {
+    return function(shouldrun, what, data) {
+      window._testResults[what] = "ran";
+      window.parent.scriptRan(shouldrun, what, data);
+      logResult((shouldrun ? "PASS: " : "FAIL: ") + what + " : " + data, shouldrun);
+    };})(window);
+
+// callback for when stuff is blocked
+var onevalblocked = (function(window) {
+    return function(shouldrun, what, data) {
+      window._testResults[what] = "blocked";
+      window.parent.scriptBlocked(shouldrun, what, data);
+      logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
+    };})(window);
+
+
+// Defer until document is loaded so that we can write the pretty result boxes
+// out.
+addEventListener('load', function() {
+  // generateCRMFRequest test -- make sure we cannot eval the callback if CSP is in effect
+  try {
+    var script = 'console.log("dynamic script eval\'d in crypto.generateCRMFRequest should be disallowed")';
+    crypto.generateCRMFRequest('CN=0', 0, 0, null, script, 384, null, 'rsa-dual-use');
+    onevalexecuted(false, "crypto.generateCRMFRequest()",
+                   "crypto.generateCRMFRequest() should not run!");
+  } catch (e) {
+    onevalblocked(false, "eval(script) inside crypto.generateCRMFRequest",
+                  "eval was blocked during crypto.generateCRMFRequest");
+  }
+
+
+}, false);
+
+
+
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_main_spec_compliant_allowed_getCRMFRequest.html
@@ -0,0 +1,12 @@
+<html>
+  <head>
+    <title>CSP eval script tests</title>
+    <script type="application/javascript"
+             src="file_CSP_evalscript_main_allowed_getCRMFRequest.js"></script>
+  </head>
+  <body>
+
+    Foo.
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_main_spec_compliant_allowed_getCRMFRequest.html^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-cache
+Content-Security-Policy: default-src 'self' ; script-src 'self' 'unsafe-eval'
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_main_spec_compliant_getCRMFRequest.html
@@ -0,0 +1,12 @@
+<html>
+  <head>
+    <title>CSP eval script tests</title>
+    <script type="application/javascript"
+             src="file_CSP_evalscript_main_getCRMFRequest.js"></script>
+  </head>
+  <body>
+
+    Foo.
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_main_spec_compliant_getCRMFRequest.html^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-cache
+Content-Security-Policy: default-src 'self'
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_no_CSP_at_all.html
@@ -0,0 +1,12 @@
+<html>
+  <head>
+    <title>CSP eval script tests: no CSP specified</title>
+    <script type="application/javascript"
+             src="file_CSP_evalscript_no_CSP_at_all.js"></script>
+  </head>
+  <body>
+
+    Foo. See bug 824652
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_no_CSP_at_all.html^headers^
@@ -0,0 +1,1 @@
+Cache-Control: no-cache
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_no_CSP_at_all.js
@@ -0,0 +1,42 @@
+// some javascript for the CSP eval() tests
+// all of these evals should succeed, as the document loading this script
+// has script-src 'self' 'unsafe-eval'
+
+function logResult(str, passed) {
+  var elt = document.createElement('div');
+  var color = passed ? "#cfc;" : "#fcc";
+  elt.setAttribute('style', 'background-color:' + color + '; width:100%; border:1px solid black; padding:3px; margin:4px;');
+  elt.innerHTML = str;
+  document.body.appendChild(elt);
+}
+
+// callback for when stuff is allowed by CSP
+var onevalexecuted = (function(window) {
+    return function(shouldrun, what, data) {
+      window.parent.scriptRan(shouldrun, what, data);
+      logResult((shouldrun ? "PASS: " : "FAIL: ") + what + " : " + data, shouldrun);
+    };})(window);
+
+// callback for when stuff is blocked
+var onevalblocked = (function(window) {
+    return function(shouldrun, what, data) {
+      window.parent.scriptBlocked(shouldrun, what, data);
+      logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
+    };})(window);
+
+
+// Defer until document is loaded so that we can write the pretty result boxes
+// out.
+addEventListener('load', function() {
+  // test that allows crypto.generateCRMFRequest eval to run when there is no CSP at all in place
+  try {
+      var script =
+        'console.log("dynamic script passed to crypto.generateCRMFRequest should execute")';
+      crypto.generateCRMFRequest('CN=0', 0, 0, null, script, 384, null, 'rsa-dual-use');
+      onevalexecuted(true, "eval(script) inside crypto.generateCRMFRequest: no CSP at all",
+                     "eval executed during crypto.generateCRMFRequest where no CSP is set at all");
+  } catch (e) {
+    onevalblocked(true, "eval(script) inside crypto.generateCRMFRequest",
+                  "eval was blocked during crypto.generateCRMFRequest");
+  }
+}, false);
--- a/content/base/test/test_CSP_evalscript.html
+++ b/content/base/test/test_CSP_evalscript.html
@@ -56,13 +56,14 @@ SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPrefEnv(
   {'set':[["security.csp.speccompliant", true]]},
     function() {
       // save this for last so that our listeners are registered.
       // ... this loads the testbed of good and bad requests.
       document.getElementById('cspframe').src = 'file_CSP_evalscript_main.html';
       document.getElementById('cspframe2').src = 'file_CSP_evalscript_main_spec_compliant.html';
       document.getElementById('cspframe3').src = 'file_CSP_evalscript_main_spec_compliant_allowed.html';
+      // document.getElementById('cspframe4').src = 'file_CSP_evalscript_no_CSP_at_all.html';
     });
 </script>
 </pre>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_CSP_evalscript_getCRMFRequest.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Content Security Policy "no eval" in crypto.getCRMFRequest()</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+  
+</div>
+
+<iframe style="width:100%;height:300px;" id='cspframe'></iframe>
+<iframe style="width:100%;height:300px;" id='cspframe2'></iframe>
+<iframe style="width:100%;height:300px;" id='cspframe3'></iframe>
+<iframe style="width:100%;height:300px;" id='cspframe4'></iframe>
+<script class="testbody" type="text/javascript">
+
+var path = "/tests/content/base/test/";
+
+var evalScriptsThatRan = 0;
+var evalScriptsBlocked = 0;
+var evalScriptsTotal = 4;
+
+
+// called by scripts that run
+var scriptRan = function(shouldrun, testname, data) {
+  evalScriptsThatRan++;
+  ok(shouldrun, 'EVAL SCRIPT RAN: ' + testname + '(' + data + ')');
+  checkTestResults();
+}
+
+// called when a script is blocked
+var scriptBlocked = function(shouldrun, testname, data) {
+  evalScriptsBlocked++;
+  ok(!shouldrun, 'EVAL SCRIPT BLOCKED: ' + testname + '(' + data + ')');
+  checkTestResults();
+}
+
+
+// Check to see if all the tests have run
+var checkTestResults = function() {
+  // if any test is incomplete, keep waiting
+  if (evalScriptsTotal - evalScriptsBlocked - evalScriptsThatRan > 0)
+    return;
+
+  // ... otherwise, finish
+  SimpleTest.finish();
+}
+
+//////////////////////////////////////////////////////////////////////
+// set up and go
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv(
+  {'set':[["security.csp.speccompliant", true]]},
+    function() {
+      // save this for last so that our listeners are registered.
+      // ... this loads the testbed of good and bad requests.
+      document.getElementById('cspframe').src = 'file_CSP_evalscript_main_getCRMFRequest.html';
+      document.getElementById('cspframe2').src = 'file_CSP_evalscript_main_spec_compliant_getCRMFRequest.html';
+      document.getElementById('cspframe3').src = 'file_CSP_evalscript_main_spec_compliant_allowed_getCRMFRequest.html';
+      document.getElementById('cspframe4').src = 'file_CSP_evalscript_no_CSP_at_all.html';
+    });
+</script>
+</pre>
+</body>
+</html>
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2724,17 +2724,17 @@ WorkerPrivate::Create(JSContext* aCx, JS
 
     NS_ASSERTION(principal, "Must have a principal now!");
 
     if (!isChrome && domain.IsEmpty()) {
       NS_ERROR("Must be chrome or have an domain!");
       return nullptr;
     }
 
-    if (!GetContentSecurityPolicy(aCx, getter_AddRefs(csp))) {
+    if (!nsContentUtils::GetContentSecurityPolicy(aCx, getter_AddRefs(csp))) {
       return nullptr;
     }
 
     if (csp && NS_FAILED(csp->GetAllowsEval(&reportEvalViolations, &evalAllowed))) {
       NS_ERROR("CSP: failed to get allowsEval");
       return nullptr;
     }
   }
@@ -4370,44 +4370,16 @@ WorkerPrivate::GetCrossThreadDispatcher(
 {
   mozilla::MutexAutoLock lock(mMutex);
   if (!mCrossThreadDispatcher && mStatus <= Running) {
     mCrossThreadDispatcher = new WorkerCrossThreadDispatcher(this);
   }
   return mCrossThreadDispatcher;
 }
 
-bool
-WorkerPrivate::GetContentSecurityPolicy(JSContext* aCx,
-                                        nsIContentSecurityPolicy** aCSP)
-{
-  AssertIsOnMainThread();
-
-  // Get the security manager
-  nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
-
-  if (!ssm) {
-    NS_ERROR("Failed to get security manager service");
-    return false;
-  }
-
-  nsCOMPtr<nsIPrincipal> subjectPrincipal = ssm->GetCxSubjectPrincipal(aCx);
-  NS_ASSERTION(subjectPrincipal, "Failed to get subjectPrincipal");
-
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  nsresult rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
-  if (NS_FAILED(rv)) {
-    NS_ERROR("CSP: Failed to get CSP from principal.");
-    return false;
-  }
-
-  csp.forget(aCSP);
-  return true;
-}
-
 void
 WorkerPrivate::BeginCTypesCall()
 {
   AssertIsOnWorkerThread();
 
   MutexAutoLock lock(mMutex);
 
   NS_ASSERTION(!mBlockedForMemoryReporter,
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -904,20 +904,16 @@ private:
                 bool aIsChromeWorker, const nsACString& aDomain,
                 nsCOMPtr<nsPIDOMWindow>& aWindow,
                 nsCOMPtr<nsIScriptContext>& aScriptContext,
                 nsCOMPtr<nsIURI>& aBaseURI, nsCOMPtr<nsIPrincipal>& aPrincipal,
                 nsCOMPtr<nsIChannel>& aChannel,
                 nsCOMPtr<nsIContentSecurityPolicy>& aCSP, bool aEvalAllowed,
                 bool aReportCSPViolations, bool aXHRParamsAllowed);
 
-  static bool
-  GetContentSecurityPolicy(JSContext *aCx,
-                           nsIContentSecurityPolicy** aCsp);
-
   bool
   Dispatch(WorkerRunnable* aEvent, EventQueue* aQueue);
 
   bool
   DispatchToSyncQueue(WorkerSyncRunnable* aEvent);
 
   void
   ClearQueue(EventQueue* aQueue);
--- a/security/manager/ssl/src/nsCrypto.cpp
+++ b/security/manager/ssl/src/nsCrypto.cpp
@@ -39,21 +39,23 @@
 #include "nsDOMJSUtils.h"
 #include "nsJSUtils.h"
 #include "nsIXPConnect.h"
 #include "nsIRunnable.h"
 #include "nsIWindowWatcher.h"
 #include "nsIPrompt.h"
 #include "nsIFilePicker.h"
 #include "nsJSPrincipals.h"
+#include "nsJSUtils.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIGenKeypairInfoDlg.h"
 #include "nsIDOMCryptoDialogs.h"
 #include "nsIFormSigningDialog.h"
+#include "nsIContentSecurityPolicy.h"
 #include "jsapi.h"
 #include "jsdbgapi.h"
 #include <ctype.h>
 #include "pk11func.h"
 #include "keyhi.h"
 #include "cryptohi.h"
 #include "seccomon.h"
 #include "secerr.h"
@@ -1889,16 +1891,49 @@ nsCrypto::GenerateCRMFRequest(JSContext*
   }
 
   JS::RootedObject script_obj(aContext, GetWrapper());
   if (MOZ_UNLIKELY(!script_obj)) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
+  nsCOMPtr<nsIContentSecurityPolicy> csp;
+  if (!nsContentUtils::GetContentSecurityPolicy(aContext, getter_AddRefs(csp))) {
+    NS_ERROR("Error: failed to get CSP");
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  bool evalAllowed = true;
+  bool reportEvalViolations = false;
+  if (csp && NS_FAILED(csp->GetAllowsEval(&reportEvalViolations, &evalAllowed))) {
+    NS_WARNING("CSP: failed to get allowsEval");
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  if (reportEvalViolations) {
+    NS_NAMED_LITERAL_STRING(scriptSample, "window.crypto.generateCRMFRequest: call to eval() or related function blocked by CSP");
+
+    const char *fileName;
+    uint32_t lineNum;
+    nsJSUtils::GetCallingLocation(aContext, &fileName, &lineNum);
+    csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
+                             NS_ConvertASCIItoUTF16(fileName),
+                             scriptSample,
+                             lineNum);
+  }
+
+  if (!evalAllowed) {
+    NS_WARNING("eval() not allowed by Content Security Policy");
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
   //Put up some UI warning that someone is trying to
   //escrow the private key.
   //Don't addref this copy.  That way ths reference goes away
   //at the same the nsIX09Cert ref goes away.
   nsNSSCertificate *escrowCert = nullptr;
   nsCOMPtr<nsIX509Cert> nssCert;
   bool willEscrow = false;
   if (!aEaCert.IsVoid()) {
@@ -2037,17 +2072,16 @@ nsCrypto::GenerateCRMFRequest(JSContext*
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     delete cryptoRunnable;
   }
 
   return crmf.forget();
 }
 
-
 // Reminder that we inherit the memory passed into us here.
 // An implementation to let us back up certs as an event.
 nsP12Runnable::nsP12Runnable(nsIX509Cert **certArr, int32_t numCerts,
                              nsIPK11Token *token)
 {
   mCertArr  = certArr;
   mNumCerts = numCerts;
   mToken = token;
--- a/testing/mochitest/android.json
+++ b/testing/mochitest/android.json
@@ -12,16 +12,17 @@
  "content/base/test/test_bug590812.html": "bug 687032",
  "content/base/test/test_copypaste.html": "",
  "content/base/test/test_CrossSiteXHR.html": "",
  "content/base/test/test_CrossSiteXHR_cache.html": "",
  "content/base/test/test_CrossSiteXHR_origin.html": "",
  "content/base/test/test_CSP.html": "TIMED_OUT",
  "content/base/test/test_CSP_frameancestors.html": "",
  "content/base/test/test_CSP_inlinescript.html": "",
+ "content/base/test/test_CSP_evalscript_getCRMFRequest.html": "bug 824652",
  "content/base/test/test_csp_redirects.html": "TIMED_OUT",
  "content/base/test/test_fileapi_slice.html": "bug 775227",
  "content/base/test/test_mozfiledataurl.html": "TIMED_OUT",
  "content/base/test/test_mixed_content_blocker.html": "TIMED_OUT, SSL_REQUIRED",
  "content/base/test/test_mixed_content_blocker_bug803225.html": "TIMED_OUT, SSL_REQUIRED",
  "content/base/test/test_mixed_content_blocker_frameNavigation.html": "TIMED_OUT, SSL_REQUIRED",
  "content/base/test/test_plugin_freezing.html": "CLICK_TO_PLAY",
  "content/base/test/test_object.html": "",