Bug 515458 - "(CSP) Implement frame ancestor check" [r=jst ui-r=johnath]
authorSid Stamm <sstamm@mozilla.com>
Fri, 05 Feb 2010 14:08:27 -0600
changeset 37926 0b3cd1b80f3c40c5d95e39f52c09937100da319d
parent 37925 d4b4fa6cb3b9df005fc6250fa86a52fb10484aab
child 37927 3aceac0922028019959fe274835d583f1c282bd1
push id11502
push userreed@reedloden.com
push dateFri, 05 Feb 2010 20:09:32 +0000
treeherdermozilla-central@0b3cd1b80f3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst, johnath
bugs515458
milestone1.9.3a2pre
Bug 515458 - "(CSP) Implement frame ancestor check" [r=jst ui-r=johnath]
browser/locales/en-US/chrome/overrides/appstrings.properties
browser/locales/en-US/chrome/overrides/netError.dtd
content/base/public/nsContentErrors.h
content/base/src/nsDocument.cpp
content/base/test/Makefile.in
content/base/test/file_CSP_frameancestors.sjs
content/base/test/file_CSP_frameancestors_main.html
content/base/test/file_CSP_frameancestors_main.js
content/base/test/test_CSP_frameancestors.html
docshell/base/nsDocShell.cpp
docshell/resources/content/netError.xhtml
dom/locales/en-US/chrome/appstrings.properties
dom/locales/en-US/chrome/netError.dtd
--- a/browser/locales/en-US/chrome/overrides/appstrings.properties
+++ b/browser/locales/en-US/chrome/overrides/appstrings.properties
@@ -57,8 +57,9 @@ unsafeContentType=The page you are tryin
 externalProtocolTitle=External Protocol Request
 externalProtocolPrompt=An external application must be launched to handle %1$S: links.\n\n\nRequested link:\n\n%2$S\n\nApplication: %3$S\n\n\nIf you were not expecting this request it may be an attempt to exploit a weakness in that other program. Cancel this request unless you are sure it is not malicious.\n
 #LOCALIZATION NOTE (externalProtocolUnknown): The following string is shown if the application name can't be determined
 externalProtocolUnknown=<Unknown>
 externalProtocolChkMsg=Remember my choice for all links of this type.
 externalProtocolLaunchBtn=Launch application
 malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences.
 phishingBlocked=The web site at %S has been reported as a web forgery designed to trick users into sharing personal or financial information.
+cspFrameAncestorBlocked=This page has a content security policy that prevents it from being embedded in this way.
--- a/browser/locales/en-US/chrome/overrides/netError.dtd
+++ b/browser/locales/en-US/chrome/overrides/netError.dtd
@@ -158,16 +158,19 @@ be temporary, and you can try again late
 ">
 
 <!ENTITY phishingBlocked.title "Suspected Web Forgery!">
 <!ENTITY phishingBlocked.longDesc "
 <p>Entering any personal information on this page may result in identity theft or other fraud.</p>
 <p>These types of web forgeries are used in scams known as phishing attacks, in which fraudulent web pages and emails are used to imitate sources you may trust.</p>
 ">
 
+<!ENTITY cspFrameAncestorBlocked.title "Blocked by Content Security Policy">
+<!ENTITY cspFrameAncestorBlocked.longDesc "<p>&brandShortName; prevented this page from loading in this way because the page has a content security policy that disallows it.</p>">
+
 <!ENTITY securityOverride.linkText "Or you can add an exception…">
 <!ENTITY securityOverride.getMeOutOfHereButton "Get me out of here!">
 <!ENTITY securityOverride.exceptionButtonLabel "Add Exception…">
 
 <!-- LOCALIZATION NOTE (securityOverride.warningText) - Do not translate the
 contents of the <xul:button> tags.  The only language content is the label= field,
 which uses strings already defined above. The button is included here (instead of
 netError.xhtml) because it exposes functionality specific to firefox. -->
--- a/content/base/public/nsContentErrors.h
+++ b/content/base/public/nsContentErrors.h
@@ -82,9 +82,13 @@
   NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_CONTENT, 12)
 
 #define NS_FINDBROADCASTER_FOUND \
   NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_CONTENT, 13)
 
 #define NS_FINDBROADCASTER_AWAIT_OVERLAYS \
   NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_CONTENT, 14)
 
+/* Error codes for CSP */
+#define NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION \
+  NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY, 99)
+
 #endif // nsContentErrors_h___
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -2340,16 +2340,35 @@ nsDocument::InitCSP()
       {
         PR_LOG(gCspPRLog, PR_LOG_DEBUG, 
                ("CSP refined, policy: \"%s\"",
                 NS_ConvertUTF16toUTF8(cspHeaderValue).get()));
       }
 #endif
     }
 
+    // Check for frame-ancestor violation
+    nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocumentContainer);
+    if (docShell) {
+        PRBool safeAncestry = false;
+
+        // PermitsAncestry sends violation reports when necessary
+        rv = mCSP->PermitsAncestry(docShell, &safeAncestry);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (!safeAncestry) {
+#ifdef PR_LOGGING
+            PR_LOG(gCspPRLog, PR_LOG_DEBUG, 
+                   ("CSP doesn't like frame's ancestry, not loading."));
+#endif
+            // stop!  ERROR page!
+            mChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
+        }
+    }
+
     //Copy into principal
     nsIPrincipal* principal = GetPrincipal();
 
     if (principal) {
         principal->SetCsp(mCSP);
 #ifdef PR_LOGGING
         PR_LOG(gCspPRLog, PR_LOG_DEBUG, 
                 ("Inserted CSP into principal %p", principal));
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -332,16 +332,20 @@ include $(topsrcdir)/config/rules.mk
 		file_bug503481.sjs \
 		test_bug503481b.html \
 		file_bug503481b_inner.html \
 		test_viewport_scroll.html \
 		test_CSP.html \
 		file_CSP.sjs \
 		file_CSP_main.html \
 		file_CSP_main.js \
+ 		test_CSP_frameancestors.html \
+ 		file_CSP_frameancestors.sjs \
+ 		file_CSP_frameancestors_main.html \
+ 		file_CSP_frameancestors_main.js \
 		test_bug540854.html \
 		bug540854.sjs \
 		$(NULL)
 
 # Disabled; see bug 492181
 #		test_plugin_freezing.html
 
 # Disabled for now. Mochitest isn't reliable enough for these.
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_frameancestors.sjs
@@ -0,0 +1,57 @@
+// SJS file for CSP frame ancestor mochitests
+function handleRequest(request, response)
+{
+  var query = {};
+  request.queryString.split('&').forEach(function (val) {
+    var [name, value] = val.split('=');
+    query[name] = unescape(value);
+  });
+
+  var isPreflight = request.method == "OPTIONS";
+
+
+  //avoid confusing cache behaviors
+  response.setHeader("Cache-Control", "no-cache", false);
+
+  // grab the desired policy from the query, and then serve a page
+  if (query['csp'])
+    response.setHeader("X-Content-Security-Policy", 
+                        unescape(query['csp']),
+                        false);
+  if (query['scriptedreport']) {
+    // spit back a script that records that the page loaded
+    response.setHeader("Content-Type", "text/javascript", false);
+    response.write('netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");');
+    if (query['double'])
+      response.write('window.parent.parent.parent.frameLoaded("' + query['scriptedreport'] + '", ' +
+                                                       'window.location.toString());');
+    else
+      response.write('window.parent.parent.frameLoaded("' + query['scriptedreport'] + '", ' +
+                                                'window.location.toString());');
+  } else if (query['internalframe']) {
+    // spit back an internal iframe (one that might be blocked)
+    response.setHeader("Content-Type", "text/html", false);
+    response.write('<html><head>');
+    if (query['double'])
+      response.write('<script src="file_CSP_frameancestors.sjs?double=1&scriptedreport=' + query['testid'] + '"></script>');
+    else 
+      response.write('<script src="file_CSP_frameancestors.sjs?scriptedreport=' + query['testid'] + '"></script>');
+    response.write('</head><body>');
+    response.write(unescape(query['internalframe']));
+    response.write('</body></html>');
+  } else if (query['externalframe']) {
+    // spit back an internal iframe (one that won't be blocked, and probably
+    // has no CSP)
+    response.setHeader("Content-Type", "text/html", false);
+    response.write('<html><head>');
+    response.write('</head><body>');
+    response.write(unescape(query['externalframe']));
+    response.write('</body></html>');
+  } else {
+    // default case: error.
+    response.setHeader("Content-Type", "text/html", false);
+    response.write('<html><body>');
+    response.write("ERROR: not sure what to serve.");
+    response.write('</body></html>');
+  }
+}
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_frameancestors_main.html
@@ -0,0 +1,44 @@
+<html>
+  <head>
+    <title>CSP frame ancestors tests</title>
+
+    <!-- this page shouldn't have a CSP, just the sub-pages. -->
+    <script src='file_CSP_frameancestors_main.js'></script>
+
+  </head>
+  <body>
+    
+<!-- These iframes will get populated by the attached javascript. -->
+<tt>  aa_allow:   /* innermost frame allows a */</tt><br/>
+<iframe id='aa_allow'></iframe><br/>
+
+<tt>  aa_block:     /* innermost frame denies a */</tt><br/>
+<iframe id='aa_block'></iframe><br/>
+
+<tt>  ab_allow:     /* innermost frame allows a */</tt><br/>
+<iframe id='ab_allow'></iframe><br/>
+
+<tt>  ab_block:     /* innermost frame denies a */</tt><br/>
+<iframe id='ab_block'></iframe><br/>
+
+<tt>  aba_allow:    /* innermost frame allows b,a */</tt><br/>
+<iframe id='aba_allow'></iframe><br/>
+
+<tt>  aba_block:    /* innermost frame denies b */</tt><br/>
+<iframe id='aba_block'></iframe><br/>
+
+<tt>  aba2_block:   /* innermost frame denies a */</tt><br/>
+<iframe id='aba2_block'></iframe><br/>
+
+<tt>  abb_allow:    /* innermost frame allows b,a */</tt><br/>
+<iframe id='abb_allow'></iframe><br/>
+
+<tt>  abb_block:    /* innermost frame denies b */</tt><br/>
+<iframe id='abb_block'></iframe><br/>
+
+<tt>  abb2_block:   /* innermost frame denies a */</tt><br/>
+<iframe id='abb2_block'></iframe><br/>
+
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_frameancestors_main.js
@@ -0,0 +1,65 @@
+// Script to populate the test frames in the frame ancestors mochitest.
+//
+function setupFrames() {
+
+  var $ = function(v) { return document.getElementById(v); }
+  var base = {
+        self: '/tests/content/base/test/file_CSP_frameancestors.sjs',
+        a: 'http://localhost:8888/tests/content/base/test/file_CSP_frameancestors.sjs',
+        b: 'http://example.com/tests/content/base/test/file_CSP_frameancestors.sjs'
+  };
+
+  var host = { a: 'http://localhost:8888', b: 'http://example.com:80' };
+
+  var innerframeuri = null;
+  var elt = null;
+
+  elt = $('aa_allow');
+  elt.src = base.a + "?testid=aa_allow&internalframe=aa_a&csp=" + 
+            escape("allow 'none'; frame-ancestors " + host.a + "; script-src 'self'");
+
+  elt = $('aa_block');
+  elt.src = base.a + "?testid=aa_block&internalframe=aa_b&csp=" + 
+            escape("allow 'none'; frame-ancestors 'none'; script-src 'self'");
+
+  elt = $('ab_allow');
+  elt.src = base.b + "?testid=ab_allow&internalframe=ab_a&csp=" +
+            escape("allow 'none'; frame-ancestors " + host.a + "; script-src 'self'");
+
+  elt = $('ab_block');
+  elt.src = base.b + "?testid=ab_block&internalframe=ab_b&csp=" +
+            escape("allow 'none'; frame-ancestors 'none'; script-src 'self'");
+
+   /* .... two-level framing */
+  elt = $('aba_allow');
+  innerframeuri = base.a + "?testid=aba_allow&double=1&internalframe=aba_a&csp=" +
+                  escape("allow 'none'; frame-ancestors " + host.a + " " + host.b + "; script-src 'self'");
+  elt.src = base.b + "?externalframe=" + escape('<iframe src="' + innerframeuri + '"></iframe>');
+
+  elt = $('aba_block');
+  innerframeuri = base.a + "?testid=aba_allow&double=1&internalframe=aba_b&csp=" +
+                  escape("allow 'none'; frame-ancestors " + host.a + "; script-src 'self'");
+  elt.src = base.b + "?externalframe=" + escape('<iframe src="' + innerframeuri + '"></iframe>');
+
+  elt = $('aba2_block');
+  innerframeuri = base.a + "?testid=aba_allow&double=1&internalframe=aba2_b&csp=" +
+                  escape("allow 'none'; frame-ancestors " + host.b + "; script-src 'self'");
+  elt.src = base.b + "?externalframe=" + escape('<iframe src="' + innerframeuri + '"></iframe>');
+
+  elt = $('abb_allow');
+  innerframeuri = base.b + "?testid=abb_allow&double=1&internalframe=abb_a&csp=" +
+                  escape("allow 'none'; frame-ancestors " + host.a + " " + host.b + "; script-src 'self'");
+  elt.src = base.b + "?externalframe=" + escape('<iframe src="' + innerframeuri + '"></iframe>');
+
+  elt = $('abb_block');
+  innerframeuri = base.b + "?testid=abb_allow&double=1&internalframe=abb_b&csp=" +
+                  escape("allow 'none'; frame-ancestors " + host.a + "; script-src 'self'");
+  elt.src = base.b + "?externalframe=" + escape('<iframe src="' + innerframeuri + '"></iframe>');
+
+  elt = $('abb2_block');
+  innerframeuri = base.b + "?testid=abb_allow&double=1&internalframe=abb2_b&csp=" +
+                  escape("allow 'none'; frame-ancestors " + host.b + "; script-src 'self'");
+  elt.src = base.b + "?externalframe=" + escape('<iframe src="' + innerframeuri + '"></iframe>');
+}
+
+window.addEventListener('load', setupFrames, false);
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_CSP_frameancestors.html
@@ -0,0 +1,120 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Content Security Policy Frame Ancestors directive</title>
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <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>
+<script class="testbody" type="text/javascript">
+
+var path = "/tests/content/base/test/";
+
+// These are test results: -1 means it hasn't run, 
+// true/false is the pass/fail result.
+var framesThatShouldLoad = {
+  aa_allow: -1,    /* innermost frame allows a */
+  //aa_block: -1,    /* innermost frame denies a */
+  ab_allow: -1,    /* innermost frame allows a */
+  //ab_block: -1,    /* innermost frame denies a */
+  aba_allow: -1,   /* innermost frame allows b,a */
+  //aba_block: -1,   /* innermost frame denies b */
+  //aba2_block: -1,  /* innermost frame denies a */
+  abb_allow: -1,   /* innermost frame allows b,a */
+  //abb_block: -1,   /* innermost frame denies b */
+  //abb2_block: -1,  /* innermost frame denies a */
+};
+
+var expectedViolationsLeft = 6;
+
+// This is used to watch the blocked data bounce off CSP and allowed data 
+// get sent out to the wire.
+function examiner() {
+  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+  this.obsvc = Components.classes['@mozilla.org/observer-service;1']
+                        .getService(Components.interfaces.nsIObserverService);
+  this.obsvc.addObserver(this, "csp-on-violate-policy", false);
+}
+examiner.prototype  = {
+  observe: function(subject, topic, data) {
+    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+    // subject should be an nsURI, and should be either allowed or blocked.
+    if(!subject.QueryInterface) return;
+      
+    if (topic === "csp-on-violate-policy") {
+      //these were blocked... record that they were blocked
+      var uri = subject.QueryInterface(Components.interfaces.nsIURI);
+      window.frameBlocked(uri.asciiSpec, data);
+    }
+  },
+
+  // must eventually call this to remove the listener, 
+  // or mochitests might get borked.
+  remove: function() {
+    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+    this.obsvc.removeObserver(this, "csp-on-violate-policy");
+  }
+}
+
+// called when a frame is loaded
+// -- if it's not enumerated above, it should not load!
+var frameLoaded = function(testname, uri) {
+  //test already complete.... forget it... remember the first result.
+  if (window.framesThatShouldLoad[testname] != -1)
+    return;
+
+  if (typeof window.framesThatShouldLoad[testname] === 'undefined') {
+    // uh-oh, we're not expecting this frame to load!
+    ok(false, testname + ' framed site should not have loaded: ' + uri);
+  } else {
+    framesThatShouldLoad[testname] = true;
+    ok(true, testname + ' framed site when allowed by csp (' + uri + ')');
+  }
+  checkTestResults();
+}
+
+// called when a frame is blocked
+// -- we can't determine *which* frame was blocked, but at least we can count them
+var frameBlocked = function(uri, policy) {
+  ok(true, 'a CSP policy blocked frame from being loaded: ' + policy);
+  expectedViolationsLeft--;
+  checkTestResults();
+}
+
+
+// Check to see if all the tests have run
+var checkTestResults = function() {
+  // if any test is incomplete, keep waiting
+  for (var v in framesThatShouldLoad)
+    if(framesThatShouldLoad[v] == -1)
+      return;
+
+  if (expectedViolationsLeft > 0)
+    return;
+
+  // ... otherwise, finish
+  window.examiner.remove();
+  SimpleTest.finish();
+}
+
+//////////////////////////////////////////////////////////////////////
+// set up and go
+window.examiner = new examiner();
+SimpleTest.waitForExplicitFinish();
+
+// 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_frameancestors_main.html';
+
+</script>
+</pre>
+</body>
+</html>
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -3617,16 +3617,21 @@ nsDocShell::DisplayLoadError(nsresult aE
         NS_ENSURE_ARG_POINTER(aURI);
         // Get the host
         nsCAutoString host;
         aURI->GetHost(host);
         CopyUTF8toUTF16(host, formatStrs[0]);
         formatStrCount = 1;
         error.AssignLiteral("netTimeout");
     }
+    else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError) {
+        // CSP error
+        cssClass.AssignLiteral("neterror");
+        error.AssignLiteral("cspFrameAncestorBlocked");
+    }
     else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
         nsCOMPtr<nsINSSErrorsService> nsserr =
             do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
 
         PRUint32 errorClass;
         if (!nsserr ||
             NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
           errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
--- a/docshell/resources/content/netError.xhtml
+++ b/docshell/resources/content/netError.xhtml
@@ -308,16 +308,17 @@
         <h1 id="et_deniedPortAccess">&deniedPortAccess.title;</h1>
         <h1 id="et_proxyResolveFailure">&proxyResolveFailure.title;</h1>
         <h1 id="et_proxyConnectFailure">&proxyConnectFailure.title;</h1>
         <h1 id="et_contentEncodingError">&contentEncodingError.title;</h1>
         <h1 id="et_unsafeContentType">&unsafeContentType.title;</h1>
         <h1 id="et_nssFailure2">&nssFailure2.title;</h1>
         <h1 id="et_nssBadCert">&nssBadCert.title;</h1>
         <h1 id="et_malwareBlocked">&malwareBlocked.title;</h1>
+        <h1 id="et_cspFrameAncestorBlocked">&cspFrameAncestorBlocked.title;</h1>
       </div>
       <div id="errorDescriptionsContainer">
         <div id="ed_generic">&generic.longDesc;</div>
         <div id="ed_dnsNotFound">&dnsNotFound.longDesc;</div>
         <div id="ed_fileNotFound">&fileNotFound.longDesc;</div>
         <div id="ed_malformedURI">&malformedURI.longDesc;</div>
         <div id="ed_protocolNotFound">&protocolNotFound.longDesc;</div>
         <div id="ed_connectionFailure">&connectionFailure.longDesc;</div>
@@ -330,16 +331,17 @@
         <div id="ed_deniedPortAccess">&deniedPortAccess.longDesc;</div>
         <div id="ed_proxyResolveFailure">&proxyResolveFailure.longDesc;</div>
         <div id="ed_proxyConnectFailure">&proxyConnectFailure.longDesc;</div>
         <div id="ed_contentEncodingError">&contentEncodingError.longDesc;</div>
         <div id="ed_unsafeContentType">&unsafeContentType.longDesc;</div>
         <div id="ed_nssFailure2">&nssFailure2.longDesc;</div>
         <div id="ed_nssBadCert">&nssBadCert.longDesc2;</div>
         <div id="ed_malwareBlocked">&malwareBlocked.longDesc;</div>
+        <div id="ed_cspFrameAncestorBlocked">&cspFrameAncestorBlocked.longDesc;</div>
       </div>
     </div>
 
     <!-- PAGE CONTAINER (for styling purposes only) -->
     <div id="errorPageContainer">
     
       <!-- Error Title -->
       <div id="errorTitle">
--- a/dom/locales/en-US/chrome/appstrings.properties
+++ b/dom/locales/en-US/chrome/appstrings.properties
@@ -56,8 +56,9 @@ unsafeContentType=The page you are tryin
 externalProtocolTitle=External Protocol Request
 externalProtocolPrompt=An external application must be launched to handle %1$S: links.\n\n\nRequested link:\n\n%2$S\n\nApplication: %3$S\n\n\nIf you were not expecting this request it may be an attempt to exploit a weakness in that other program. Cancel this request unless you are sure it is not malicious.\n
 #LOCALIZATION NOTE (externalProtocolUnknown): The following string is shown if the application name can't be determined
 externalProtocolUnknown=<Unknown>
 externalProtocolChkMsg=Remember my choice for all links of this type.
 externalProtocolLaunchBtn=Launch application
 malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences.
 phishingBlocked=The web site at %S has been reported as a web forgery designed to trick users into sharing personal or financial information.
+cspFrameAncestorBlocked=This page has a content security policy that prevents it from being embedded in this way.
--- a/dom/locales/en-US/chrome/netError.dtd
+++ b/dom/locales/en-US/chrome/netError.dtd
@@ -75,13 +75,16 @@
 ">
 
 <!ENTITY phishingBlocked.title "Suspected Web Forgery!">
 <!ENTITY phishingBlocked.longDesc "
 <p>Entering any personal information on this page may result in identity theft or other fraud.</p>
 <p>These types of web forgeries are used in scams known as phishing attacks, in which fraudulent web pages and emails are used to imitate sources you may trust.</p>
 ">
 
+<!ENTITY cspFrameAncestorBlocked.title "Blocked by Content Security Policy">
+<!ENTITY cspFrameAncestorBlocked.longDesc "<p>The browser prevented this page from loading in this way because the page has a content security policy that disallows it.</p>">
+
 <!-- Include app-specific error messages - do not change this in localization!
      Some applications might override netErrorApp.dtd with their specific version,
      this inclusion needs to be intact for that approach to work correctly. -->
 <!ENTITY % netErrorAppDTD SYSTEM "chrome://global/locale/netErrorApp.dtd">
 %netErrorAppDTD;