Commit db773cda authored by David Van Cleve's avatar David Van Cleve Committed by Commit Bot

Trust Tokens: Add the "trusttoken" iframe attribute (and parse it)

The experimental Trust Token API (https://github.com/wicg/trust-token-api)
takes in parameters specifying a Trust Tokens protocol operation,
annotates outgoing requests with request headers corresponding to
protocol state, and processes corresponding response headers. For the
MVP of the API, we're making the functionality available via Fetch,
iframe, and XHR.

To implement the iframe surface, this CL adds a new iframe attribute,
"trusttoken," that accepts as input a syntactically identical struct to
the corresponding Fetch parameter; the CL also adds code for parsing the
parameter. The attribute is gated by the TrustTokens runtime-enabled
feature.

A subsequent CL will add the results of the parse to the frame's load
request in order for the downstream request handling logic to be able to
access the parameters.

Bug: 1062396
Change-Id: I8f0d00e17e06f5f4bbf780db5a77a71ea4d64db4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2116014
Commit-Queue: David Van Cleve <davidvc@chromium.org>
Reviewed-by: default avatarJeremy Roman <jbroman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#755033}
parent 983be9e8
......@@ -580,6 +580,8 @@ blink_core_sources("html") {
"track/vtt/vtt_token.h",
"track/vtt/vtt_tokenizer.cc",
"track/vtt/vtt_tokenizer.h",
"trust_token_attribute_parsing.cc",
"trust_token_attribute_parsing.h",
"window_name_collection.cc",
"window_name_collection.h",
]
......@@ -699,5 +701,8 @@ blink_core_tests("unit_tests") {
"track/text_track_list_test.cc",
"track/vtt/buffered_line_reader_test.cc",
"track/vtt/vtt_scanner_test.cc",
"trust_token_attribute_parsing_test.cc",
]
deps = [ "//services/network/trust_tokens:test_support" ]
}
specific_include_rules = {
"trust_token_attribute_parsing_test.cc": [
"+services/network/trust_tokens/test",
],
}
......@@ -318,6 +318,7 @@
"topmargin",
"translate",
"truespeed",
"trusttoken",
"type",
"usemap",
"valign",
......
......@@ -342,6 +342,11 @@ void HTMLFrameOwnerElement::UpdateRequiredPolicy() {
}
}
network::mojom::blink::TrustTokenParamsPtr
HTMLFrameOwnerElement::ConstructTrustTokenParams() const {
return nullptr;
}
void HTMLFrameOwnerElement::FrameOwnerPropertiesChanged() {
// Don't notify about updates if ContentFrame() is null, for example when
// the subframe hasn't been created yet; or if we are in the middle of
......
......@@ -21,6 +21,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_HTML_FRAME_OWNER_ELEMENT_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_HTML_FRAME_OWNER_ELEMENT_H_
#include "services/network/public/mojom/trust_tokens.mojom-blink-forward.h"
#include "third_party/blink/public/common/frame/frame_owner_element_type.h"
#include "third_party/blink/public/mojom/scroll/scrollbar_mode.mojom-blink.h"
#include "third_party/blink/renderer/core/core_export.h"
......@@ -182,6 +183,11 @@ class CORE_EXPORT HTMLFrameOwnerElement : public HTMLElement,
// changes.
void UpdateRequiredPolicy();
// Return a set of Trust Tokens parameters for requests for this frame,
// based on the frame attributes.
virtual network::mojom::blink::TrustTokenParamsPtr ConstructTrustTokenParams()
const;
private:
// Intentionally private to prevent redundant checks when the type is
// already HTMLFrameOwnerElement.
......
......@@ -24,6 +24,7 @@
#include "third_party/blink/renderer/core/html/html_iframe_element.h"
#include "services/network/public/mojom/trust_tokens.mojom-blink.h"
#include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_html_iframe_element.h"
#include "third_party/blink/renderer/core/css/css_property_names.h"
......@@ -35,12 +36,14 @@
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
#include "third_party/blink/renderer/core/frame/sandbox_flags.h"
#include "third_party/blink/renderer/core/html/html_document.h"
#include "third_party/blink/renderer/core/html/trust_token_attribute_parsing.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/core/layout/layout_iframe.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/json/json_parser.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
namespace blink {
......@@ -262,6 +265,10 @@ void HTMLIFrameElement::ParseAttribute(
required_policy_ = value;
UpdateRequiredPolicy();
}
} else if (name == html_names::kTrusttokenAttr) {
if (trust_token_ != value) {
trust_token_ = value;
}
} else {
// Websites picked up a Chromium article that used this non-specified
// attribute which ended up changing shape after the specification process.
......@@ -414,4 +421,55 @@ network::mojom::ReferrerPolicy HTMLIFrameElement::ReferrerPolicyAttribute() {
return referrer_policy_;
}
network::mojom::blink::TrustTokenParamsPtr
HTMLIFrameElement::ConstructTrustTokenParams() const {
if (!trust_token_)
return nullptr;
JSONParseError parse_error;
std::unique_ptr<JSONValue> parsed_attribute =
ParseJSON(trust_token_, &parse_error);
if (!parsed_attribute) {
GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
mojom::blink::ConsoleMessageSource::kOther,
mojom::blink::ConsoleMessageLevel::kError,
"iframe trusttoken attribute was invalid JSON: " + parse_error.message +
String::Format(" (line %d, col %d)", parse_error.line,
parse_error.column)));
return nullptr;
}
network::mojom::blink::TrustTokenParamsPtr parsed_params =
internal::TrustTokenParamsFromJson(std::move(parsed_attribute));
if (!parsed_params) {
GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
mojom::blink::ConsoleMessageSource::kOther,
mojom::blink::ConsoleMessageLevel::kError,
"Couldn't parse iframe trusttoken attribute (was it missing a "
"field?)"));
return nullptr;
}
// Trust token redemption and signing (but not issuance) require that the
// trust-token-redemption feature policy be present.
bool operation_requires_feature_policy =
parsed_params->type ==
network::mojom::blink::TrustTokenOperationType::kRedemption ||
parsed_params->type ==
network::mojom::blink::TrustTokenOperationType::kSigning;
if (operation_requires_feature_policy &&
(!GetDocument().IsFeatureEnabled(
mojom::blink::FeaturePolicyFeature::kTrustTokenRedemption))) {
GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
mojom::blink::ConsoleMessageSource::kOther,
mojom::blink::ConsoleMessageLevel::kError,
"Trust Tokens: Attempted redemption or signing without the "
"trust-token-redemption Feature Policy feature present."));
return nullptr;
}
return parsed_params;
}
} // namespace blink
......@@ -24,6 +24,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_HTML_IFRAME_ELEMENT_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_HTML_IFRAME_ELEMENT_H_
#include "services/network/public/mojom/trust_tokens.mojom-blink-forward.h"
#include "third_party/blink/public/common/feature_policy/feature_policy.h"
#include "third_party/blink/public/common/frame/frame_owner_element_type.h"
#include "third_party/blink/renderer/core/core_export.h"
......@@ -86,6 +87,9 @@ class CORE_EXPORT HTMLIFrameElement final
network::mojom::ReferrerPolicy ReferrerPolicyAttribute() override;
network::mojom::blink::TrustTokenParamsPtr ConstructTrustTokenParams()
const override;
// FrameOwner overrides:
bool AllowFullscreen() const override { return allow_fullscreen_; }
bool AllowPaymentRequest() const override { return allow_payment_request_; }
......@@ -95,6 +99,10 @@ class CORE_EXPORT HTMLIFrameElement final
AtomicString required_csp_;
AtomicString allow_;
AtomicString required_policy_; // policy attribute
// String attribute storing a JSON representation of the Trust Token
// parameters (in order to align with the fetch interface to the Trust Token
// API). If present, this is parsed in ConstructTrustTokenParams.
AtomicString trust_token_;
bool allow_fullscreen_;
bool allow_payment_request_;
bool collapsed_by_client_;
......
......@@ -54,6 +54,9 @@
[RuntimeEnabled=LazyFrameLoading, CEReactions, Reflect, ReflectOnly=("lazy", "eager", "auto"), ReflectMissing="auto", ReflectInvalid="auto"] attribute DOMString loading;
// Trust Tokens request parameters (https://github.com/wicg/trust-token-api)
[RuntimeEnabled=TrustTokens, SecureContext, Reflect] attribute DOMString trustToken;
// obsolete members
// https://html.spec.whatwg.org/C/#HTMLIFrameElement-partial
[CEReactions, Reflect] attribute DOMString align;
......
// 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 "third_party/blink/renderer/core/html/trust_token_attribute_parsing.h"
#include "services/network/public/mojom/trust_tokens.mojom-blink.h"
#include "services/network/public/mojom/trust_tokens.mojom-shared.h"
#include "third_party/blink/renderer/core/fetch/trust_token.h"
#include "third_party/blink/renderer/platform/json/json_values.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
namespace internal {
namespace {
bool ParseType(const String& in, network::mojom::TrustTokenOperationType* out) {
if (in == "token-request") {
*out = network::mojom::TrustTokenOperationType::kIssuance;
return true;
} else if (in == "srr-token-redemption") {
*out = network::mojom::TrustTokenOperationType::kRedemption;
return true;
} else if (in == "send-srr") {
*out = network::mojom::TrustTokenOperationType::kSigning;
return true;
} else {
return false;
}
}
bool ParseRefreshPolicy(const String& in,
network::mojom::TrustTokenRefreshPolicy* out) {
if (in == "none") {
*out = network::mojom::TrustTokenRefreshPolicy::kUseCached;
return true;
} else if (in == "refresh") {
*out = network::mojom::TrustTokenRefreshPolicy::kRefresh;
return true;
}
return false;
}
bool ParseSignRequestData(const String& in,
network::mojom::TrustTokenSignRequestData* out) {
if (in == "omit") {
*out = network::mojom::TrustTokenSignRequestData::kOmit;
return true;
} else if (in == "headers-only") {
*out = network::mojom::TrustTokenSignRequestData::kHeadersOnly;
return true;
} else if (in == "include") {
*out = network::mojom::TrustTokenSignRequestData::kInclude;
return true;
}
return false;
}
} // namespace
// Given a JSON representation of a Trust Token parameters struct, constructs
// and returns the represented struct if the JSON representation is valid;
// returns nullopt otherwise.
network::mojom::blink::TrustTokenParamsPtr TrustTokenParamsFromJson(
std::unique_ptr<JSONValue> in) {
JSONObject* object = JSONObject::Cast(in.get());
if (!object)
return nullptr;
auto ret = network::mojom::blink::TrustTokenParams::New();
// |type| is required.
String type;
if (!object->GetString("type", &type))
return nullptr;
if (!ParseType(type, &ret->type))
return nullptr;
// |refreshPolicy| is optional.
if (JSONValue* refresh_policy = object->Get("refreshPolicy")) {
String str_policy;
if (!refresh_policy->AsString(&str_policy))
return nullptr;
if (!ParseRefreshPolicy(str_policy, &ret->refresh_policy))
return nullptr;
}
// |signRequestData| is optional.
if (JSONValue* sign_request_data = object->Get("signRequestData")) {
String str_sign_request_data;
if (!sign_request_data->AsString(&str_sign_request_data))
return nullptr;
if (!ParseSignRequestData(str_sign_request_data, &ret->sign_request_data)) {
return nullptr;
}
}
// |includeTimestampHeader| is optional.
if (JSONValue* include_timestamp_header =
object->Get("includeTimestampHeader")) {
if (!include_timestamp_header->AsBoolean(&ret->include_timestamp_header))
return nullptr;
}
// |issuer| is optional, but, if it's present, it must be a valid origin,
// potentially trustworthy, and HTTP or HTTPS.
if (JSONValue* issuer = object->Get("issuer")) {
String str_issuer;
if (!issuer->AsString(&str_issuer))
return nullptr;
ret->issuer = SecurityOrigin::CreateFromString(str_issuer);
if (!ret->issuer)
return nullptr;
if (!ret->issuer->IsPotentiallyTrustworthy())
return nullptr;
if (ret->issuer->Protocol() != "http" && ret->issuer->Protocol() != "https")
return nullptr;
}
// |additionalSignedHeaders| is optional.
if (JSONValue* additional_signed_headers =
object->Get("additionalSignedHeaders")) {
JSONArray* signed_headers_array =
JSONArray::Cast(additional_signed_headers);
if (!signed_headers_array)
return nullptr;
// Because of the characteristics of the Trust Tokens protocol, we expect
// roughly 2-5 elements in this array.
for (size_t i = 0; i < signed_headers_array->size(); ++i) {
String next;
if (!signed_headers_array->at(i)->AsString(&next))
return nullptr;
ret->additional_signed_headers.push_back(std::move(next));
}
}
return ret;
}
} // namespace internal
} // namespace blink
// 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 THIRD_PARTY_BLINK_RENDERER_CORE_HTML_TRUST_TOKEN_ATTRIBUTE_PARSING_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_TRUST_TOKEN_ATTRIBUTE_PARSING_H_
#include "base/optional.h"
#include "services/network/public/mojom/trust_tokens.mojom-blink-forward.h"
#include "third_party/blink/renderer/core/core_export.h"
namespace blink {
class JSONValue;
namespace internal {
// Given a JSON representation of a Trust Token parameters struct, constructs
// and returns the represented struct if the JSON representation is valid;
// returns nullopt otherwise.
CORE_EXPORT network::mojom::blink::TrustTokenParamsPtr TrustTokenParamsFromJson(
std::unique_ptr<JSONValue> in);
} // namespace internal
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_TRUST_TOKEN_ATTRIBUTE_PARSING_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 "third_party/blink/renderer/core/html/trust_token_attribute_parsing.h"
#include "services/network/public/mojom/trust_tokens.mojom-blink.h"
#include "services/network/trust_tokens/test/trust_token_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/platform/json/json_parser.h"
#include "third_party/blink/renderer/platform/json/json_values.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
namespace internal {
namespace {
network::mojom::blink::TrustTokenParamsPtr NetworkParamsToBlinkParams(
network::mojom::TrustTokenParamsPtr params) {
auto ret = network::mojom::blink::TrustTokenParams::New();
ret->type = params->type;
ret->refresh_policy = params->refresh_policy;
ret->sign_request_data = params->sign_request_data;
ret->include_timestamp_header = params->include_timestamp_header;
if (params->issuer)
ret->issuer = SecurityOrigin::CreateFromUrlOrigin(*params->issuer);
for (const std::string& header : params->additional_signed_headers) {
ret->additional_signed_headers.push_back(String::FromUTF8(header));
}
return ret;
}
} // namespace
using TrustTokenAttributeParsingSuccess =
::testing::TestWithParam<network::TrustTokenTestParameters>;
INSTANTIATE_TEST_SUITE_P(
WithIssuanceParams,
TrustTokenAttributeParsingSuccess,
::testing::ValuesIn(network::kIssuanceTrustTokenTestParameters));
INSTANTIATE_TEST_SUITE_P(
WithRedemptionParams,
TrustTokenAttributeParsingSuccess,
::testing::ValuesIn(network::kRedemptionTrustTokenTestParameters));
INSTANTIATE_TEST_SUITE_P(
WithSigningParams,
TrustTokenAttributeParsingSuccess,
::testing::ValuesIn(network::kSigningTrustTokenTestParameters));
// Test roundtrip serializations-then-deserializations for a collection of test
// cases covering all possible values of all enum attributes, and all
// possibilities (e.g. optional members present vs. not present) for all other
// attributes.
TEST_P(TrustTokenAttributeParsingSuccess, Roundtrip) {
network::mojom::TrustTokenParams network_expectation;
std::string input;
network::TrustTokenParametersAndSerialization
expected_params_and_serialization =
network::SerializeTrustTokenParametersAndConstructExpectation(
GetParam());
network::mojom::blink::TrustTokenParamsPtr expectation =
NetworkParamsToBlinkParams(
std::move(expected_params_and_serialization.params));
std::unique_ptr<JSONValue> json_value = ParseJSON(
String::FromUTF8(expected_params_and_serialization.serialized_params));
ASSERT_TRUE(json_value);
auto result = TrustTokenParamsFromJson(std::move(json_value));
ASSERT_TRUE(result);
// We can't use mojo's generated Equals method here because it doesn't play
// well with the "issuer" field's type of
// scoped_refptr<blink::SecurityOrigin>: in particular, the method does an
// address-to-address comparison of the pointers.
EXPECT_EQ(result->type, expectation->type);
EXPECT_EQ(result->refresh_policy, expectation->refresh_policy);
EXPECT_EQ(result->sign_request_data, expectation->sign_request_data);
EXPECT_EQ(result->include_timestamp_header,
expectation->include_timestamp_header);
EXPECT_EQ(!!result->issuer, !!expectation->issuer);
if (result->issuer)
EXPECT_EQ(result->issuer->ToString(), expectation->issuer->ToString());
EXPECT_EQ(result->additional_signed_headers,
expectation->additional_signed_headers);
}
TEST(TrustTokenAttributeParsing, NotADictionary) {
auto json = ParseJSON(R"(
3
)");
ASSERT_TRUE(json);
ASSERT_FALSE(TrustTokenParamsFromJson(std::move(json)));
}
TEST(TrustTokenAttributeParsing, MissingType) {
auto json = ParseJSON(R"(
{ }
)");
ASSERT_TRUE(json);
ASSERT_FALSE(TrustTokenParamsFromJson(std::move(json)));
}
TEST(TrustTokenAttributeParsing, TypeUnsafeType) {
auto json = ParseJSON(R"(
{ "type": 3 }
)");
ASSERT_TRUE(json);
ASSERT_FALSE(TrustTokenParamsFromJson(std::move(json)));
}
TEST(TrustTokenAttributeParsing, InvalidType) {
auto json = ParseJSON(R"(
{ "type": "not a valid type" }
)");
ASSERT_TRUE(json);
ASSERT_FALSE(TrustTokenParamsFromJson(std::move(json)));
}
TEST(TrustTokenAttributeParsing, TypeUnsafeRefreshPolicy) {
auto json = ParseJSON(R"(
{ "type": "token-request",
"refreshPolicy": 3 }
)");
ASSERT_TRUE(json);
ASSERT_FALSE(TrustTokenParamsFromJson(std::move(json)));
}
TEST(TrustTokenAttributeParsing, InvalidRefreshPolicy) {
auto json = ParseJSON(R"(
{ "type": "token-request",
"refreshPolicy": "not a valid refresh policy" }
)");
ASSERT_TRUE(json);
ASSERT_FALSE(TrustTokenParamsFromJson(std::move(json)));
}
TEST(TrustTokenAttributeParsing, TypeUnsafeSignRequestData) {
auto json = ParseJSON(R"(
{ "type": "token-request",
"signRequestData": 3 }
)");
ASSERT_TRUE(json);
ASSERT_FALSE(TrustTokenParamsFromJson(std::move(json)));
}
TEST(TrustTokenAttributeParsing, InvalidSignRequestData) {
auto json = ParseJSON(R"(
{ "type": "token-request",
"signRequestData": "not a member of the signRequestData enum" }
)");
ASSERT_TRUE(json);
ASSERT_FALSE(TrustTokenParamsFromJson(std::move(json)));
}
TEST(TrustTokenAttributeParsing, TypeUnsafeIncludeTimestampHeader) {
auto json = ParseJSON(R"(
{ "type": "token-request",
"includeTimestampHeader": 3 }
)");
ASSERT_TRUE(json);
ASSERT_FALSE(TrustTokenParamsFromJson(std::move(json)));
}
TEST(TrustTokenAttributeParsing, TypeUnsafeIssuer) {
auto json = ParseJSON(R"(
{ "type": "token-request",
"issuer": 3 }
)");
ASSERT_TRUE(json);
ASSERT_FALSE(TrustTokenParamsFromJson(std::move(json)));
}
// Test that the parser requires that |issuer| be a valid origin.
TEST(TrustTokenAttributeParsing, NonUrlIssuer) {
JSONParseError err;
auto json = ParseJSON(R"(
{ "type": "token-request",
"issuer": "not a URL" }
)",
&err);
ASSERT_TRUE(json);
ASSERT_FALSE(TrustTokenParamsFromJson(std::move(json)));
}
// Test that the parser requires that |issuer| be a potentially trustworthy
// origin.
TEST(TrustTokenAttributeParsing, InsecureIssuer) {
auto json = ParseJSON(R"(
{ "type": "token-request",
"issuer": "http://not-potentially-trustworthy.example" }
)");
ASSERT_TRUE(json);
ASSERT_FALSE(TrustTokenParamsFromJson(std::move(json)));
}
// Test that the parser requires that |issuer| be a HTTP or HTTPS origin.
TEST(TrustTokenAttributeParsing, NonHttpNonHttpsIssuer) {
auto json = ParseJSON(R"(
{ "type": "token-request",
"issuer": "file:///" }
)");
ASSERT_TRUE(json);
ASSERT_FALSE(TrustTokenParamsFromJson(std::move(json)));
}
TEST(TrustTokenAttributeParsing, TypeUnsafeAdditionalSignedHeaders) {
auto json = ParseJSON(R"(
{ "type": "token-request",
"additionalSignedHeaders": 3}
)");
ASSERT_TRUE(json);
ASSERT_FALSE(TrustTokenParamsFromJson(std::move(json)));
}
// Test that the parser requires that all members of the additionalSignedHeaders
// be strings.
TEST(TrustTokenAttributeParsing, TypeUnsafeAdditionalSignedHeader) {
auto json = ParseJSON(R"(
{ "type": "token-request",
"additionalSignedHeaders": ["plausible header", 17] }
)");
ASSERT_TRUE(json);
ASSERT_FALSE(TrustTokenParamsFromJson(std::move(json)));
}
} // namespace internal
} // namespace blink
......@@ -666,6 +666,7 @@ html element iframe
property scrolling
property src
property srcdoc
property trustToken
property width
html element img
property align
......
......@@ -3383,6 +3383,7 @@ interface HTMLIFrameElement : HTMLElement
getter scrolling
getter src
getter srcdoc
getter trustToken
getter width
method constructor
method getSVGDocument
......@@ -3405,6 +3406,7 @@ interface HTMLIFrameElement : HTMLElement
setter scrolling
setter src
setter srcdoc
setter trustToken
setter width
interface HTMLImageElement : HTMLElement
attribute @@toStringTag
......
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