Source code

Revision control

Copy as Markdown

Other Tools

/* 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 "PermissionsPolicyParser.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/dom/Feature.h"
#include "mozilla/dom/PermissionsPolicyUtils.h"
#include "mozilla/dom/PolicyTokenizer.h"
#include "mozilla/net/SFV.h"
#include "nsIScriptError.h"
#include "nsIURI.h"
#include "nsNetUtil.h"
namespace mozilla::dom {
namespace {
void ReportToConsoleUnsupportedFeature(Document* aDocument,
const nsString& aFeatureName) {
if (!aDocument) {
return;
}
AutoTArray<nsString, 1> params = {aFeatureName};
nsContentUtils::ReportToConsole(
nsIScriptError::warningFlag, "Permissions Policy"_ns, aDocument,
PropertiesFile::SECURITY_PROPERTIES,
"PermissionsPolicyUnsupportedFeatureName", params);
}
void ReportToConsoleInvalidEmptyAllowValue(Document* aDocument,
const nsString& aFeatureName) {
if (!aDocument) {
return;
}
AutoTArray<nsString, 1> params = {aFeatureName};
nsContentUtils::ReportToConsole(
nsIScriptError::warningFlag, "Permissions Policy"_ns, aDocument,
PropertiesFile::SECURITY_PROPERTIES,
"PermissionsPolicyInvalidEmptyAllowValue", params);
}
void ReportToConsoleInvalidAllowValue(Document* aDocument,
const nsString& aValue) {
if (!aDocument) {
return;
}
AutoTArray<nsString, 1> params = {aValue};
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
"Permissions Policy"_ns, aDocument,
PropertiesFile::SECURITY_PROPERTIES,
"PermissionsPolicyInvalidAllowValue", params);
}
} // namespace
/* static */
bool PermissionsPolicyParser::ParsePolicyFromAttribute(
const nsAString& aPolicy, Document* aDocument, nsIPrincipal* aSelfOrigin,
nsIPrincipal* aSrcOrigin, nsTArray<Feature>& aParsedFeatures) {
MOZ_ASSERT(aSelfOrigin);
nsTArray<CopyableTArray<nsString>> tokens;
PolicyTokenizer::tokenizePolicy(aPolicy, tokens);
nsTArray<Feature> parsedFeatures;
for (const nsTArray<nsString>& featureTokens : tokens) {
if (featureTokens.IsEmpty()) {
continue;
}
if (!PermissionsPolicyUtils::IsSupportedFeature(featureTokens[0])) {
ReportToConsoleUnsupportedFeature(aDocument, featureTokens[0]);
continue;
}
Feature feature(featureTokens[0]);
if (featureTokens.Length() == 1) {
if (aSrcOrigin) {
feature.AppendToAllowList(aSrcOrigin);
} else {
ReportToConsoleInvalidEmptyAllowValue(aDocument, featureTokens[0]);
continue;
}
} else {
// we gotta start at 1 here
for (uint32_t i = 1; i < featureTokens.Length(); ++i) {
const nsString& curVal = featureTokens[i];
if (curVal.LowerCaseEqualsASCII("'none'")) {
feature.SetAllowsNone();
break;
}
if (curVal.EqualsLiteral("*")) {
feature.SetAllowsAll();
break;
}
if (curVal.LowerCaseEqualsASCII("'self'")) {
feature.AppendToAllowList(aSelfOrigin);
continue;
}
if (aSrcOrigin && curVal.LowerCaseEqualsASCII("'src'")) {
feature.AppendToAllowList(aSrcOrigin);
continue;
}
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), curVal);
if (NS_FAILED(rv)) {
ReportToConsoleInvalidAllowValue(aDocument, curVal);
continue;
}
nsCOMPtr<nsIPrincipal> origin = BasePrincipal::CreateContentPrincipal(
uri, BasePrincipal::Cast(aSelfOrigin)->OriginAttributesRef());
if (NS_WARN_IF(!origin)) {
ReportToConsoleInvalidAllowValue(aDocument, curVal);
continue;
}
feature.AppendToAllowList(origin);
}
}
// No duplicate!
bool found = false;
for (const Feature& parsedFeature : parsedFeatures) {
if (parsedFeature.Name() == feature.Name()) {
found = true;
break;
}
}
if (!found) {
parsedFeatures.AppendElement(feature);
}
}
aParsedFeatures = std::move(parsedFeatures);
return true;
}
// Temporary: this supports only exact origins. Bug 2068536 will replace it
// with Permissions-Policy source-expression parsing.
static bool AppendOriginToFeature(const nsACString& aValue, Document* aDocument,
nsIPrincipal* aSelfOrigin,
Feature& aFeature) {
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aValue);
if (NS_FAILED(rv)) {
ReportToConsoleInvalidAllowValue(aDocument, NS_ConvertUTF8toUTF16(aValue));
return false;
}
nsCOMPtr<nsIPrincipal> origin = BasePrincipal::CreateContentPrincipal(
uri, BasePrincipal::Cast(aSelfOrigin)->OriginAttributesRef());
if (NS_WARN_IF(!origin)) {
ReportToConsoleInvalidAllowValue(aDocument, NS_ConvertUTF8toUTF16(aValue));
return false;
}
aFeature.AppendToAllowList(origin);
return true;
}
/* static */
bool PermissionsPolicyParser::ParsePolicyFromHeader(
const nsACString& aPolicy, Document* aDocument, nsIPrincipal* aSelfOrigin,
nsTArray<Feature>& aParsedFeatures) {
MOZ_ASSERT(aSelfOrigin);
// 1. treats a malformed structured header as an empty policy.
aParsedFeatures.Clear();
auto dictionary = net::SFV::ParseDict(aPolicy);
if (!dictionary.IsValid()) {
return false;
}
nsTArray<nsCString> keys;
if (NS_FAILED(dictionary.GetKeys(keys))) {
return false;
}
nsTArray<Feature> parsedFeatures;
for (const nsCString& key : keys) {
nsString featureName = NS_ConvertUTF8toUTF16(key);
if (!PermissionsPolicyUtils::IsSupportedFeature(featureName)) {
ReportToConsoleUnsupportedFeature(aDocument, featureName);
continue;
}
Feature feature(featureName);
auto innerList = dictionary.GetInnerList(key);
// 2 handle SFV lists, e.g. camera=(self)
if (innerList.IsValid()) {
for (size_t i = 0; i < innerList.Length(); ++i) {
auto item = innerList.GetItemAt(i);
// 2.1 handle tokens such as * and self
nsAutoCString token;
if (NS_SUCCEEDED(item.GetValue<net::SFV::Token>(token))) {
if (token.EqualsLiteral("*")) {
feature.SetAllowsAll();
break;
}
if (token.EqualsLiteral("self")) {
feature.AppendToAllowList(aSelfOrigin);
}
continue;
}
// 2.2 Parse quoted allowlist entries including origins
// TODO Bug 2068536: Support non-origin source expressions.
nsAutoCString source;
if (NS_SUCCEEDED(item.GetValue<net::SFV::SFVString>(source))) {
AppendOriginToFeature(source, aDocument, aSelfOrigin, feature);
}
}
parsedFeatures.AppendElement(std::move(feature));
continue;
}
// 3. handle SFV individual values, e.g. camera=self
nsAutoCString value;
bool validValue = false;
if (NS_SUCCEEDED(dictionary.GetItem<net::SFV::Token>(key, value))) {
if (value.EqualsLiteral("*")) {
feature.SetAllowsAll();
validValue = true;
} else if (value.EqualsLiteral("self")) {
feature.AppendToAllowList(aSelfOrigin);
validValue = true;
}
} else if (NS_SUCCEEDED(
dictionary.GetItem<net::SFV::SFVString>(key, value))) {
validValue =
AppendOriginToFeature(value, aDocument, aSelfOrigin, feature);
}
if (validValue) {
parsedFeatures.AppendElement(std::move(feature));
}
}
aParsedFeatures = std::move(parsedFeatures);
return true;
}
} // namespace mozilla::dom