Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  IntegrityPolicy.cpp

  Sprache: C
 

/* 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 "IntegrityPolicy.h"

#include "mozilla/Logging.h"
#include "mozilla/StaticPrefs_security.h"
#include "mozilla/dom/RequestBinding.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "mozilla/net/SFV.h"
#include "nsCOMPtr.h"
#include "nsIClassInfoImpl.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
#include "nsString.h"

using namespace mozilla;

static LazyLogModule sIntegrityPolicyLogModule("IntegrityPolicy");
#define LOG(fmt, ...) \
  MOZ_LOG_FMT(sIntegrityPolicyLogModule, LogLevel::Debug, fmt, ##__VA_ARGS__)

namespace mozilla::dom {

RequestDestination ContentTypeToDestination(nsContentPolicyType aType) {
  // From SecFetch.cpp
  // https://searchfox.org/mozilla-central/rev/f1e32fa7054859d37eea8804e220dfcc7fb53b03/dom/security/SecFetch.cpp#24-32
  switch (aType) {
    case nsIContentPolicy::TYPE_INTERNAL_SCRIPT:
    case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD:
    case nsIContentPolicy::TYPE_INTERNAL_MODULE:
    case nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD:
    // We currently only support documents.
    // case nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS:
    case nsIContentPolicy::TYPE_INTERNAL_CHROMEUTILS_COMPILED_SCRIPT:
    case nsIContentPolicy::TYPE_INTERNAL_FRAME_MESSAGEMANAGER_SCRIPT:
    case nsIContentPolicy::TYPE_SCRIPT:
      return RequestDestination::Script;

    case nsIContentPolicy::TYPE_STYLESHEET:
    case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET:
    case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD:
      return RequestDestination::Style;

    default:
      return RequestDestination::_empty;
  }
}

Maybe<IntegrityPolicy::DestinationType> DOMRequestDestinationToDestinationType(
    RequestDestination aDestination) {
  switch (aDestination) {
    case RequestDestination::Script:
      return Some(IntegrityPolicy::DestinationType::Script);
    case RequestDestination::Style:
      return StaticPrefs::security_integrity_policy_stylesheet_enabled()
                 ? Some(IntegrityPolicy::DestinationType::Style)
                 : Nothing{};

    default:
      return Nothing{};
  }
}

Maybe<IntegrityPolicy::DestinationType>
IntegrityPolicy::ContentTypeToDestinationType(nsContentPolicyType aType) {
  return DOMRequestDestinationToDestinationType(
      ContentTypeToDestination(aType));
}

// https://w3c.github.io/webappsec-subresource-integrity/#integrity-policy-section
// The headers' value is a Dictionary [RFC9651], with every member-value being
// an inner list of tokens.
nsresult GetTokenValuesFromInnerList(const net::SFV::InnerListResult& aList,
                                     nsTArray<nsCString>& aValues) {
  size_t len = aList.Length();
  for (size_t i = 0; i < len; i++) {
    auto item = aList.GetItemAt(i);
    if (!item.IsValid()) {
      return NS_ERROR_FAILURE;
    }

    nsAutoCString tokenValue;
    nsresult rv = item.GetValue<net::SFV::Token>(tokenValue);
    NS_ENSURE_SUCCESS(rv, rv);

    aValues.AppendElement(tokenValue);
  }

  return NS_OK;
}

/* static */
Result<IntegrityPolicy::Sources, nsresult> ParseSources(
    const net::SFV::DictResult& aDict) {
  // sources, a list of sources, Initially empty.

  // 3. If dictionary["sources"] does not exist or if its value contains
  // "inline", append "inline" to integrityPolicy's sources.
  auto innerList = aDict.GetInnerList("sources"_ns);
  if (!innerList.IsValid()) {
    // The key doesn't exists, set it to inline as per spec.
    return IntegrityPolicy::Sources(IntegrityPolicy::SourceType::Inline);
  }

  nsTArray<nsCString> sources;
  nsresult rv = GetTokenValuesFromInnerList(innerList, sources);
  NS_ENSURE_SUCCESS(rv, Err(rv));

  IntegrityPolicy::Sources result;
  for (const auto& source : sources) {
    if (source.EqualsLiteral("inline")) {
      result += IntegrityPolicy::SourceType::Inline;
    } else {
      LOG("ParseSources: Unknown source: {}", source.get());
      // Unknown source, we don't know how to handle it
      continue;
    }
  }

  return result;
}

/* static */
Result<IntegrityPolicy::Destinations, nsresult>
IntegrityPolicy::ParseDestinations(const net::SFV::DictResult& aDict,
                                   bool aIsWAICT) {
  // blocked destinations, a list of destinations, initially empty.

  auto innerList = aDict.GetInnerList("blocked-destinations"_ns);
  if (!innerList.IsValid()) {
    // Required in WAICT.
    if (aIsWAICT) {
      return Err(NS_ERROR_FAILURE);
    }
    return IntegrityPolicy::Destinations();
  }

  nsTArray<nsCString> destinations;
  nsresult rv = GetTokenValuesFromInnerList(innerList, destinations);
  NS_ENSURE_SUCCESS(rv, Err(rv));

  IntegrityPolicy::Destinations result;
  for (const auto& destination : destinations) {
    if (destination.EqualsLiteral("script")) {
      result += IntegrityPolicy::DestinationType::Script;
    } else if (destination.EqualsLiteral("style")) {
      if (StaticPrefs::security_integrity_policy_stylesheet_enabled()) {
        result += IntegrityPolicy::DestinationType::Style;
      }
    } else if (aIsWAICT && destination.EqualsLiteral("image")) {
      result += IntegrityPolicy::DestinationType::Image;
    } else {
      LOG("ParseDestinations: Unknown destination: {}", destination.get());
      // Unknown destination, we don't know how to handle it
      continue;
    }
  }

  return result;
}

/* static */
Result<nsTArray<nsCString>, nsresult> IntegrityPolicy::ParseEndpoints(
    const net::SFV::DictResult& aDict) {
  // endpoints, a list of strings, initially empty.
  auto innerList = aDict.GetInnerList("endpoints"_ns);
  if (!innerList.IsValid()) {
    // The key doesn't exists, return empty list.
    return nsTArray<nsCString>();
  }

  nsTArray<nsCString> endpoints;
  nsresult rv = GetTokenValuesFromInnerList(innerList, endpoints);
  NS_ENSURE_SUCCESS(rv, Err(rv));

  return endpoints;
}

/* static */
// https://w3c.github.io/webappsec-subresource-integrity/#processing-an-integrity-policy
nsresult IntegrityPolicy::ParseHeaders(const nsACString& aHeader,
                                       const nsACString& aHeaderRO,
                                       IntegrityPolicy** aPolicy) {
  if (!StaticPrefs::security_integrity_policy_enabled()) {
    return NS_OK;
  }

  // 1. Let integrityPolicy be a new integrity policy struct.
  // (Our struct contains two entries, one for the enforcement header and one
  // for report-only)
  RefPtr<IntegrityPolicy> policy = new IntegrityPolicy();

  LOG("[{}] Parsing headers: enforcement='{}' report-only='{}'",
      static_cast<void*>(policy), PromiseFlatCString(aHeader).get(),
      PromiseFlatCString(aHeaderRO).get());

  for (const auto& isROHeader : {false, true}) {
    const auto& headerString = isROHeader ? aHeaderRO : aHeader;

    if (headerString.IsEmpty()) {
      LOG("[{}] No {} header.", static_cast<void*>(policy),
          isROHeader ? "report-only" : "enforcement");
      continue;
    }

    // 2. Let dictionary be the result of getting a structured field value from
    // headers given headerName and "dictionary".
    auto dict = net::SFV::ParseDict(headerString);
    if (!dict.IsValid()) {
      LOG("[{}] Failed to parse {} header.", static_cast<void*>(policy),
          isROHeader ? "report-only" : "enforcement");
      continue;
    }

    // 3. If dictionary["sources"] does not exist or if its value contains
    // "inline", append "inline" to integrityPolicy’s sources.
    auto sourcesResult = ParseSources(dict);
    if (sourcesResult.isErr()) {
      LOG("[{}] Failed to parse sources for {} header.",
          static_cast<void*>(policy),
          isROHeader ? "report-only" : "enforcement");
      continue;
    }

    // 4. If dictionary["blocked-destinations"] exists:
    auto destinationsResult = ParseDestinations(dict, /* aIsWAICT */ false);
    if (destinationsResult.isErr()) {
      LOG("[{}] Failed to parse destinations for {} header.",
          static_cast<void*>(policy),
          isROHeader ? "report-only" : "enforcement");
      continue;
    }

    // 5. If dictionary["endpoints"] exists:
    auto endpointsResult = ParseEndpoints(dict);
    if (endpointsResult.isErr()) {
      LOG("[{}] Failed to parse endpoints for {} header.",
          static_cast<void*>(policy),
          isROHeader ? "report-only" : "enforcement");
      continue;
    }

    LOG("[{}] Creating policy for {} header. sources={} destinations={} "
        "endpoints=[{}]",
        static_cast<void*>(policy), isROHeader ? "report-only" : "enforcement",
        sourcesResult.unwrap().serialize(),
        destinationsResult.unwrap().serialize(),
        fmt::join(endpointsResult.unwrap(), ", "));

    Entry entry = Entry(sourcesResult.unwrap(), destinationsResult.unwrap(),
                        endpointsResult.unwrap());
    if (isROHeader) {
      policy->mReportOnly.emplace(entry);
    } else {
      policy->mEnforcement.emplace(entry);
    }
  }

  // 6. Return integrityPolicy.
  policy.forget(aPolicy);

  LOG("[{}] Finished parsing headers.", static_cast<void*>(policy));

  return NS_OK;
}

void IntegrityPolicy::PolicyContains(DestinationType aDestination,
                                     bool* aContains, bool* aROContains) const {
  // 10. Let block be a boolean, initially false.
  *aContains = false;
  // 11. Let reportBlock be a boolean, initially false.
  *aROContains = false;

  // 12. If policy’s sources contains "inline" and policy’s blocked destinations
  // contains request’s destination, set block to true.
  if (mEnforcement && mEnforcement->mDestinations.contains(aDestination) &&
      mEnforcement->mSources.contains(SourceType::Inline)) {
    *aContains = true;
  }

  // 13. If reportPolicy’s sources contains "inline" and reportPolicy’s blocked
  // destinations contains request’s destination, set reportBlock to true.
  if (mReportOnly && mReportOnly->mDestinations.contains(aDestination) &&
      mReportOnly->mSources.contains(SourceType::Inline)) {
    *aROContains = true;
  }
}

void IntegrityPolicy::Endpoints(nsTArray<nsCString>& aEnforcement,
                                nsTArray<nsCString>& aReportOnly) const {
  if (mEnforcement) {
    aEnforcement = mEnforcement->mEndpoints.Clone();
  }
  if (mReportOnly) {
    aReportOnly = mReportOnly->mEndpoints.Clone();
  }
}

void IntegrityPolicy::ToArgs(const IntegrityPolicy* aPolicy,
                             mozilla::ipc::IntegrityPolicyArgs& aArgs) {
  aArgs.enforcement() = Nothing();
  aArgs.reportOnly() = Nothing();

  if (!aPolicy) {
    return;
  }

  if (aPolicy->mEnforcement) {
    mozilla::ipc::IntegrityPolicyEntry entry;
    entry.sources() = aPolicy->mEnforcement->mSources;
    entry.destinations() = aPolicy->mEnforcement->mDestinations;
    entry.endpoints() = aPolicy->mEnforcement->mEndpoints.Clone();
    aArgs.enforcement() = Some(entry);
  }

  if (aPolicy->mReportOnly) {
    mozilla::ipc::IntegrityPolicyEntry entry;
    entry.sources() = aPolicy->mReportOnly->mSources;
    entry.destinations() = aPolicy->mReportOnly->mDestinations;
    entry.endpoints() = aPolicy->mReportOnly->mEndpoints.Clone();
    aArgs.reportOnly() = Some(entry);
  }
}

void IntegrityPolicy::FromArgs(const mozilla::ipc::IntegrityPolicyArgs& aArgs,
                               IntegrityPolicy** aPolicy) {
  RefPtr<IntegrityPolicy> policy = new IntegrityPolicy();

  if (aArgs.enforcement().isSome()) {
    const auto& entry = *aArgs.enforcement();
    policy->mEnforcement.emplace(Entry(entry.sources(), entry.destinations(),
                                       entry.endpoints().Clone()));
  }

  if (aArgs.reportOnly().isSome()) {
    const auto& entry = *aArgs.reportOnly();
    policy->mReportOnly.emplace(Entry(entry.sources(), entry.destinations(),
                                      entry.endpoints().Clone()));
  }

  policy.forget(aPolicy);
}

void IntegrityPolicy::InitFromOther(IntegrityPolicy* aOther) {
  if (!aOther) {
    return;
  }

  if (aOther->mEnforcement) {
    mEnforcement.emplace(Entry(*aOther->mEnforcement));
  }

  if (aOther->mReportOnly) {
    mReportOnly.emplace(Entry(*aOther->mReportOnly));
  }
}

bool IntegrityPolicy::Equals(const IntegrityPolicy* aPolicy,
                             const IntegrityPolicy* aOtherPolicy) {
  // Do a quick pointer check first, also checks if both are null.
  if (aPolicy == aOtherPolicy) {
    return true;
  }

  // We checked if they were null above, so make sure one of them is not null.
  if (!aPolicy || !aOtherPolicy) {
    return false;
  }

  if (!Entry::Equals(aPolicy->mEnforcement, aOtherPolicy->mEnforcement)) {
    return false;
  }

  if (!Entry::Equals(aPolicy->mReportOnly, aOtherPolicy->mReportOnly)) {
    return false;
  }

  return true;
}

bool IntegrityPolicy::Entry::Equals(const Maybe<Entry>& aPolicy,
                                    const Maybe<Entry>& aOtherPolicy) {
  // If one is set and the other is not, they are not equal.
  if (aPolicy.isSome() != aOtherPolicy.isSome()) {
    return false;
  }

  // If both are not set, they are equal.
  if (aPolicy.isNothing() && aOtherPolicy.isNothing()) {
    return true;
  }

  if (aPolicy->mSources != aOtherPolicy->mSources) {
    return false;
  }

  if (aPolicy->mDestinations != aOtherPolicy->mDestinations) {
    return false;
  }

  if (aPolicy->mEndpoints != aOtherPolicy->mEndpoints) {
    return false;
  }

  return true;
}

constexpr static const uint32_t kIntegrityPolicySerializationVersion = 1;

NS_IMETHODIMP
IntegrityPolicy::Read(nsIObjectInputStream* aStream) {
  uint32_t version;
  MOZ_TRY(aStream->Read32(&version));

  if (version != kIntegrityPolicySerializationVersion) {
    LOG("IntegrityPolicy::Read: Unsupported version: {}", version);
    return NS_ERROR_FAILURE;
  }

  for (const bool& isRO : {false, true}) {
    bool hasPolicy;
    MOZ_TRY(aStream->ReadBoolean(&hasPolicy));

    if (!hasPolicy) {
      continue;
    }

    uint32_t sources;
    MOZ_TRY(aStream->Read32(&sources));

    Sources sourcesSet;
    sourcesSet.deserialize(sources);

    uint32_t destinations;
    MOZ_TRY(aStream->Read32(&destinations));

    Destinations destinationsSet;
    destinationsSet.deserialize(destinations);

    uint32_t endpointsLen;
    MOZ_TRY(aStream->Read32(&endpointsLen));

    nsTArray<nsCString> endpoints(endpointsLen);
    for (size_t endpointI = 0; endpointI < endpointsLen; endpointI++) {
      nsCString endpoint;
      MOZ_TRY(aStream->ReadCString(endpoint));
      endpoints.AppendElement(std::move(endpoint));
    }

    Entry entry = Entry(sourcesSet, destinationsSet, std::move(endpoints));
    if (isRO) {
      mReportOnly.emplace(entry);
    } else {
      mEnforcement.emplace(entry);
    }
  }

  return NS_OK;
}

NS_IMETHODIMP
IntegrityPolicy::Write(nsIObjectOutputStream* aStream) {
  MOZ_TRY(aStream->Write32(kIntegrityPolicySerializationVersion));

  for (const auto& entry : {mEnforcement, mReportOnly}) {
    if (!entry) {
      MOZ_TRY(aStream->WriteBoolean(false));
      continue;
    }

    MOZ_TRY(aStream->WriteBoolean(true));

    MOZ_TRY(aStream->Write32(entry->mSources.serialize()));
    MOZ_TRY(aStream->Write32(entry->mDestinations.serialize()));

    MOZ_TRY(aStream->Write32(entry->mEndpoints.Length()));
    for (const auto& endpoint : entry->mEndpoints) {
      MOZ_TRY(aStream->WriteCString(endpoint));
    }
  }

  return NS_OK;
}

NS_IMPL_CLASSINFO(IntegrityPolicy, nullptr, 0, NS_IINTEGRITYPOLICY_IID)
NS_IMPL_ISUPPORTS_CI(IntegrityPolicy, nsIIntegrityPolicy, nsISerializable)

}  // namespace mozilla::dom

#undef LOG

Messung V0.5 in Prozent
C=86 H=92 G=88

¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=752002