Bug 671389 - Part 1: Implement CSP sandbox directive r=ckerschb,smaug
authorDeian Stefan <deian@cs.stanford.edu>
Tue, 03 Feb 2015 23:40:00 +0100
changeset 256414 cd3e227df9dc718c1bb96f0f339c25e13895a6a5
parent 256413 cec0b3b4403a4ab2d1af5f6732d327372f934a18
child 256415 8d6021f66c491f8ac0de45ed27344ad32cf49bd4
push id721
push userjlund@mozilla.com
push dateTue, 21 Apr 2015 23:03:33 +0000
treeherdermozilla-release@d27c9211ebb3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersckerschb, smaug
bugs671389
milestone38.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 671389 - Part 1: Implement CSP sandbox directive r=ckerschb,smaug
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/base/nsDocument.cpp
dom/base/nsSandboxFlags.h
dom/interfaces/security/nsIContentSecurityPolicy.idl
dom/locales/en-US/chrome/security/csp.properties
dom/security/nsCSPContext.cpp
dom/security/nsCSPParser.cpp
dom/security/nsCSPParser.h
dom/security/nsCSPUtils.cpp
dom/security/nsCSPUtils.h
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -1236,54 +1236,88 @@ nsContentUtils::GetParserService()
     if (NS_FAILED(rv)) {
       sParserService = nullptr;
     }
   }
 
   return sParserService;
 }
 
+static nsIAtom** sSandboxFlagAttrs[] = {
+  &nsGkAtoms::allowsameorigin,     // SANDBOXED_ORIGIN
+  &nsGkAtoms::allowforms,          // SANDBOXED_FORMS
+  &nsGkAtoms::allowscripts,        // SANDBOXED_SCRIPTS | SANDBOXED_AUTOMATIC_FEATURES
+  &nsGkAtoms::allowtopnavigation,  // SANDBOXED_TOPLEVEL_NAVIGATION
+  &nsGkAtoms::allowpointerlock,    // SANDBOXED_POINTER_LOCK
+  &nsGkAtoms::allowpopups          // SANDBOXED_AUXILIARY_NAVIGATION
+};
+
+static const uint32_t sSandboxFlagValues[] = {
+  SANDBOXED_ORIGIN,                                 // allow-same-origin
+  SANDBOXED_FORMS,                                  // allow-forms
+  SANDBOXED_SCRIPTS | SANDBOXED_AUTOMATIC_FEATURES, // allow-scripts
+  SANDBOXED_TOPLEVEL_NAVIGATION,                    // allow-top-navigation
+  SANDBOXED_POINTER_LOCK,                           // allow-pointer-lock
+  SANDBOXED_AUXILIARY_NAVIGATION                    // allow-popups
+};
+
 /**
  * A helper function that parses a sandbox attribute (of an <iframe> or
  * a CSP directive) and converts it to the set of flags used internally.
  *
- * @param sandboxAttr   the sandbox attribute
- * @return              the set of flags (0 if sandboxAttr is null)
+ * @param aSandboxAttr  the sandbox attribute
+ * @return              the set of flags (SANDBOXED_NONE if aSandboxAttr is null)
  */
 uint32_t
-nsContentUtils::ParseSandboxAttributeToFlags(const nsAttrValue* sandboxAttr)
+nsContentUtils::ParseSandboxAttributeToFlags(const nsAttrValue* aSandboxAttr)
 {
   // No sandbox attribute, no sandbox flags.
-  if (!sandboxAttr) { return 0; }
+  if (!aSandboxAttr) { return SANDBOXED_NONE; }
 
   //  Start off by setting all the restriction flags.
   uint32_t out = SANDBOXED_NAVIGATION
                | SANDBOXED_AUXILIARY_NAVIGATION
                | SANDBOXED_TOPLEVEL_NAVIGATION
                | SANDBOXED_PLUGINS
                | SANDBOXED_ORIGIN
                | SANDBOXED_FORMS
                | SANDBOXED_SCRIPTS
                | SANDBOXED_AUTOMATIC_FEATURES
                | SANDBOXED_POINTER_LOCK
                | SANDBOXED_DOMAIN;
 
-// Macro for updating the flag according to the keywords
-#define IF_KEYWORD(atom, flags) \
-  if (sandboxAttr->Contains(nsGkAtoms::atom, eIgnoreCase)) { out &= ~(flags); }
-
-  IF_KEYWORD(allowsameorigin, SANDBOXED_ORIGIN)
-  IF_KEYWORD(allowforms,  SANDBOXED_FORMS)
-  IF_KEYWORD(allowscripts, SANDBOXED_SCRIPTS | SANDBOXED_AUTOMATIC_FEATURES)
-  IF_KEYWORD(allowtopnavigation, SANDBOXED_TOPLEVEL_NAVIGATION)
-  IF_KEYWORD(allowpointerlock, SANDBOXED_POINTER_LOCK)
-  IF_KEYWORD(allowpopups, SANDBOXED_AUXILIARY_NAVIGATION)
+  MOZ_ASSERT(ArrayLength(sSandboxFlagAttrs) == ArrayLength(sSandboxFlagValues),
+             "Lengths of SandboxFlagAttrs and SandboxFlagvalues do not match");
+
+  // For each flag: if it's in the attribute, update the (out) flag
+  for (uint32_t i = 0; i <  ArrayLength(sSandboxFlagAttrs); i++) {
+    if (aSandboxAttr->Contains(*sSandboxFlagAttrs[i], eIgnoreCase)) {
+        out &= ~(sSandboxFlagValues[i]);
+    }
+  }
 
   return out;
-#undef IF_KEYWORD
+}
+
+/**
+ * A helper function that checks if a string matches (case-insensitive) a valid
+ * sandbox flag.
+ *
+ * @param aFlag  the potential sandbox flag
+ * @return       true if the flag is a sandbox flag
+ */
+bool
+nsContentUtils::IsValidSandboxFlag(const nsAString& aFlag)
+{
+  for (uint32_t i = 0; i < ArrayLength(sSandboxFlagAttrs); i++) {
+    if (EqualsIgnoreASCIICase(nsDependentAtomString(*sSandboxFlagAttrs[i]), aFlag)) {
+      return true;
+    }
+  }
+  return false;
 }
 
 nsIBidiKeyboard*
 nsContentUtils::GetBidiKeyboard()
 {
   if (!sBidiKeyboard) {
     nsresult rv = CallGetService("@mozilla.org/widget/bidikeyboard;1", &sBidiKeyboard);
     if (NS_FAILED(rv)) {
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -827,20 +827,29 @@ public:
   static nsresult GetLocalizedString(PropertiesFile aFile,
                                      const char* aKey,
                                      nsXPIDLString& aResult);
 
   /**
    * A helper function that parses a sandbox attribute (of an <iframe> or
    * a CSP directive) and converts it to the set of flags used internally.
    *
-   * @param sandboxAttr   the sandbox attribute
-   * @return              the set of flags (0 if sandboxAttr is null)
+   * @param aSandboxAttr  the sandbox attribute
+   * @return              the set of flags (SANDBOXED_NONE if aSandboxAttr is null)
    */
-  static uint32_t ParseSandboxAttributeToFlags(const nsAttrValue* sandboxAttr);
+  static uint32_t ParseSandboxAttributeToFlags(const nsAttrValue* aSandboxAttr);
+
+  /**
+   * A helper function that checks if a string matches a valid sandbox
+   * flag.
+   *
+   * @param aFlag  the potential sandbox flag
+   * @return       true if the flag is a sandbox flag
+   */
+  static bool IsValidSandboxFlag(const nsAString& aFlag);
 
 
   /**
    * Fill (with the parameters given) the localized string named |aKey| in
    * properties file |aFile|.
    */
 private:
   static nsresult FormatLocalizedString(PropertiesFile aFile,
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -2859,17 +2859,17 @@ nsDocument::InitCSP(nsIChannel* aChannel
     httpChannel->GetResponseHeader(
         NS_LITERAL_CSTRING("content-security-policy-report-only"),
         tCspROHeaderValue);
   }
   NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
   NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
 
   // Figure out if we need to apply an app default CSP or a CSP from an app manifest
-  nsIPrincipal* principal = NodePrincipal();
+  nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
 
   uint16_t appStatus = principal->GetAppStatus();
   bool applyAppDefaultCSP = false;
   bool applyAppManifestCSP = false;
 
   nsAutoString appManifestCSP;
   nsAutoString appDefaultCSP;
   if (appStatus != nsIPrincipal::APP_STATUS_NOT_INSTALLED) {
@@ -3031,21 +3031,40 @@ nsDocument::InitCSP(nsIChannel* aChannel
 #endif
     }
 
     // Referrer Policy is set separately for the speculative parser in
     // nsHTMLDocument::StartDocumentLoad() so there's nothing to do here for
     // speculative loads.
   }
 
+  // ----- Set sandbox flags according to CSP header
+  // The document may already have some sandbox flags set (e.g., if the
+  // document is an iframe with the sandbox attribute set).  If we have a CSP
+  // sandbox directive, intersect the CSP sandbox flags with the existing
+  // flags.  This corresponds to the _least_ permissive policy.
+  uint32_t cspSandboxFlags = SANDBOXED_NONE;
+  rv = csp->GetCSPSandboxFlags(&cspSandboxFlags);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mSandboxFlags |= cspSandboxFlags;
+
+  if (cspSandboxFlags & SANDBOXED_ORIGIN) {
+    // If the new CSP sandbox flags do not have the allow-same-origin flag
+    // reset the document principal to a null principal
+    principal = do_CreateInstance("@mozilla.org/nullprincipal;1");
+    SetPrincipal(principal);
+  }
+
+
   rv = principal->SetCsp(csp);
   NS_ENSURE_SUCCESS(rv, rv);
 #ifdef PR_LOGGING
   PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-         ("Inserted CSP into principal %p", principal));
+         ("Inserted CSP into principal %p", principal.get()));
 #endif
 
   return NS_OK;
 }
 
 void
 nsDocument::StopDocumentLoad()
 {
--- a/dom/base/nsSandboxFlags.h
+++ b/dom/base/nsSandboxFlags.h
@@ -7,16 +7,21 @@
  * Constant flags that describe how a document is sandboxed according to the
  * HTML5 spec.
  */
 
 #ifndef nsSandboxFlags_h___
 #define nsSandboxFlags_h___
 
 /**
+ * This constant denotes the lack of a sandbox attribute/directive.
+ */
+const unsigned long SANDBOXED_NONE  = 0x0;
+
+/**
  * This flag prevents content from navigating browsing contexts other than
  * itself, browsing contexts nested inside it, the top-level browsing context
  * and browsing contexts that it has opened.
  * As it is always on for sandboxed browsing contexts, it is used implicitly
  * within the code by checking that the overall flags are non-zero.
  * It is only uesd directly when the sandbox flags are initially set up.
  */
 const unsigned long SANDBOXED_NAVIGATION  = 0x1;
--- a/dom/interfaces/security/nsIContentSecurityPolicy.idl
+++ b/dom/interfaces/security/nsIContentSecurityPolicy.idl
@@ -15,17 +15,17 @@ interface nsIURI;
  * nsIContentSecurityPolicy
  * Describes an XPCOM component used to model and enforce CSPs.  Instances of
  * this class may have multiple policies within them, but there should only be
  * one of these per document/principal.
  */
 
 typedef unsigned short CSPDirective;
 
-[scriptable, uuid(68434447-b816-4473-a731-efc4f6d59902)]
+[scriptable, uuid(9454a677-5342-4220-8154-e619410e07e7)]
 interface nsIContentSecurityPolicy : nsISerializable
 {
   /**
    * Directives supported by Content Security Policy.  These are enums for
    * the CSPDirective type.
    * The NO_DIRECTIVE entry is  used for checking default permissions and
    * returning failure when asking CSP which directive to check.
    *
@@ -43,16 +43,17 @@ interface nsIContentSecurityPolicy : nsI
   const unsigned short FONT_SRC_DIRECTIVE         = 8;
   const unsigned short CONNECT_SRC_DIRECTIVE      = 9;
   const unsigned short REPORT_URI_DIRECTIVE       = 10;
   const unsigned short FRAME_ANCESTORS_DIRECTIVE  = 11;
   const unsigned short REFLECTED_XSS_DIRECTIVE    = 12;
   const unsigned short BASE_URI_DIRECTIVE         = 13;
   const unsigned short FORM_ACTION_DIRECTIVE      = 14;
   const unsigned short REFERRER_DIRECTIVE         = 15;
+  const unsigned short SANDBOX_DIRECTIVE          = 16;
 
   /**
    * Accessor method for a read-only string version of the policy at a given
    * index.
    */
   AString getPolicy(in unsigned long index);
 
   /**
@@ -258,16 +259,27 @@ interface nsIContentSecurityPolicy : nsI
    *    directives that don't fall-back.
    * @return
    *    Whether or not the provided URI is allowed by CSP under the given
    *    directive. (block the pending operation if false).
    */
   boolean permits(in nsIURI aURI, in CSPDirective aDir, in boolean aSpecific);
 
   /**
+   * Delegate method called by the service when the protected document is loaded.
+   * Returns the intersection of all the sandbox flags contained in
+   * CSP policies. This is the most restricting sandbox policy.
+   * See nsSandboxFlags.h for the possible flags.
+   *
+   * @return
+   *    sandbox flags or SANDBOXED_NONE if no sandbox directive exists
+   */
+  uint32_t getCSPSandboxFlags();
+
+  /**
    * Delegate method called by the service when sub-elements of the protected
    * document are being loaded.  Given a bit of information about the request,
    * decides whether or not the policy is satisfied.
    *
    * Calls to this may trigger violation reports when queried, so
    * this value should not be cached.
    */
   short shouldLoad(in nsContentPolicyType aContentType,
--- a/dom/locales/en-US/chrome/security/csp.properties
+++ b/dom/locales/en-US/chrome/security/csp.properties
@@ -45,16 +45,19 @@ inlineScriptBlocked = An attempt to exec
 # inline style refers to CSS code that is embedded into the HTML document.
 inlineStyleBlocked = An attempt to apply inline style sheets has been blocked
 # LOCALIZATION NOTE (scriptFromStringBlocked):
 # eval is a name and should not be localized.
 scriptFromStringBlocked = An attempt to call JavaScript from a string (by calling a function like eval) has been blocked
 # LOCALIZATION NOTE (hostNameMightBeKeyword):
 # %1$S is the hostname in question and %2$S is the keyword
 hostNameMightBeKeyword = Interpreting %1$S as a hostname, not a keyword. If you intended this to be a keyword, use '%2$S' (wrapped in single quotes).
+# LOCALIZATION NOTE (ignoringReportOnlyDirective):
+# %1$S is the directive that is ignore in report-only mode.
+ignoringReportOnlyDirective = Ignoring sandbox directive when delivered in a report-only policy '%1$S'.
 # LOCALIZATION NOTE (notSupportingDirective):
 # directive is not supported (e.g. 'reflected-xss')
 notSupportingDirective = Not supporting directive '%1$S'. Directive and values will be ignored.
 
 # CSP Errors:
 # LOCALIZATION NOTE (couldntParseInvalidSource):
 # %1$S is the source that could not be parsed
 couldntParseInvalidSource = Couldn't parse invalid source %1$S
@@ -65,8 +68,11 @@ couldntParseInvalidHost = Couldn't parse
 # %1$S is the string source
 couldntParseScheme = Couldn't parse scheme in %1$S
 # LOCALIZATION NOTE (couldntParsePort):
 # %1$S is the string source
 couldntParsePort = Couldn't parse port in %1$S
 # LOCALIZATION NOTE (duplicateDirective):
 # %1$S is the name of the duplicate directive
 duplicateDirective = Duplicate %1$S directives detected.  All but the first instance will be ignored.
+# LOCALIZATION NOTE (couldntParseInvalidSandboxFlag):
+# %1$S is the option that could not be understood
+couldntParseInvalidSandboxFlag = Couldn't parse invalid sandbox flag %1$S
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -32,16 +32,17 @@
 #include "nsNullPrincipal.h"
 #include "nsIContentPolicy.h"
 #include "nsSupportsPrimitives.h"
 #include "nsThreadUtils.h"
 #include "nsString.h"
 #include "prlog.h"
 #include "mozilla/dom/CSPReportBinding.h"
 #include "mozilla/net/ReferrerPolicy.h"
+#include "nsSandboxFlags.h"
 
 using namespace mozilla;
 
 #if defined(PR_LOGGING)
 static PRLogModuleInfo *
 GetCspContextLog()
 {
   static PRLogModuleInfo *gCspContextPRLog;
@@ -1181,16 +1182,59 @@ nsCSPContext::Permits(nsIURI* aURI,
                   spec.get(), aDir,
                   *outPermits ? "allow" : "deny"));
   }
 #endif
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsCSPContext::GetCSPSandboxFlags(uint32_t* aOutSandboxFlags)
+{
+  if (aOutSandboxFlags == nullptr) {
+    return NS_ERROR_FAILURE;
+  }
+  *aOutSandboxFlags = SANDBOXED_NONE;
+
+  for (uint32_t i = 0; i < mPolicies.Length(); i++) {
+    uint32_t flags = mPolicies[i]->getSandboxFlags();
+
+    // current policy doesn't have sandbox flag, check next policy
+    if (!flags) {
+      continue;
+    }
+
+    // current policy has sandbox flags, if the policy is in
+    // enforcement-mode (i.e., not report-only) set these flags
+    // and check for policies with more restrictions
+    if (!mPolicies[i]->getReportOnlyFlag()) {
+      *aOutSandboxFlags |= flags;
+    } else {
+      // sandbox directive is ignored in report-only mode, warn about
+      // it and continue the loop checking for an enforcement-mode policy
+      nsAutoString policy;
+      mPolicies[i]->toString(policy);
+
+      CSPCONTEXTLOG(("nsCSPContext::ShouldSandbox, report only policy, ignoring sandbox in: %s",
+                      policy.get()));
+
+      const char16_t* params[] = { policy.get() };
+      CSP_LogLocalizedStr(MOZ_UTF16("ignoringReportOnlyDirective"),
+                          params, ArrayLength(params),
+                          EmptyString(),
+                          EmptyString(),
+                          0, 0,
+                          nsIScriptError::warningFlag,
+                          "CSP", mInnerWindowID);
+    }
+  }
+  return NS_OK;
+}
+
 /* ========== CSPViolationReportListener implementation ========== */
 
 NS_IMPL_ISUPPORTS(CSPViolationReportListener, nsIStreamListener, nsIRequestObserver, nsISupports);
 
 CSPViolationReportListener::CSPViolationReportListener()
 {
 }
 
--- a/dom/security/nsCSPParser.cpp
+++ b/dom/security/nsCSPParser.cpp
@@ -10,16 +10,17 @@
 #include "nsIConsoleService.h"
 #include "nsIScriptError.h"
 #include "nsIStringBundle.h"
 #include "nsNetUtil.h"
 #include "nsReadableUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsUnicharUtils.h"
 #include "mozilla/net/ReferrerPolicy.h"
+#include "nsContentUtils.h"
 
 using namespace mozilla;
 
 #if defined(PR_LOGGING)
 static PRLogModuleInfo*
 GetCspParserLog()
 {
   static PRLogModuleInfo* gCspParserPRLog;
@@ -907,16 +908,49 @@ nsCSPParser::reportURIList(nsTArray<nsCS
     }
 
     // Create new nsCSPReportURI and append to the list.
     nsCSPReportURI* reportURI = new nsCSPReportURI(uri);
     outSrcs.AppendElement(reportURI);
   }
 }
 
+/* Helper function for parsing sandbox flags. This function solely
+ * concatenates all the source list tokens (the sandbox flags) so the
+ * attribute parser (nsContentUtils::ParseSandboxAttributeToFlags) can
+ * use them.
+ */
+void
+nsCSPParser::sandboxFlagList(nsTArray<nsCSPBaseSrc*>& outSrcs)
+{
+  nsAutoString flags;
+
+  // remember, srcs start at index 1
+  for (uint32_t i = 1; i < mCurDir.Length(); i++) {
+    mCurToken = mCurDir[i];
+
+    CSPPARSERLOG(("nsCSPParser::sandboxFlagList, mCurToken: %s, mCurValue: %s",
+                 NS_ConvertUTF16toUTF8(mCurToken).get(),
+                 NS_ConvertUTF16toUTF8(mCurValue).get()));
+
+    if (!nsContentUtils::IsValidSandboxFlag(mCurToken)) {
+      const char16_t* params[] = { mCurToken.get() };
+      logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidSandboxFlag",
+                               params, ArrayLength(params));
+      continue;
+    }
+    flags.Append(mCurToken);
+    if (i != mCurDir.Length() - 1) {
+      flags.AppendASCII(" ");
+    }
+  }
+  nsCSPSandboxFlags* sandboxFlags = new nsCSPSandboxFlags(flags);
+  outSrcs.AppendElement(sandboxFlags);
+}
+
 // directive-value = *( WSP / <VCHAR except ";" and ","> )
 void
 nsCSPParser::directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs)
 {
   CSPPARSERLOG(("nsCSPParser::directiveValue"));
 
   // The tokenzier already generated an array in the form of
   // [ name, src, src, ... ], no need to parse again, but
@@ -928,16 +962,23 @@ nsCSPParser::directiveValue(nsTArray<nsC
 
   // special case handling of the referrer directive (since it doesn't contain
   // source lists)
   if (CSP_IsDirective(mCurDir[0], nsIContentSecurityPolicy::REFERRER_DIRECTIVE)) {
     referrerDirectiveValue();
     return;
   }
 
+  // For the sandbox flag the source list is a list of flags, so we're
+  // special casing this directive
+  if (CSP_IsDirective(mCurDir[0], nsIContentSecurityPolicy::SANDBOX_DIRECTIVE)) {
+    sandboxFlagList(outSrcs);
+    return;
+  }
+
   // Otherwise just forward to sourceList
   sourceList(outSrcs);
 }
 
 // directive-name = 1*( ALPHA / DIGIT / "-" )
 nsCSPDirective*
 nsCSPParser::directiveName()
 {
--- a/dom/security/nsCSPParser.h
+++ b/dom/security/nsCSPParser.h
@@ -123,24 +123,25 @@ class nsCSPParser {
     nsCSPHashSrc*   hashSource();
     nsCSPHostSrc*   appHost(); // helper function to support app specific hosts
     nsCSPHostSrc*   host();
     bool            hostChar();
     bool            schemeChar();
     bool            port();
     bool            path(nsCSPHostSrc* aCspHost);
 
-    bool subHost();                                       // helper function to parse subDomains
-    bool atValidUnreservedChar();                         // helper function to parse unreserved
-    bool atValidSubDelimChar();                           // helper function to parse sub-delims
-    bool atValidPctEncodedChar();                         // helper function to parse pct-encoded
-    bool subPath(nsCSPHostSrc* aCspHost);                 // helper function to parse paths
-    void reportURIList(nsTArray<nsCSPBaseSrc*>& outSrcs); // helper function to parse report-uris
-    void percentDecodeStr(const nsAString& aEncStr,       // helper function to percent-decode
+    bool subHost();                                         // helper function to parse subDomains
+    bool atValidUnreservedChar();                           // helper function to parse unreserved
+    bool atValidSubDelimChar();                             // helper function to parse sub-delims
+    bool atValidPctEncodedChar();                           // helper function to parse pct-encoded
+    bool subPath(nsCSPHostSrc* aCspHost);                   // helper function to parse paths
+    void reportURIList(nsTArray<nsCSPBaseSrc*>& outSrcs);   // helper function to parse report-uris
+    void percentDecodeStr(const nsAString& aEncStr,         // helper function to percent-decode
                           nsAString& outDecStr);
+    void sandboxFlagList(nsTArray<nsCSPBaseSrc*>& outSrcs); // helper function to parse sandbox flags
 
     inline bool atEnd()
     {
       return mCurChar >= mEndChar;
     }
 
     inline bool accept(char16_t aSymbol)
     {
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -7,16 +7,19 @@
 #include "nsDebug.h"
 #include "nsIConsoleService.h"
 #include "nsICryptoHash.h"
 #include "nsIScriptError.h"
 #include "nsIServiceManager.h"
 #include "nsIStringBundle.h"
 #include "nsNetUtil.h"
 #include "nsReadableUtils.h"
+#include "nsContentUtils.h"
+#include "nsAttrValue.h"
+#include "nsSandboxFlags.h"
 
 #if defined(PR_LOGGING)
 static PRLogModuleInfo*
 GetCspUtilsLog()
 {
   static PRLogModuleInfo* gCspUtilsPRLog;
   if (!gCspUtilsPRLog)
     gCspUtilsPRLog = PR_NewLogModule("CSPUtils");
@@ -667,16 +670,33 @@ nsCSPReportURI::toString(nsAString& outS
   nsAutoCString spec;
   nsresult rv = mReportURI->GetSpec(spec);
   if (NS_FAILED(rv)) {
     return;
   }
   outStr.AppendASCII(spec.get());
 }
 
+/* ===== nsCSPSandboxFlags ===================== */
+
+nsCSPSandboxFlags::nsCSPSandboxFlags(const nsAString& aFlags)
+  : mFlags(aFlags)
+{
+}
+
+nsCSPSandboxFlags::~nsCSPSandboxFlags()
+{
+}
+
+void
+nsCSPSandboxFlags::toString(nsAString& outStr) const
+{
+  outStr.Append(mFlags);
+}
+
 /* ===== nsCSPDirective ====================== */
 
 nsCSPDirective::nsCSPDirective(CSPDirective aDirective)
 {
   mDirective = aDirective;
 }
 
 nsCSPDirective::~nsCSPDirective()
@@ -969,8 +989,31 @@ nsCSPPolicy::getReportURIs(nsTArray<nsSt
 {
   for (uint32_t i = 0; i < mDirectives.Length(); i++) {
     if (mDirectives[i]->equals(nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
       mDirectives[i]->getReportURIs(outReportURIs);
       return;
     }
   }
 }
+
+/*
+ * Helper function that returns the underlying bit representation of
+ * sandbox flags. The function returns SANDBOXED_NONE if there is no
+ * sandbox directives.
+ */
+uint32_t
+nsCSPPolicy::getSandboxFlags() const
+{
+  nsAutoString flags;
+  for (uint32_t i = 0; i < mDirectives.Length(); i++) {
+    if (mDirectives[i]->equals(nsIContentSecurityPolicy::SANDBOX_DIRECTIVE)) {
+      flags.Truncate();
+      mDirectives[i]->toString(flags);
+
+      nsAttrValue attr;
+      attr.ParseAtomArray(flags);
+
+      return nsContentUtils::ParseSandboxAttributeToFlags(&attr);
+    }
+  }
+  return SANDBOXED_NONE;
+}
--- a/dom/security/nsCSPUtils.h
+++ b/dom/security/nsCSPUtils.h
@@ -69,17 +69,18 @@ static const char* CSPStrDirectives[] = 
   "frame-src",       // FRAME_SRC_DIRECTIVE
   "font-src",        // FONT_SRC_DIRECTIVE
   "connect-src",     // CONNECT_SRC_DIRECTIVE
   "report-uri",      // REPORT_URI_DIRECTIVE
   "frame-ancestors", // FRAME_ANCESTORS_DIRECTIVE
   "reflected-xss",   // REFLECTED_XSS_DIRECTIVE
   "base-uri",        // BASE_URI_DIRECTIVE
   "form-action",     // FORM_ACTION_DIRECTIVE
-  "referrer"         // REFERRER_DIRECTIVE
+  "referrer",        // REFERRER_DIRECTIVE
+  "sandbox",         // SANDBOX_DIRECTIVE
 };
 
 inline const char* CSP_CSPDirectiveToString(CSPDirective aDir)
 {
   return CSPStrDirectives[static_cast<uint32_t>(aDir)];
 }
 
 inline CSPDirective CSP_StringToCSPDirective(const nsAString& aDir)
@@ -263,16 +264,29 @@ class nsCSPReportURI : public nsCSPBaseS
     virtual ~nsCSPReportURI();
 
     void toString(nsAString& outStr) const;
 
   private:
     nsCOMPtr<nsIURI> mReportURI;
 };
 
+/* =============== nsCSPSandboxFlag ============ */
+
+class nsCSPSandboxFlags : public nsCSPBaseSrc {
+  public:
+    explicit nsCSPSandboxFlags(const nsAString& aFlags);
+    virtual ~nsCSPSandboxFlags();
+
+    void toString(nsAString& outStr) const;
+
+  private:
+    nsString mFlags;
+};
+
 /* =============== nsCSPDirective ============= */
 
 class nsCSPDirective {
   public:
     nsCSPDirective();
     explicit nsCSPDirective(CSPDirective aDirective);
     virtual ~nsCSPDirective();
 
@@ -344,15 +358,17 @@ class nsCSPPolicy {
     void getDirectiveStringForContentType(nsContentPolicyType aContentType,
                                           nsAString& outDirective) const;
 
     void getDirectiveAsString(CSPDirective aDir, nsAString& outDirective) const;
 
     inline uint32_t getNumDirectives() const
       { return mDirectives.Length(); }
 
+    uint32_t getSandboxFlags() const;
+
   private:
     nsTArray<nsCSPDirective*> mDirectives;
     bool                      mReportOnly;
     nsString                  mReferrerPolicy;
 };
 
 #endif /* nsCSPUtils_h___ */