tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FeaturePolicyUtils.cpp (9369B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "FeaturePolicyUtils.h"
      8 
      9 #include "ipc/IPCMessageUtilsSpecializations.h"
     10 #include "mozilla/StaticPrefs_dom.h"
     11 #include "mozilla/dom/BrowsingContext.h"
     12 #include "mozilla/dom/Document.h"
     13 #include "mozilla/dom/FeaturePolicyViolationReportBody.h"
     14 #include "mozilla/dom/PermissionMessageUtils.h"
     15 #include "mozilla/dom/ReportingUtils.h"
     16 #include "nsContentUtils.h"
     17 #include "nsIOService.h"
     18 #include "nsJSUtils.h"
     19 
     20 namespace mozilla {
     21 namespace dom {
     22 
     23 struct FeatureMap {
     24  const char* mFeatureName;
     25  FeaturePolicyUtils::FeaturePolicyValue mDefaultAllowList;
     26 };
     27 
     28 /*
     29 * IMPORTANT: Do not change this list without review from a DOM peer _AND_ a
     30 * DOM Security peer!
     31 */
     32 static FeatureMap sSupportedFeatures[] = {
     33    {"camera", FeaturePolicyUtils::FeaturePolicyValue::eSelf},
     34    {"geolocation", FeaturePolicyUtils::FeaturePolicyValue::eSelf},
     35    {"microphone", FeaturePolicyUtils::FeaturePolicyValue::eSelf},
     36    {"display-capture", FeaturePolicyUtils::FeaturePolicyValue::eSelf},
     37    {"fullscreen", FeaturePolicyUtils::FeaturePolicyValue::eSelf},
     38    {"web-share", FeaturePolicyUtils::FeaturePolicyValue::eSelf},
     39    {"gamepad", FeaturePolicyUtils::FeaturePolicyValue::eAll},
     40    {"publickey-credentials-create",
     41     FeaturePolicyUtils::FeaturePolicyValue::eSelf},
     42    {"publickey-credentials-get",
     43     FeaturePolicyUtils::FeaturePolicyValue::eSelf},
     44    {"speaker-selection", FeaturePolicyUtils::FeaturePolicyValue::eSelf},
     45    {"storage-access", FeaturePolicyUtils::FeaturePolicyValue::eAll},
     46    {"screen-wake-lock", FeaturePolicyUtils::FeaturePolicyValue::eSelf},
     47 };
     48 
     49 /*
     50 * This is experimental features list, which is disabled by default by pref
     51 * dom.security.featurePolicy.experimental.enabled.
     52 */
     53 static FeatureMap sExperimentalFeatures[] = {
     54    // We don't support 'autoplay' for now, because it would be overwrote by
     55    // 'user-gesture-activation' policy. However, we can still keep it in the
     56    // list as we might start supporting it after we use different autoplay
     57    // policy.
     58    {"autoplay", FeaturePolicyUtils::FeaturePolicyValue::eAll},
     59    {"encrypted-media", FeaturePolicyUtils::FeaturePolicyValue::eAll},
     60    {"midi", FeaturePolicyUtils::FeaturePolicyValue::eSelf},
     61    {"payment", FeaturePolicyUtils::FeaturePolicyValue::eAll},
     62    {"document-domain", FeaturePolicyUtils::FeaturePolicyValue::eAll},
     63    {"vr", FeaturePolicyUtils::FeaturePolicyValue::eAll},
     64    // https://immersive-web.github.io/webxr/#feature-policy
     65    {"xr-spatial-tracking", FeaturePolicyUtils::FeaturePolicyValue::eSelf},
     66 };
     67 
     68 /* static */
     69 bool FeaturePolicyUtils::IsExperimentalFeature(const nsAString& aFeatureName) {
     70  uint32_t numFeatures =
     71      (sizeof(sExperimentalFeatures) / sizeof(sExperimentalFeatures[0]));
     72  for (uint32_t i = 0; i < numFeatures; ++i) {
     73    if (aFeatureName.LowerCaseEqualsASCII(
     74            sExperimentalFeatures[i].mFeatureName)) {
     75      return true;
     76    }
     77  }
     78 
     79  return false;
     80 }
     81 
     82 /* static */
     83 bool FeaturePolicyUtils::IsSupportedFeature(const nsAString& aFeatureName) {
     84  uint32_t numFeatures =
     85      (sizeof(sSupportedFeatures) / sizeof(sSupportedFeatures[0]));
     86  for (uint32_t i = 0; i < numFeatures; ++i) {
     87    if (aFeatureName.LowerCaseEqualsASCII(sSupportedFeatures[i].mFeatureName)) {
     88      return true;
     89    }
     90  }
     91 
     92  return StaticPrefs::dom_security_featurePolicy_experimental_enabled() &&
     93         IsExperimentalFeature(aFeatureName);
     94 }
     95 
     96 /* static */
     97 void FeaturePolicyUtils::ForEachFeature(
     98    const std::function<void(const char*)>& aCallback) {
     99  uint32_t numFeatures =
    100      (sizeof(sSupportedFeatures) / sizeof(sSupportedFeatures[0]));
    101  for (uint32_t i = 0; i < numFeatures; ++i) {
    102    aCallback(sSupportedFeatures[i].mFeatureName);
    103  }
    104 
    105  if (StaticPrefs::dom_security_featurePolicy_experimental_enabled()) {
    106    numFeatures =
    107        (sizeof(sExperimentalFeatures) / sizeof(sExperimentalFeatures[0]));
    108    for (uint32_t i = 0; i < numFeatures; ++i) {
    109      aCallback(sExperimentalFeatures[i].mFeatureName);
    110    }
    111  }
    112 }
    113 
    114 /* static */ FeaturePolicyUtils::FeaturePolicyValue
    115 FeaturePolicyUtils::DefaultAllowListFeature(const nsAString& aFeatureName) {
    116  uint32_t numFeatures =
    117      (sizeof(sSupportedFeatures) / sizeof(sSupportedFeatures[0]));
    118  for (uint32_t i = 0; i < numFeatures; ++i) {
    119    if (aFeatureName.LowerCaseEqualsASCII(sSupportedFeatures[i].mFeatureName)) {
    120      return sSupportedFeatures[i].mDefaultAllowList;
    121    }
    122  }
    123 
    124  if (StaticPrefs::dom_security_featurePolicy_experimental_enabled()) {
    125    numFeatures =
    126        (sizeof(sExperimentalFeatures) / sizeof(sExperimentalFeatures[0]));
    127    for (uint32_t i = 0; i < numFeatures; ++i) {
    128      if (aFeatureName.LowerCaseEqualsASCII(
    129              sExperimentalFeatures[i].mFeatureName)) {
    130        return sExperimentalFeatures[i].mDefaultAllowList;
    131      }
    132    }
    133  }
    134 
    135  return FeaturePolicyValue::eNone;
    136 }
    137 
    138 static bool IsSameOriginAsTop(Document* aDocument) {
    139  MOZ_ASSERT(aDocument);
    140 
    141  BrowsingContext* browsingContext = aDocument->GetBrowsingContext();
    142  if (!browsingContext) {
    143    return false;
    144  }
    145 
    146  nsPIDOMWindowOuter* topWindow = browsingContext->Top()->GetDOMWindow();
    147  if (!topWindow) {
    148    // If we don't have a DOMWindow, We are not in same origin.
    149    return false;
    150  }
    151 
    152  Document* topLevelDocument = topWindow->GetExtantDoc();
    153  if (!topLevelDocument) {
    154    return false;
    155  }
    156 
    157  return NS_SUCCEEDED(
    158      nsContentUtils::CheckSameOrigin(topLevelDocument, aDocument));
    159 }
    160 
    161 /* static */
    162 bool FeaturePolicyUtils::IsFeatureUnsafeAllowedAll(
    163    Document* aDocument, const nsAString& aFeatureName) {
    164  MOZ_ASSERT(aDocument);
    165 
    166  if (!aDocument->IsHTMLDocument()) {
    167    return false;
    168  }
    169 
    170  FeaturePolicy* policy = aDocument->FeaturePolicy();
    171  MOZ_ASSERT(policy);
    172 
    173  return policy->HasFeatureUnsafeAllowsAll(aFeatureName) &&
    174         !policy->IsSameOriginAsSrc(aDocument->NodePrincipal()) &&
    175         !policy->AllowsFeatureExplicitlyInAncestorChain(
    176             aFeatureName, policy->DefaultOrigin()) &&
    177         !IsSameOriginAsTop(aDocument);
    178 }
    179 
    180 /* static */
    181 bool FeaturePolicyUtils::IsFeatureAllowed(Document* aDocument,
    182                                          const nsAString& aFeatureName) {
    183  MOZ_ASSERT(aDocument);
    184 
    185  // Skip apply features in experimental phase
    186  if (!StaticPrefs::dom_security_featurePolicy_experimental_enabled() &&
    187      IsExperimentalFeature(aFeatureName)) {
    188    return true;
    189  }
    190 
    191  FeaturePolicy* policy = aDocument->FeaturePolicy();
    192  MOZ_ASSERT(policy);
    193 
    194  if (policy->AllowsFeatureInternal(aFeatureName, policy->DefaultOrigin())) {
    195    return true;
    196  }
    197 
    198  ReportViolation(aDocument, aFeatureName);
    199  return false;
    200 }
    201 
    202 /* static */
    203 void FeaturePolicyUtils::ReportViolation(Document* aDocument,
    204                                         const nsAString& aFeatureName) {
    205  MOZ_ASSERT(aDocument);
    206 
    207  nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI();
    208  if (NS_WARN_IF(!uri)) {
    209    return;
    210  }
    211 
    212  // Strip the URL of any possible username/password and make it ready to be
    213  // presented in the UI.
    214  nsCOMPtr<nsIURI> exposableURI = net::nsIOService::CreateExposableURI(uri);
    215  nsAutoCString spec;
    216  nsresult rv = exposableURI->GetSpec(spec);
    217  if (NS_WARN_IF(NS_FAILED(rv))) {
    218    return;
    219  }
    220  JSContext* cx = nsContentUtils::GetCurrentJSContext();
    221  if (NS_WARN_IF(!cx)) {
    222    return;
    223  }
    224 
    225  Nullable<int32_t> lineNumber;
    226  Nullable<int32_t> columnNumber;
    227  auto loc = JSCallingLocation::Get();
    228  if (loc) {
    229    lineNumber.SetValue(static_cast<int32_t>(loc.mLine));
    230    columnNumber.SetValue(static_cast<int32_t>(loc.mColumn));
    231  }
    232 
    233  nsPIDOMWindowInner* window = aDocument->GetInnerWindow();
    234  if (NS_WARN_IF(!window)) {
    235    return;
    236  }
    237 
    238  RefPtr<FeaturePolicyViolationReportBody> body =
    239      new FeaturePolicyViolationReportBody(window->AsGlobal(), aFeatureName,
    240                                           loc.FileName(), lineNumber,
    241                                           columnNumber, u"enforce"_ns);
    242 
    243  ReportingUtils::Report(window->AsGlobal(), nsGkAtoms::featurePolicyViolation,
    244                         u"default"_ns, NS_ConvertUTF8toUTF16(spec), body);
    245 }
    246 
    247 }  // namespace dom
    248 }  // namespace mozilla
    249 
    250 namespace IPC {
    251 
    252 void ParamTraits<mozilla::dom::FeaturePolicyInfo>::Write(
    253    MessageWriter* aWriter, const mozilla::dom::FeaturePolicyInfo& aParam) {
    254  WriteParam(aWriter, aParam.mInheritedDeniedFeatureNames);
    255  WriteParam(aWriter, aParam.mAttributeEnabledFeatureNames);
    256  WriteParam(aWriter, aParam.mDeclaredString);
    257  WriteParam(aWriter, aParam.mDefaultOrigin);
    258  WriteParam(aWriter, aParam.mSelfOrigin);
    259  WriteParam(aWriter, aParam.mSrcOrigin);
    260 }
    261 
    262 bool ParamTraits<mozilla::dom::FeaturePolicyInfo>::Read(
    263    MessageReader* aReader, mozilla::dom::FeaturePolicyInfo* aResult) {
    264  return ReadParam(aReader, &aResult->mInheritedDeniedFeatureNames) &&
    265         ReadParam(aReader, &aResult->mAttributeEnabledFeatureNames) &&
    266         ReadParam(aReader, &aResult->mDeclaredString) &&
    267         ReadParam(aReader, &aResult->mDefaultOrigin) &&
    268         ReadParam(aReader, &aResult->mSelfOrigin) &&
    269         ReadParam(aReader, &aResult->mSrcOrigin);
    270 }
    271 
    272 }  // namespace IPC