Bug 1100630 - Print Related JS-Line on CSP Violation (if any) r=ckerschb,dveditz
☠☠ backed out by 5a6a99018a64 ☠ ☠
authorSebastian Streich <sstreich@mozilla.com>
Wed, 17 Jul 2019 10:54:07 +0000
changeset 483113 8b0c5e44d937513e87b21fc0bc428b8b00adc0ea
parent 483112 f00e82b087d64124bba60b81d6f53f606beb637d
child 483114 a0c57ddf072577c513ec7f72cf9b427a0d10c826
push id90199
push userdvarga@mozilla.com
push dateWed, 17 Jul 2019 11:43:18 +0000
treeherderautoland@8b0c5e44d937 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersckerschb, dveditz
bugs1100630
milestone70.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 1100630 - Print Related JS-Line on CSP Violation (if any) r=ckerschb,dveditz Differential Revision: https://phabricator.services.mozilla.com/D31420
devtools/client/webconsole/test/mochitest/browser.ini
devtools/client/webconsole/test/mochitest/browser_webconsole_csp_violation.js
devtools/client/webconsole/test/mochitest/test-csp-violation-base-uri.html
devtools/client/webconsole/test/mochitest/test-csp-violation-base-uri.html^headers^
devtools/client/webconsole/test/mochitest/test-csp-violation-form-action.html
devtools/client/webconsole/test/mochitest/test-csp-violation-form-action.html^headers^
devtools/client/webconsole/test/mochitest/test-csp-violation-frame-ancestor-child.html
devtools/client/webconsole/test/mochitest/test-csp-violation-frame-ancestor-child.html^headers^
devtools/client/webconsole/test/mochitest/test-csp-violation-frame-ancestor-parent.html
devtools/client/webconsole/test/mochitest/test-csp-violation-frame-ancestor-parent.html^headers^
devtools/client/webconsole/test/mochitest/test-csp-violation-inline.html
devtools/client/webconsole/test/mochitest/test-csp-violation-inline.html^headers^
dom/security/nsCSPContext.cpp
--- a/devtools/client/webconsole/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/test/mochitest/browser.ini
@@ -18,16 +18,26 @@ support-files =
   test_hsts-invalid-headers.sjs
   test-autocomplete-in-stackframe.html
   test-batching.html
   test-console-trace-duplicates.html
   test-cd-iframe-child.html
   test-cd-iframe-parent.html
   test-console-api-iframe.html
   test-csp-violation.html
+  test-csp-violation-inline.html
+  test-csp-violation-inline.html^headers^
+  test-csp-violation-base-uri.html
+  test-csp-violation-base-uri.html^headers^
+  test-csp-violation-form-action.html
+  test-csp-violation-form-action.html^headers^
+  test-csp-violation-frame-ancestor-child.html^headers^
+  test-csp-violation-frame-ancestor-child.html
+  test-csp-violation-frame-ancestor-parent.html^headers^
+  test-csp-violation-frame-ancestor-parent.html
   test-cspro.html
   test-cspro.html^headers^
   test-iframe-child.html
   test-iframe-parent.html
   test-certificate-messages.html
   test-click-function-to-source.html
   test-click-function-to-source.js
   test-closure-optimized-out.html
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_csp_violation.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_csp_violation.js
@@ -3,27 +3,110 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that the Web Console CSP messages for two META policies
 // are correctly displayed. See Bug 1247459.
 
 "use strict";
 
-const TEST_URI = "data:text/html;charset=utf8,Web Console CSP violation test";
-const TEST_VIOLATION =
-  "https://example.com/browser/devtools/client/webconsole/" +
-  "test/mochitest/test-csp-violation.html";
-const CSP_VIOLATION_MSG =
-  "Content Security Policy: The page\u2019s settings " +
-  "blocked the loading of a resource at " +
-  "http://some.example.com/test.png (\u201cimg-src\u201d).";
-
 add_task(async function() {
+  const TEST_URI = "data:text/html;charset=utf8,Web Console CSP violation test";
   const hud = await openNewTabAndConsole(TEST_URI);
   hud.ui.clearOutput();
+  {
+    const TEST_VIOLATION =
+      "https://example.com/browser/devtools/client/webconsole/" +
+      "test/mochitest/test-csp-violation.html";
+    const CSP_VIOLATION_MSG =
+      "Content Security Policy: The page\u2019s settings " +
+      "blocked the loading of a resource at " +
+      "http://some.example.com/test.png (\u201cimg-src\u201d).";
+    const onRepeatedMessage = waitForRepeatedMessage(hud, CSP_VIOLATION_MSG, 2);
+    await loadDocument(TEST_VIOLATION);
+    await onRepeatedMessage;
+    ok(true, "Received expected messages");
+  }
+  hud.ui.clearOutput();
+  // Testing CSP Inline Violations
+  {
+    const TEST_VIOLATION =
+      "https://example.com/browser/devtools/client/webconsole/" +
+      "test/mochitest/test-csp-violation-inline.html";
+    const CSP_VIOLATION =
+      `Content Security Policy: The page’s settings blocked` +
+      ` the loading of a resource at inline (“style-src”).`;
+    const VIOLATION_LOCATION_HTML = "test-csp-violation-inline.html:18:1";
+    const VIOLATION_LOCATION_JS = "test-csp-violation-inline.html:14:24";
+    await loadDocument(TEST_VIOLATION);
+    // Triggering the Violation via HTML
+    let msg = await waitFor(() => findMessage(hud, CSP_VIOLATION));
+    let locationNode = msg.querySelector(".message-location");
+    info(`EXPECT ${VIOLATION_LOCATION_HTML} GOT: ${locationNode.textContent}`);
+    ok(
+      locationNode.textContent == VIOLATION_LOCATION_HTML,
+      "Printed the CSP Violation with HTML Context"
+    );
+    // Triggering the Violation via JS
+    hud.ui.clearOutput();
+    hud.jsterm.execute("window.violate()");
+    msg = await waitFor(() => findMessage(hud, CSP_VIOLATION));
+    locationNode = msg.querySelector(".message-location");
+    info(`EXPECT ${VIOLATION_LOCATION_JS} GOT: ${locationNode.textContent}`);
+    ok(
+      locationNode.textContent == VIOLATION_LOCATION_JS,
+      "Printed the CSP Violation with JS Context"
+    );
+  }
+  hud.ui.clearOutput();
+  // Testing Base URI
+  {
+    const TEST_VIOLATION =
+      "https://example.com/browser/devtools/client/webconsole/" +
+      "test/mochitest/test-csp-violation-base-uri.html";
+    const CSP_VIOLATION = `Content Security Policy: The page’s settings blocked the loading of a resource at https://evil.com/ (“base-uri”).`;
+    const VIOLATION_LOCATION = "test-csp-violation-base-uri.html:15:24";
+    await loadDocument(TEST_VIOLATION);
+    let msg = await waitFor(() => findMessage(hud, CSP_VIOLATION));
+    ok(msg, "Base-URI validation was Printed");
+    // Triggering the Violation via JS
+    hud.ui.clearOutput();
+    hud.jsterm.execute("window.violate()");
+    msg = await waitFor(() => findMessage(hud, CSP_VIOLATION));
+    const locationNode = msg.querySelector(".message-location");
+    console.log(locationNode.textContent);
+    ok(
+      locationNode.textContent == VIOLATION_LOCATION,
+      "Base-URI validation was Printed with the Responsible JS Line"
+    );
+  }
+  hud.ui.clearOutput();
+  // Testing CSP Form Action
+  {
+    const TEST_VIOLATION =
+      "https://example.com/browser/devtools/client/webconsole/" +
+      "test/mochitest/test-csp-violation-form-action.html";
+    const CSP_VIOLATION = `Content Security Policy: The page’s settings blocked the loading of a resource at https://evil.com/evil.com (“form-action”).`;
+    const VIOLATION_LOCATION = "test-csp-violation-form-action.html:14:39";
 
-  const onRepeatedMessage = waitForRepeatedMessage(hud, CSP_VIOLATION_MSG, 2);
-  await loadDocument(TEST_VIOLATION);
-  await onRepeatedMessage;
-
-  ok(true, "Received expected messages");
+    await loadDocument(TEST_VIOLATION);
+    const msg = await waitFor(() => findMessage(hud, CSP_VIOLATION));
+    const locationNode = msg.querySelector(".message-location");
+    info(`EXPECT ${VIOLATION_LOCATION} GOT: ${locationNode.textContent}`);
+    ok(
+      locationNode.textContent == VIOLATION_LOCATION,
+      "JS Line which Triggered the CSP-Form Action Violation was Printed"
+    );
+  }
+  hud.ui.clearOutput();
+  // Testing CSP Frame Ancestors Directive
+  {
+    const TEST_VIOLATION =
+      "https://example.com/browser/devtools/client/webconsole/" +
+      "test/mochitest/test-csp-violation-frame-ancestor-parent.html";
+    const CSP_VIOLATION =
+      `Content Security Policy: The page’s settings blocked` +
+      ` the loading of a resource at ${TEST_VIOLATION} (“frame-ancestors”).`;
+    await loadDocument(TEST_VIOLATION);
+    const msg = await waitFor(() => findMessage(hud, CSP_VIOLATION));
+    ok(msg, "Frame-Ancestors violation by html was printed");
+  }
 });
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/test-csp-violation-base-uri.html
@@ -0,0 +1,18 @@
+<html>
+      <head>
+          <title>CSP Base-URI Violation Test </title>
+          <base href="https://evil.com/">
+      </head>
+      <body>
+          <h1> Crashing the Base Element</h1>
+      </body>
+      <script>
+        "use strict";
+        window.violate = ()=>{
+          document.head.innerHTML = "";
+          const b = document.createElement("base");
+          b.href = "https://evil.com";
+          document.head.append(b);
+        };
+      </script>
+    </html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/test-csp-violation-base-uri.html^headers^
@@ -0,0 +1,1 @@
+Content-Security-Policy: base-uri 'self';
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/test-csp-violation-form-action.html
@@ -0,0 +1,16 @@
+<html>
+      <head>
+          <title>CSP Base-URI Violation Test </title>
+          <base href="https://evil.com/">
+      </head>
+      <body>
+          <form action="evil.com" >
+              <input type="text" value="test" name="test" />
+              <button type="submit">Submit Button</button>
+          </form>
+      </body>
+      <script>
+        "use strict";
+        document.querySelector("form").submit();
+      </script>
+    </html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/test-csp-violation-form-action.html^headers^
@@ -0,0 +1,1 @@
+Content-Security-Policy: form-action 'self';
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/test-csp-violation-frame-ancestor-child.html
@@ -0,0 +1,9 @@
+<html>
+      <head>
+          <title>CSP frame-ancestors Violation Test </title>
+          <base href="https://evil.com/">
+      </head>
+      <body>
+            <h1> This Should not be Loadable</h1>
+      </body>
+    </html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/test-csp-violation-frame-ancestor-child.html^headers^
@@ -0,0 +1,1 @@
+Content-Security-Policy: frame-ancestors 'none';
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/test-csp-violation-frame-ancestor-parent.html
@@ -0,0 +1,21 @@
+<html>
+      <head>
+          <title>CSP frame-ancestors Violation Test </title>
+          <base href="https://evil.com/">
+      </head>
+      <body>
+          <iframe src="https://example.com/browser/devtools/client/webconsole/test/mochitest/test-csp-violation-frame-ancestor-child.html">
+          </iframe>
+      </body>
+      <script>
+        "use strict";
+        window.violate = ()=>{
+          const iframe = document.querySelector("iframe");
+          const src = iframe.src;
+          iframe.src = "";
+          requestAnimationFrame(() => {
+            iframe.src = src;
+          });
+        };
+      </script>
+    </html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/test-csp-violation-frame-ancestor-parent.html^headers^
@@ -0,0 +1,1 @@
+Content-Security-Policy: form-action 'self';
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/test-csp-violation-inline.html
@@ -0,0 +1,21 @@
+ <html>
+      <head>
+        <title>CSP Inline Violations Test</title>
+
+      </head>
+      <body>
+          This Background should be neither Red nor Blue c:
+      </body>
+      <script>
+        "use strict";
+        window.violate = () =>{
+          const style = document.createElement("style");
+          style.innerHTML = "body { background-color: red; }";
+          document.head.appendChild(style);
+        };
+      </script>
+
+      <style>
+        background-color:blue;
+      </style>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/test-csp-violation-inline.html^headers^
@@ -0,0 +1,1 @@
+Content-Security-Policy: style-src 'self';
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -48,16 +48,17 @@
 #include "mozilla/net/ReferrerPolicy.h"
 #include "nsINetworkInterceptController.h"
 #include "nsSandboxFlags.h"
 #include "nsIScriptElement.h"
 #include "nsIEventTarget.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/Element.h"
 #include "nsXULAppAPI.h"
+#include "nsJSUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static LogModule* GetCspContextLog() {
   static LazyLogModule gCspContextPRLog("CSPContext");
   return gCspContextPRLog;
 }
@@ -206,29 +207,38 @@ bool nsCSPContext::permitsInternal(
         CSPCONTEXTLOG(("nsCSPContext::permitsInternal, false"));
         permits = false;
       }
 
       // Do not send a report or notify observers if this is a preload - the
       // decision may be wrong due to the inability to get the nonce, and will
       // incorrectly fail the unit tests.
       if (!aIsPreload && aSendViolationReports) {
+        uint32_t lineNumber = 0;
+        uint32_t columnNumber = 0;
+        nsAutoCString spec;
+        JSContext* cx = nsContentUtils::GetCurrentJSContext();
+        if (cx) {
+          nsJSUtils::GetCallingLocation(cx, spec, &lineNumber, &columnNumber);
+          // If GetCallingLocation fails linenumber & columnNumber are set to 0
+          // anyway so we can skip checking if that is the case.
+        }
         AsyncReportViolation(
             aTriggeringElement, aCSPEventListener,
             (aSendContentLocationInViolationReports ? aContentLocation
                                                     : nullptr),
             BlockedContentSource::eUnknown, /* a BlockedContentSource */
             aOriginalURIIfRedirect, /* in case of redirect originalURI is not
                                        null */
             violatedDirective, p,   /* policy index        */
             EmptyString(),          /* no observer subject */
-            EmptyString(),          /* no source file      */
-            EmptyString(),          /* no script sample    */
-            0,                      /* no line number      */
-            0);                     /* no column number    */
+            NS_ConvertUTF8toUTF16(spec), /* source file      */
+            EmptyString(),               /* no script sample    */
+            lineNumber,                  /* line number      */
+            columnNumber);               /*  column number    */
       }
     }
   }
 
   return permits;
 }
 
 /* ===== nsISupports implementation ========== */
@@ -482,27 +492,42 @@ void nsCSPContext::reportInlineViolation
   }
 
   // use selfURI as the sourceFile
   nsAutoCString sourceFile;
   if (mSelfURI) {
     mSelfURI->GetSpec(sourceFile);
   }
 
+  uint32_t lineNumber = aLineNumber;
+  uint32_t columnNumber = aColumnNumber;
+
+  JSContext* cx = nsContentUtils::GetCurrentJSContext();
+  if (cx) {
+    if (!nsJSUtils::GetCallingLocation(cx, sourceFile, &lineNumber,
+                                       &columnNumber)) {
+      // Get Calling Location resets line/col to 0
+      // so we reset those to the intial arguments
+      // in case it failed
+      lineNumber = aLineNumber;
+      columnNumber = aColumnNumber;
+    }
+  }
+
   AsyncReportViolation(aTriggeringElement, aCSPEventListener,
                        nullptr,                        // aBlockedURI
                        BlockedContentSource::eInline,  // aBlockedSource
                        mSelfURI,                       // aOriginalURI
                        aViolatedDirective,             // aViolatedDirective
                        aViolatedPolicyIndex,           // aViolatedPolicyIndex
                        observerSubject,                // aObserverSubject
                        NS_ConvertUTF8toUTF16(sourceFile),  // aSourceFile
                        aContent,                           // aScriptSample
-                       aLineNumber,                        // aLineNum
-                       aColumnNumber);                     // aColumnNum
+                       lineNumber,                         // aLineNum
+                       columnNumber);                      // aColumnNum
 }
 
 NS_IMETHODIMP
 nsCSPContext::GetAllowsInline(nsContentPolicyType aContentType,
                               const nsAString& aNonce, bool aParserCreated,
                               Element* aTriggeringElement,
                               nsICSPEventListener* aCSPEventListener,
                               const nsAString& aContentOfPseudoScript,