New xss event format
authorRiccardo Pelizzi <r.pelizzi@gmail.com>
Sat, 11 Aug 2012 12:46:07 -0400
changeset 49 244c359a659a0af8305c13bb711e0f27065f12a0
parent 48 65aed6a94e01176386ce311c74a1aa4505142fdb
child 50 f1e86f56ee2795aa0596a442eef3d8a8dd7ff4a5
push id29
push userr.pelizzi@gmail.com
push dateSat, 11 Aug 2012 16:46:17 +0000
New xss event format
xssfilter
--- a/xssfilter
+++ b/xssfilter
@@ -102,67 +102,71 @@ diff --git a/b2g/locales/en-US/chrome/ov
  <!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.warningContent) - Do not translate the
 diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
 --- a/browser/base/content/browser.js
 +++ b/browser/base/content/browser.js
-@@ -633,16 +633,49 @@ var gPopupBlockerObserver = {
+@@ -633,16 +633,53 @@ var gPopupBlockerObserver = {
    dontShowMessage: function ()
    {
      var showMessage = gPrefService.getBoolPref("privacy.popups.showBrowserMessage");
      gPrefService.setBoolPref("privacy.popups.showBrowserMessage", !showMessage);
      gBrowser.getNotificationBox().removeCurrentNotification();
    }
  };
  
 +const gXSSObserver = {
 +
 +  observe: function (aSubject, aTopic, aData)
 +  {
 +
 +    if (!gPrefService.getBoolPref("security.xssfilter.displayWarning"))
 +      return;
 +
++    // parse incoming xss array
++    aSubject.QueryInterface(Ci.nsIArray);
++    var policy = aSubject.queryElementAt(0, Ci.nsISupportsCString).data;
++    var content = aSubject.queryElementAt(1, Ci.nsISupportsCString).data;
++    var url = aSubject.queryElementAt(2, Ci.nsISupportsCString).data;
++    var blockMode = aSubject.queryElementAt(3, Ci.nsISupportsPRBool).data;
++
++    // if it is a block mode event, do not display the warning
++    if (blockMode)
++      return;
++
 +    var nb = gBrowser.getNotificationBox();
 +    const priority = nb.PRIORITY_WARNING_MEDIUM;
 +
-+    var msg = aSubject.QueryInterface(Ci.nsISupportsCString).data;
-+    var details = aData;
-+
-+    // if it is a block mode event, do not display the warning
-+    if (aData.indexOf(";;block") != -1)
-+      return;
-+
 +    var buttons = [{
-+      label: 'View Script',
++      label: 'View Unsafe Content',
 +      accessKey: 'V',
 +      popup: null,
 +      callback: function () {
-+        alert(details);
++        alert(content);
 +      }
 +    }];
 +
-+    nb.appendNotification(msg, 'popup-blocked',
-+                          'chrome://browser/skin/Info.png',
++    nb.appendNotification("The XSS Filter has detected a potential XSS attack. Type: " +
++                          policy, 'popup-blocked', 'chrome://browser/skin/Info.png',
 +                          priority, buttons);
 +  }
 +};
 +
  const gFormSubmitObserver = {
    QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]),
  
    panel: null,
  
    init: function()
    {
      this.panel = document.getElementById('invalid-form-popup');
-@@ -1250,16 +1283,17 @@ var gBrowserInit = {
+@@ -1250,16 +1287,17 @@ var gBrowserInit = {
  #endif
  
      Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
      Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false);
      Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false);
      Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
      Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
      Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
@@ -170,17 +174,17 @@ diff --git a/browser/base/content/browse
      Services.obs.addObserver(gFormSubmitObserver, "invalidformsubmit", false);
  
      BrowserOffline.init();
      OfflineApps.init();
      IndexedDBPromptHelper.init();
      gFormSubmitObserver.init();
      SocialUI.init();
      AddonManager.addAddonListener(AddonsMgrListener);
-@@ -1594,16 +1628,17 @@ var gBrowserInit = {
+@@ -1594,16 +1632,17 @@ var gBrowserInit = {
  
        Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
        Services.obs.removeObserver(gXPInstallObserver, "addon-install-disabled");
        Services.obs.removeObserver(gXPInstallObserver, "addon-install-started");
        Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
        Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
        Services.obs.removeObserver(gXPInstallObserver, "addon-install-complete");
        Services.obs.removeObserver(gFormSubmitObserver, "invalidformsubmit");
@@ -1096,17 +1100,17 @@ diff --git a/content/base/src/nsScriptLo
    request->mIsInline = true;
    request->mURI = mDocument->GetDocumentURI();
    request->mLineNo = aElement->GetScriptLineNumber();
  
 diff --git a/content/base/src/nsXSSFilter.cpp b/content/base/src/nsXSSFilter.cpp
 new file mode 100644
 --- /dev/null
 +++ b/content/base/src/nsXSSFilter.cpp
-@@ -0,0 +1,536 @@
+@@ -0,0 +1,568 @@
 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 +/* This Source Code Form is subject to the terms of the Mozilla Public
 + * License, v. 2.0. If a copy of the MPL was not distributed with this
 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 +
 +#include "prlog.h"
 +#include "nsString.h"
 +#include "nsCOMPtr.h"
@@ -1139,16 +1143,17 @@ new file mode 100644
 +#include "nsIWebNavigation.h"
 +#include "nsIDocShellTreeItem.h"
 +#include "nsContentErrors.h"
 +#include "nsXSSFilter.h"
 +#include "nsIInputStream.h"
 +#include "nsISeekableStream.h"
 +#include "nsIUploadChannel.h"
 +#include "nsIConsoleService.h"
++#include "nsIMutableArray.h"
 +
 +using namespace mozilla;
 +
 +bool nsXSSFilter::sXSSEnabled = true;
 +bool nsXSSFilter::sReportOnly = false;
 +bool nsXSSFilter::sBlockMode = false;
 +
 +#ifdef PR_LOGGING
@@ -1297,17 +1302,17 @@ new file mode 100644
 +
 +  if (!IsEnabled()) {
 +    return true;
 +  }
 +
 +  nsXSSUtils::CheckInline(aScript, GetParams());
 +
 +  if (nsXSSUtils::HasAttack(GetParams())) {
-+    NotifyViolation("Inline Script", aScript);
++    NotifyViolation(NS_LITERAL_STRING("Inline Script"), aScript, GetURI());
 +    return IsReportOnly();
 +  }
 +  return true;
 +}
 +
 +bool
 +nsXSSFilter::PermitsExternalScript(nsIURI *aURI)
 +{
@@ -1334,17 +1339,18 @@ new file mode 100644
 +  if (cache.Get(domain, &c)) {
 +    return c;
 +  }
 +
 +  nsXSSUtils::CheckExternal(aURI, GetURI(), GetParams());
 +  if (nsXSSUtils::HasAttack(GetParams())) {
 +    nsCAutoString spec;
 +    aURI->GetSpec(spec);
-+    NotifyViolation("External Script", NS_ConvertUTF8toUTF16(spec));
++    NotifyViolation(NS_LITERAL_STRING("External Script"), 
++                    NS_ConvertUTF8toUTF16(spec), GetURI());
 +    cache.Put(domain, false);
 +    return IsReportOnly();
 +  }
 +  cache.Put(domain, true);
 +  return true;
 +}
 +
 +bool
@@ -1361,17 +1367,17 @@ new file mode 100644
 +  nsAutoString escUri;
 +  nsXSSUtils::UnescapeLoop(aURI, escUri, mParentDoc);
 +  LOG_XSS_1("escaped javascript url: %s",
 +            NS_ConvertUTF16toUTF8(escUri).get());
 +
 +  nsXSSUtils::CheckInline(escUri, GetParams());
 +
 +  if (nsXSSUtils::HasAttack(GetParams())) {
-+    NotifyViolation("JS URL", aURI);
++    NotifyViolation(NS_LITERAL_STRING("JS URL"), aURI, GetURI());
 +    return IsReportOnly();
 +  }
 +  return true;
 +}
 +
 +bool
 +nsXSSFilter::PermitsEventListener(const nsAString& aScript)
 +{
@@ -1381,17 +1387,17 @@ new file mode 100644
 +
 +  if (!IsEnabled()) {
 +    return true;
 +  }
 +
 +  nsXSSUtils::CheckInline(aScript, GetParams());
 +
 +  if (nsXSSUtils::HasAttack(GetParams())) {
-+    NotifyViolation("Event Listener", aScript);
++    NotifyViolation(NS_LITERAL_STRING("Event Listener"), aScript, GetURI());
 +    return IsReportOnly();
 +  }
 +  return true;
 +}
 +
 +bool
 +nsXSSFilter::PermitsBaseElement(nsIURI *aOldURI, nsIURI* aNewURI)
 +{
@@ -1418,17 +1424,18 @@ new file mode 100644
 +  if (oldD.Equals(newD)) {
 +    return true;
 +  }
 +
 +  nsXSSUtils::CheckExternal(aNewURI, GetURI(), GetParams());
 +  if (nsXSSUtils::HasAttack(GetParams())) {
 +    nsCAutoString spec;
 +    aNewURI->GetSpec(spec);
-+    NotifyViolation("Base Element", NS_ConvertUTF8toUTF16(spec));
++    NotifyViolation(NS_LITERAL_STRING("Base Element"), 
++                    NS_ConvertUTF8toUTF16(spec), GetURI());
 +    return IsReportOnly();
 +  }
 +  return true;
 +}
 +
 +bool
 +nsXSSFilter::PermitsExternalObject(nsIURI *aURI)
 +{
@@ -1455,17 +1462,18 @@ new file mode 100644
 +  if (cache.Get(domain, &c)) {
 +    return c;
 +  }
 +
 +  nsXSSUtils::CheckExternal(aURI, GetURI(), GetParams());
 +  if (nsXSSUtils::HasAttack(GetParams())) {
 +    nsCAutoString spec;
 +    aURI->GetSpec(spec);
-+    NotifyViolation("Object", NS_ConvertUTF8toUTF16(spec));
++    NotifyViolation(NS_LITERAL_STRING("Object"),
++                    NS_ConvertUTF8toUTF16(spec), GetURI());
 +    return IsReportOnly();
 +  }
 +  return true;
 +}
 +
 +bool
 +nsXSSFilter::PermitsDataURL(nsIURI *aURI)
 +{
@@ -1480,17 +1488,18 @@ new file mode 100644
 +
 +  if (!IsEnabled()) {
 +    return true;
 +  }
 +
 +  nsXSSUtils::CheckInline(NS_ConvertUTF8toUTF16(spec), GetParams());
 +
 +  if (nsXSSUtils::HasAttack(GetParams())) {
-+    NotifyViolation("Data URL", NS_ConvertUTF8toUTF16(spec));
++    NotifyViolation(NS_LITERAL_STRING("Data URL"),
++                    NS_ConvertUTF8toUTF16(spec), GetURI());
 +    return IsReportOnly();
 +  }
 +  return true;
 +}
 +
 +bool
 +nsXSSFilter::PermitsJSAction(const nsAString& aCode)
 +{
@@ -1499,17 +1508,17 @@ new file mode 100644
 +
 +  if (!IsEnabled()) {
 +    return true;
 +  }
 +
 +  nsXSSUtils::CheckInline(aCode, GetParams());
 +
 +  if (nsXSSUtils::HasAttack(GetParams())) {
-+    NotifyViolation("JS Action", aCode);
++    NotifyViolation(NS_LITERAL_STRING("JS Action"), aCode, GetURI());
 +    return IsReportOnly();
 +  }
 +  return true;
 +}
 +
 +ParameterArray&
 +nsXSSFilter::GetParams()
 +{
@@ -1553,85 +1562,112 @@ new file mode 100644
 +}
 +
 +nsIURI*
 +nsXSSFilter::GetURI()
 +{
 +  return mParentDoc->GetDocumentURI();
 +}
 +
++DomainMap&
++nsXSSFilter::GetDomainCache()
++{
++  if (!mDomainCache.IsInitialized()) {
++    mDomainCache.Init();
++  }
++  return mDomainCache;
++}
++
 +bool nsXSSFilter::IsEnabled() { return sXSSEnabled && mIsEnabled; }
 +bool nsXSSFilter::IsBlockMode() { return sBlockMode || mBlockMode; }
 +bool nsXSSFilter::IsReportOnly() { return sReportOnly; }
 +
 +
 +class nsXSSNotifier: public nsRunnable
 +{
 +public:
-+  nsXSSNotifier(const char *policy, const nsAString& details, bool blockMode) :
-+    mPolicy(policy), mDetails(details), mBlockMode(blockMode)
++  nsXSSNotifier(const nsAString& policy, const nsAString& content, nsIURI* url, bool blockMode) :
++    mPolicy(policy), mContent(content),  mURI(url), mBlockMode(blockMode)
 +  { };
 +
 +  NS_IMETHOD Run() {
 +    LOG_XSS("Sending Observer Notification");
 +    nsCOMPtr<nsIObserverService> observerService =
 +      services::GetObserverService();
 +    if (!observerService) {
 +      return NS_OK;
 +    }
++
++    // the nsIArray will contain four parameters: violated policy,
++    // content, url and blockMode
++    nsresult rv = NS_OK;
++    nsCOMPtr<nsIMutableArray> params = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
++    NS_ENSURE_SUCCESS(rv, rv);
++
 +    nsCOMPtr<nsISupportsCString>
-+      p(do_CreateInstance("@mozilla.org/supports-cstring;1"));
-+    p->SetData(nsCAutoString(mPolicy));
-+    if (mBlockMode) {
-+      mDetails.Append(NS_LITERAL_STRING(";;block"));
-+    }
-+    observerService->NotifyObservers(p, "xss-on-violate-policy",
-+                                     PromiseFlatString(mDetails).get());
++      wrappedPolicy(do_CreateInstance("@mozilla.org/supports-cstring;1"));
++    wrappedPolicy->SetData(NS_ConvertUTF16toUTF8(mPolicy));
++
++    nsCOMPtr<nsISupportsCString>
++      wrappedContent(do_CreateInstance("@mozilla.org/supports-cstring;1"));
++    wrappedContent->SetData(NS_ConvertUTF16toUTF8(mContent));
++
++    nsCOMPtr<nsISupportsCString>
++      wrappedUrl(do_CreateInstance("@mozilla.org/supports-cstring;1"));
++    nsCAutoString spec;
++    mURI->GetSpec(spec);
++    wrappedUrl->SetData(spec);
++
++    nsCOMPtr<nsISupportsPRBool>
++      wrappedBlock(do_CreateInstance("@mozilla.org/supports-PRBool;1"));
++    wrappedBlock->SetData(mBlockMode);
++    
++    params->AppendElement(wrappedPolicy, false);
++    params->AppendElement(wrappedContent, false);
++    params->AppendElement(wrappedUrl, false);
++    params->AppendElement(wrappedBlock, false);
++    observerService->NotifyObservers(params, "xss-on-violate-policy", NULL);
 +    return NS_OK;
 +  }
 +
 +
++
 +private:
 +  ~nsXSSNotifier() { };
-+  const char* mPolicy;
-+  nsString mDetails;
++  nsString mPolicy, mContent;
++  nsIURI* mURI;
 +  bool mBlockMode;
 +};
 +
-+DomainMap&
-+nsXSSFilter::GetDomainCache()
-+{
-+  if (!mDomainCache.IsInitialized()) {
-+    mDomainCache.Init();
-+  }
-+  return mDomainCache;
-+}
-+
-+
 +nsresult
-+nsXSSFilter::NotifyViolation(const char* policy, const nsAString& content)
++nsXSSFilter::NotifyViolation(const nsAString& policy, const nsAString& content, nsIURI* url)
 +{
 +  LOG_XSS("Violation");
 +
++  nsCAutoString spec;
++  url->GetSpec(spec);
++
 +  // send to console
 +  nsCOMPtr<nsIConsoleService> aConsoleService =
 +    do_GetService( "@mozilla.org/consoleservice;1" );
 +  nsAutoString msg;
-+  msg.Assign(NS_LITERAL_STRING("XSS Violation: "));
-+  msg.Append(NS_ConvertUTF8toUTF16(policy));
++  msg.Assign(NS_LITERAL_STRING("XSS Violation on URL "));
++  msg.Append(NS_ConvertUTF8toUTF16(spec));
++  msg.Append(NS_LITERAL_STRING(": "));
++  msg.Append(policy);
 +  aConsoleService->
 +    LogStringMessage(msg.get());
 +
 +  // send to observers as xss-on-violate-policy
 +  nsCOMPtr<nsIThread> thread = do_GetMainThread();
 +  if (!thread) {
 +    LOG_XSS("Main thread unavailable");
 +    return NS_OK;
 +  }
-+  nsCOMPtr<nsIRunnable> runnable = new nsXSSNotifier(policy, content, IsBlockMode());
++  nsCOMPtr<nsIRunnable> runnable = new nsXSSNotifier(policy, content, url, IsBlockMode());
 +  thread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
 +
 +  // block the page is block mode is enabled
 +  if (IsBlockMode()) {
 +    nsCOMPtr<nsIHttpChannel> httpChannel =
 +      do_QueryInterface(mParentDoc->GetChannel());
 +    httpChannel->Cancel(NS_ERROR_XSS_BLOCK);
 +  }
@@ -1835,17 +1871,17 @@ new file mode 100644
 +   * Notifies the user of an XSS Violation. Three events are generated:
 +   * 1) message to error console
 +   * 2) notify observers of xss-on-violate-policy (Currently, there is a
 +   * pref that is used by browser.js to popup a notification. We plan
 +   * something less consipcuous for the final version)
 +   * 3) If block mode is enabled, it also navigates away the tab to an
 +   * error page.
 +   */
-+  nsresult NotifyViolation(const char* policy, const nsAString& content);
++  nsresult NotifyViolation(const nsAString& policy, const nsAString& content, nsIURI* url);
 +};
 +
 +
 +
 +
 +#endif /* nsXSSFilter_h */
 diff --git a/content/base/src/nsXSSUtils.cpp b/content/base/src/nsXSSUtils.cpp
 new file mode 100644
@@ -3951,17 +3987,17 @@ new file mode 100644
 +    return NS_OK;
 +  }
 +  return xss->Test();
 +}
 diff --git a/content/base/test/file_XSS.js b/content/base/test/file_XSS.js
 new file mode 100644
 --- /dev/null
 +++ b/content/base/test/file_XSS.js
-@@ -0,0 +1,309 @@
+@@ -0,0 +1,318 @@
 +netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 +const Cu = Components.utils;
 +const Ci = Components.interfaces;
 +const Cc = Components.classes;
 +const env = Cc["@mozilla.org/process/environment;1"].
 +  getService(Ci.nsIEnvironment);
 +// we use this for clarity
 +var GET = "GET", POST = "POST";
@@ -4118,31 +4154,40 @@ new file mode 100644
 +}
 +
 +function getObserverService() {
 +  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 +  return Cc["@mozilla.org/observer-service;1"].
 +    getService(Ci.nsIObserverService);
 +}
 +
++function parseXSSArray(xpcomarray) {
++  if (xpcomarray.length == 0)
++    return null;
++  return {policy: policy, content: content, url: url, blockMode: blockMode};
++}
++
 +const Observer = {
 +
 +  observe: function (aSubject, aTopic, aData)
 +  {
-+    // TODO: have cases checking for block mode
 +    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 +    var t = Tester.getCase();
-+    var isBlock = aData.indexOf(";;block") != -1;
++    aSubject.QueryInterface(Ci.nsIArray);
++    var policy = aSubject.queryElementAt(0, Ci.nsISupportsCString).data;
++    var content = aSubject.queryElementAt(1, Ci.nsISupportsCString).data;
++    var url = aSubject.queryElementAt(2, Ci.nsISupportsCString).data;
++    var blockMode = aSubject.queryElementAt(3, Ci.nsISupportsPRBool).data;
 +    var shouldBlock = t.attack == ATTACK_BLOCK;
 +    var shouldAttack = t.attack == ATTACK || t.attack == ATTACK_BLOCK;
 +    if (t.todo == DONE) {
-+      ok(shouldAttack && (isBlock == shouldBlock), t.method + ": " + t.description +
++      ok(shouldAttack && (blockMode == shouldBlock), t.method + ": " + t.description +
 +	 " (" + t.attack + ")");
 +    } else {
-+      todo(shouldAttack && (isBlock == shouldBlock), t.method + ": " + t.description +
++      todo(shouldAttack && (blockMode == shouldBlock), t.method + ": " + t.description +
 +	   " (" + t.attack + ")");
 +    }
 +    setTimeout("Tester.next();", 10);
 +  }
 +};
 +
 +var Tester = {
 +
@@ -5455,28 +5500,29 @@ diff --git a/mobile/xul/chrome/content/n
      </div>
  
      <!-- Error Title -->
      <div id="errorTitle">
        <h1 class="errorTitleText" />
 diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js
 --- a/modules/libpref/src/init/all.js
 +++ b/modules/libpref/src/init/all.js
-@@ -1316,16 +1316,20 @@ pref("signed.applets.codebase_principal_
+@@ -1316,16 +1316,21 @@ pref("signed.applets.codebase_principal_
  pref("security.checkloaduri", true);
  pref("security.xpconnect.plugin.unrestricted", true);
  // security-sensitive dialogs should delay button enabling. In milliseconds.
  pref("security.dialog_enable_delay", 2000);
  
  pref("security.csp.enable", true);
  pref("security.csp.debug", false);
  
 +pref("security.xssfilter.enable", true);
 +pref("security.xssfilter.reportOnly", false);
 +pref("security.xssfilter.blockMode", false);
++pref("security.xssfilter.displayWarning", true);
 +
  // Modifier key prefs: default to Windows settings,
  // menu access key = alt, accelerator key = control.
  // Use 17 for Ctrl, 18 for Alt, 224 for Meta, 91 for Win, 0 for none. Mac settings in macprefs.js
  pref("ui.key.accelKey", 17);
  pref("ui.key.menuAccessKey", 18);
  pref("ui.key.generalAccessKey", -1);