Commit 15c60c50 authored by Domenic Denicola's avatar Domenic Denicola Committed by Commit Bot

Origin policy: implement header parsing

This implements the algorithm at
https://wicg.github.io/origin-policy/#parse-an-origin-policy-header
along with supporting data structures to represent the parse result.

The parsed header is not yet used by any part of the system, and so
these changes are not web-exposed yet; that will be done in a follow-up.
Instead the parser has a lot of unit tests.

Bug: 1042036
Change-Id: I3b0e117ee9af352fc30b9e63906caf4e98d44b00
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2099082Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarJames MacLean <wjmaclean@chromium.org>
Reviewed-by: default avatarDaniel Vogelheim <vogelheim@chromium.org>
Commit-Queue: Domenic Denicola <domenic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#750964}
parent 110188fb
...@@ -85,8 +85,12 @@ jumbo_component("network_service") { ...@@ -85,8 +85,12 @@ jumbo_component("network_service") {
"origin_policy/origin_policy_constants.h", "origin_policy/origin_policy_constants.h",
"origin_policy/origin_policy_fetcher.cc", "origin_policy/origin_policy_fetcher.cc",
"origin_policy/origin_policy_fetcher.h", "origin_policy/origin_policy_fetcher.h",
"origin_policy/origin_policy_header_values.cc",
"origin_policy/origin_policy_header_values.h",
"origin_policy/origin_policy_manager.cc", "origin_policy/origin_policy_manager.cc",
"origin_policy/origin_policy_manager.h", "origin_policy/origin_policy_manager.h",
"origin_policy/origin_policy_parsed_header.cc",
"origin_policy/origin_policy_parsed_header.h",
"origin_policy/origin_policy_parser.cc", "origin_policy/origin_policy_parser.cc",
"origin_policy/origin_policy_parser.h", "origin_policy/origin_policy_parser.h",
"p2p/socket.cc", "p2p/socket.cc",
...@@ -314,6 +318,7 @@ source_set("tests") { ...@@ -314,6 +318,7 @@ source_set("tests") {
"network_service_unittest.cc", "network_service_unittest.cc",
"network_usage_accumulator_unittest.cc", "network_usage_accumulator_unittest.cc",
"origin_policy/origin_policy_manager_unittest.cc", "origin_policy/origin_policy_manager_unittest.cc",
"origin_policy/origin_policy_parsed_header_unittest.cc",
"origin_policy/origin_policy_parser_unittest.cc", "origin_policy/origin_policy_parser_unittest.cc",
"p2p/socket_tcp_server_unittest.cc", "p2p/socket_tcp_server_unittest.cc",
"p2p/socket_tcp_unittest.cc", "p2p/socket_tcp_unittest.cc",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/network/origin_policy/origin_policy_header_values.h"
#include "base/logging.h"
namespace network {
OriginPolicyAllowedValue::OriginPolicyAllowedValue(
OriginPolicyAllowedValue::State state,
const base::Optional<std::string>& string)
: state_(state), string_(string) {}
OriginPolicyAllowedValue::~OriginPolicyAllowedValue() = default;
OriginPolicyAllowedValue::OriginPolicyAllowedValue(
const OriginPolicyAllowedValue&) = default;
// static
const OriginPolicyAllowedValue OriginPolicyAllowedValue::FromString(
const std::string& string) {
DCHECK(!string.empty());
return OriginPolicyAllowedValue(OriginPolicyAllowedValue::State::kString,
string);
}
// static
const OriginPolicyAllowedValue OriginPolicyAllowedValue::Latest() {
return OriginPolicyAllowedValue(OriginPolicyAllowedValue::State::kLatestToken,
std::string());
}
// static
const OriginPolicyAllowedValue OriginPolicyAllowedValue::Null() {
return OriginPolicyAllowedValue(OriginPolicyAllowedValue::State::kNullToken,
std::string());
}
OriginPolicyPreferredValue::OriginPolicyPreferredValue(
const base::Optional<std::string>& string)
: string_(string) {}
OriginPolicyPreferredValue::~OriginPolicyPreferredValue() = default;
OriginPolicyPreferredValue::OriginPolicyPreferredValue(
const OriginPolicyPreferredValue&) = default;
// static
const OriginPolicyPreferredValue OriginPolicyPreferredValue::FromString(
const std::string& string) {
DCHECK(!string.empty());
return OriginPolicyPreferredValue(string);
}
// static
const OriginPolicyPreferredValue
OriginPolicyPreferredValue::LatestFromNetwork() {
return OriginPolicyPreferredValue(base::nullopt);
}
} // namespace network
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SERVICES_NETWORK_ORIGIN_POLICY_ORIGIN_POLICY_HEADER_VALUES_H_
#define SERVICES_NETWORK_ORIGIN_POLICY_ORIGIN_POLICY_HEADER_VALUES_H_
#include <string>
#include "base/component_export.h"
#include "base/optional.h"
namespace network {
// Represents a value from the Origin-Policy header's allowed=() list.
//
// Values can be a string, or the special tokens `latest` or `null`.
class COMPONENT_EXPORT(NETWORK_SERVICE) OriginPolicyAllowedValue final {
public:
~OriginPolicyAllowedValue();
OriginPolicyAllowedValue(const OriginPolicyAllowedValue&);
// The caller is expected to a string that is a valid origin policy ID:
// https://wicg.github.io/origin-policy/#valid-origin-policy-id
static const OriginPolicyAllowedValue FromString(const std::string&);
static const OriginPolicyAllowedValue Null();
static const OriginPolicyAllowedValue Latest();
bool operator==(const OriginPolicyAllowedValue& other) const {
return state_ == other.state_ && string_ == other.string_;
}
bool operator!=(const OriginPolicyAllowedValue& other) const {
return !(*this == other);
}
bool is_latest() const {
return state_ == OriginPolicyAllowedValue::State::kLatestToken;
}
bool is_null() const {
return state_ == OriginPolicyAllowedValue::State::kNullToken;
}
bool is_string() const {
return state_ == OriginPolicyAllowedValue::State::kString;
}
const std::string& string() const {
DCHECK(is_string());
DCHECK(string_.has_value());
return *string_;
}
private:
enum class State { kString, kLatestToken, kNullToken };
OriginPolicyAllowedValue(State, const base::Optional<std::string>&);
State state_;
base::Optional<std::string> string_;
};
// Represents a value from the Origin-Policy header's preferred= entry.
//
// Values can be a string, or the special token `latest-from-network`.
class COMPONENT_EXPORT(NETWORK_SERVICE) OriginPolicyPreferredValue final {
public:
~OriginPolicyPreferredValue();
OriginPolicyPreferredValue(const OriginPolicyPreferredValue&);
// The caller is expected to a string that is a valid origin policy ID:
// https://wicg.github.io/origin-policy/#valid-origin-policy-id
static const OriginPolicyPreferredValue FromString(const std::string&);
static const OriginPolicyPreferredValue LatestFromNetwork();
bool operator==(const OriginPolicyPreferredValue& other) const {
return string_ == other.string_;
}
bool operator!=(const OriginPolicyPreferredValue& other) const {
return !(*this == other);
}
bool is_latest_from_network() const { return !string_.has_value(); }
bool is_string() const { return string_.has_value(); }
const std::string& string() const {
DCHECK(is_string());
return *string_;
}
private:
explicit OriginPolicyPreferredValue(const base::Optional<std::string>&);
// If string_ is base::nullopt, then this is latest-from-network. This is a
// small optimization compared to the State enum used for
// OriginPolicyAllowedValue.
base::Optional<std::string> string_;
};
} // namespace network
#endif // SERVICES_NETWORK_ORIGIN_POLICY_ORIGIN_POLICY_HEADER_VALUES_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/network/origin_policy/origin_policy_parsed_header.h"
#include "base/stl_util.h"
#include "net/http/structured_headers.h"
namespace network {
namespace sh = net::structured_headers;
OriginPolicyParsedHeader::OriginPolicyParsedHeader(
const std::vector<OriginPolicyAllowedValue>& allowed,
const base::Optional<OriginPolicyPreferredValue>& preferred)
: allowed_(allowed), preferred_(preferred) {
DCHECK(!allowed_.empty() || preferred_.has_value());
}
OriginPolicyParsedHeader::~OriginPolicyParsedHeader() = default;
OriginPolicyParsedHeader::OriginPolicyParsedHeader(
const OriginPolicyParsedHeader&) = default;
// https://wicg.github.io/origin-policy/#parse-an-origin-policy-header
base::Optional<OriginPolicyParsedHeader> OriginPolicyParsedHeader::FromString(
const std::string& string) {
base::Optional<sh::Dictionary> parsed_header_opt =
sh::ParseDictionary(string);
if (!parsed_header_opt) {
return base::nullopt;
}
// TODO(domenic): when https://crbug.com/1061139 gets fixed we can
// make this a const reference.
sh::Dictionary& parsed_header = *parsed_header_opt;
std::vector<OriginPolicyAllowedValue> allowed;
if (parsed_header.contains("allowed")) {
if (!parsed_header["allowed"].member_is_inner_list) {
return base::nullopt;
}
const std::vector<sh::ParameterizedItem>& raw_allowed_list =
parsed_header["allowed"].member;
for (const auto& parameterized_item : raw_allowed_list) {
base::Optional<OriginPolicyAllowedValue> result;
const sh::Item& item = parameterized_item.item;
if (item.is_string()) {
const std::string& string = item.GetString();
if (string.empty()) {
return base::nullopt;
}
result = OriginPolicyAllowedValue::FromString(string);
} else if (item.is_token()) {
const std::string& string = item.GetString();
if (string == "null") {
result = OriginPolicyAllowedValue::Null();
} else if (string == "latest") {
result = OriginPolicyAllowedValue::Latest();
}
} else {
return base::nullopt;
}
if (result && !base::Contains(allowed, *result)) {
allowed.push_back(*result);
}
}
}
base::Optional<OriginPolicyPreferredValue> preferred;
if (parsed_header.contains("preferred")) {
if (parsed_header["preferred"].member_is_inner_list) {
return base::nullopt;
}
const sh::Item& item = parsed_header["preferred"].member[0].item;
if (item.is_string()) {
const std::string& string = item.GetString();
if (string.empty()) {
return base::nullopt;
}
preferred = OriginPolicyPreferredValue::FromString(string);
} else if (item.is_token()) {
const std::string& string = item.GetString();
if (string == "latest-from-network") {
preferred = OriginPolicyPreferredValue::LatestFromNetwork();
}
} else {
return base::nullopt;
}
}
if (allowed.empty() && !preferred.has_value()) {
return base::nullopt;
}
return OriginPolicyParsedHeader(allowed, preferred);
}
} // namespace network
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SERVICES_NETWORK_ORIGIN_POLICY_ORIGIN_POLICY_PARSED_HEADER_H_
#define SERVICES_NETWORK_ORIGIN_POLICY_ORIGIN_POLICY_PARSED_HEADER_H_
#include <string>
#include <vector>
#include "base/component_export.h"
#include "base/optional.h"
#include "services/network/origin_policy/origin_policy_header_values.h"
namespace network {
// Contains the parsed result of an Origin-Policy header, i.e. the output of
// https://wicg.github.io/origin-policy/#parse-an-origin-policy-header.
class COMPONENT_EXPORT(NETWORK_SERVICE) OriginPolicyParsedHeader final {
public:
static base::Optional<OriginPolicyParsedHeader> FromString(
const std::string&);
~OriginPolicyParsedHeader();
OriginPolicyParsedHeader(const OriginPolicyParsedHeader&);
const std::vector<OriginPolicyAllowedValue>& allowed() const {
return allowed_;
}
const base::Optional<OriginPolicyPreferredValue>& preferred() const {
return preferred_;
}
private:
OriginPolicyParsedHeader(
const std::vector<OriginPolicyAllowedValue>& allowed,
const base::Optional<OriginPolicyPreferredValue>& preferred);
std::vector<OriginPolicyAllowedValue> allowed_;
base::Optional<OriginPolicyPreferredValue> preferred_;
};
} // namespace network
#endif // SERVICES_NETWORK_ORIGIN_POLICY_ORIGIN_POLICY_PARSED_HEADER_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment