1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
content / browser / renderer_host / navigation_policy_container_builder.cc [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/navigation_policy_container_builder.h"
#include <utility>
#include "content/browser/renderer_host/frame_navigation_entry.h"
#include "content/browser/renderer_host/navigation_state_keep_alive.h"
#include "content/browser/renderer_host/policy_container_host.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "services/network/public/cpp/web_sandbox_flags.h"
#include "services/network/public/mojom/content_security_policy.mojom-forward.h"
#include "services/network/public/mojom/ip_address_space.mojom.h"
#include "services/network/public/mojom/web_sandbox_flags.mojom.h"
namespace content {
namespace {
// Returns a copy of |parent|'s policies, or nullopt if |parent| is nullptr.
std::unique_ptr<PolicyContainerPolicies> GetParentPolicies(
RenderFrameHostImpl* parent) {
if (!parent) {
return nullptr;
}
return parent->policy_container_host()->policies().ClonePtr();
}
// Returns a copy of the navigation initiator's policies, if any.
//
// Must only be called on the browser's UI thread.
std::unique_ptr<PolicyContainerPolicies> GetInitiatorPolicies(
const blink::LocalFrameToken* frame_token,
int initiator_process_id,
StoragePartitionImpl* storage_partition) {
if (!frame_token) {
return nullptr;
}
PolicyContainerHost* initiator_policy_container_host =
RenderFrameHostImpl::GetPolicyContainerHost(
frame_token, initiator_process_id, storage_partition);
DCHECK(initiator_policy_container_host);
if (!initiator_policy_container_host) {
// Guard against wrong tokens being passed accidentally.
return nullptr;
}
return initiator_policy_container_host->policies().ClonePtr();
}
// Returns a copy of the given history |entry|'s policies, if any.
std::unique_ptr<PolicyContainerPolicies> GetHistoryPolicies(
const FrameNavigationEntry* entry) {
if (!entry) {
return nullptr;
}
const PolicyContainerPolicies* policies = entry->policy_container_policies();
if (!policies) {
return nullptr;
}
return policies->ClonePtr();
}
} // namespace
NavigationPolicyContainerBuilder::NavigationPolicyContainerBuilder(
RenderFrameHostImpl* parent,
const blink::LocalFrameToken* initiator_frame_token,
int initiator_process_id,
StoragePartition* storage_partition,
const FrameNavigationEntry* history_entry)
: parent_policies_(GetParentPolicies(parent)),
initiator_policies_(GetInitiatorPolicies(
initiator_frame_token,
initiator_process_id,
static_cast<StoragePartitionImpl*>(storage_partition))),
history_policies_(GetHistoryPolicies(history_entry)) {}
NavigationPolicyContainerBuilder::~NavigationPolicyContainerBuilder() = default;
const PolicyContainerPolicies*
NavigationPolicyContainerBuilder::InitiatorPolicies() const {
return initiator_policies_.get();
}
const PolicyContainerPolicies*
NavigationPolicyContainerBuilder::ParentPolicies() const {
return parent_policies_.get();
}
const PolicyContainerPolicies*
NavigationPolicyContainerBuilder::HistoryPolicies() const {
return history_policies_.get();
}
void NavigationPolicyContainerBuilder::SetIPAddressSpace(
network::mojom::IPAddressSpace address_space) {
DCHECK(!HasComputedPolicies());
delivered_policies_.ip_address_space = address_space;
}
void NavigationPolicyContainerBuilder::SetIsOriginPotentiallyTrustworthy(
bool value) {
DCHECK(!HasComputedPolicies());
delivered_policies_.is_web_secure_context = value;
}
void NavigationPolicyContainerBuilder::SetAllowCrossOriginIsolation(
bool value) {
DCHECK(!HasComputedPolicies());
delivered_policies_.allow_cross_origin_isolation = value;
}
void NavigationPolicyContainerBuilder::AddContentSecurityPolicy(
network::mojom::ContentSecurityPolicyPtr policy) {
DCHECK(!HasComputedPolicies());
DCHECK(policy);
delivered_policies_.content_security_policies.push_back(std::move(policy));
}
void NavigationPolicyContainerBuilder::AddContentSecurityPolicies(
std::vector<network::mojom::ContentSecurityPolicyPtr> policies) {
DCHECK(!HasComputedPolicies());
delivered_policies_.AddContentSecurityPolicies(std::move(policies));
}
void NavigationPolicyContainerBuilder::SetCrossOriginOpenerPolicy(
network::CrossOriginOpenerPolicy coop) {
DCHECK(!HasComputedPolicies());
delivered_policies_.cross_origin_opener_policy = coop;
}
void NavigationPolicyContainerBuilder::SetCrossOriginEmbedderPolicy(
network::CrossOriginEmbedderPolicy coep) {
DCHECK(!HasComputedPolicies());
delivered_policies_.cross_origin_embedder_policy = coep;
}
void NavigationPolicyContainerBuilder::SetDocumentIsolationPolicy(
const network::DocumentIsolationPolicy& dip) {
DCHECK(!HasComputedPolicies());
delivered_policies_.document_isolation_policy = dip;
}
const PolicyContainerPolicies&
NavigationPolicyContainerBuilder::DeliveredPoliciesForTesting() const {
DCHECK(!HasComputedPolicies());
return delivered_policies_;
}
void NavigationPolicyContainerBuilder::ComputePoliciesForError() {
// The decision to commit an error page can happen after receiving the
// response for a regular document. It overrides any previous attempt to
// |ComputePolicies()|.
host_ = nullptr;
DCHECK(!HasComputedPolicies());
// TODO(crbug.com/40747546): We should enforce strict policies on error
// pages.
PolicyContainerPolicies policies;
// We commit error pages with the same address space as the underlying page,
// so that auto-reloading error pages does not show up as a private network
// request (from the unknown/public address space to private). See also
// crbug.com/1180140.
policies.ip_address_space = delivered_policies_.ip_address_space;
SetFinalPolicies(std::move(policies));
DCHECK(HasComputedPolicies());
}
void NavigationPolicyContainerBuilder::ComputeIsWebSecureContext() {
DCHECK(!HasComputedPolicies());
if (!parent_policies_) {
// No parent. Only the trustworthiness of the origin matters.
return;
}
// The child can only be a secure context if the parent is too.
delivered_policies_.is_web_secure_context &=
parent_policies_->is_web_secure_context;
}
void NavigationPolicyContainerBuilder::ComputeSandboxFlags(
bool is_inside_mhtml,
network::mojom::WebSandboxFlags frame_sandbox_flags,
PolicyContainerPolicies& policies) {
DCHECK(!HasComputedPolicies());
auto sandbox_flags_to_commit = frame_sandbox_flags;
// The document can also restrict sandbox further, via its CSP.
for (const auto& csp : policies.content_security_policies) {
sandbox_flags_to_commit |= csp->sandbox;
}
// The URL of a document loaded from a MHTML archive is controlled by the
// Content-Location header. This can be set to an arbitrary URL. This is
// potentially dangerous. For this reason we force the document to be
// sandboxed, providing exceptions only for creating new windows. This
// includes disallowing javascript and using an opaque origin.
if (is_inside_mhtml) {
sandbox_flags_to_commit |= ~network::mojom::WebSandboxFlags::kPopups &
~network::mojom::WebSandboxFlags::
kPropagatesToAuxiliaryBrowsingContexts;
}
policies.sandbox_flags = sandbox_flags_to_commit;
}
void NavigationPolicyContainerBuilder::IncorporateDeliveredPolicies(
const GURL& url,
PolicyContainerPolicies& policies) {
// Delivered content security policies must be appended.
policies.AddContentSecurityPolicies(
mojo::Clone(delivered_policies_.content_security_policies));
// The delivered IP address space (if any) overrides the IP address space.
if (delivered_policies_.ip_address_space !=
network::mojom::IPAddressSpace::kUnknown) {
policies.ip_address_space = delivered_policies_.ip_address_space;
}
}
PolicyContainerPolicies
NavigationPolicyContainerBuilder::ComputeInheritedPolicies(const GURL& url) {
DCHECK(url.SchemeIsLocal()) << url << " should not inherit policies";
if (url.IsAboutSrcdoc()) {
DCHECK(parent_policies_)
<< "About:srcdoc documents should always have a parent frame.";
return parent_policies_->Clone();
}
if (initiator_policies_) {
return initiator_policies_->Clone();
}
return PolicyContainerPolicies();
}
PolicyContainerPolicies NavigationPolicyContainerBuilder::ComputeFinalPolicies(
const GURL& url,
bool is_inside_mhtml,
network::mojom::WebSandboxFlags frame_sandbox_flags,
bool is_credentialless) {
PolicyContainerPolicies policies;
// Policies are either inherited from another document for local scheme, or
// directly set from the delivered response.
if (!url.SchemeIsLocal()) {
policies = delivered_policies_.Clone();
} else if (history_policies_) {
// For a local scheme, history policies should not incorporate delivered
// ones as this may lead to duplication of some policies already stored in
// history. For example, consider the following HTML:
// <iframe src="about:blank" csp="something">
// This will store CSP: something in history. The next time we have a
// history navigation we will have CSP: something twice.
policies = history_policies_->Clone();
} else {
policies = ComputeInheritedPolicies(url);
IncorporateDeliveredPolicies(url, policies);
}
// `can_navigate_top_without_user_gesture` is inherited from the parent.
// Later in `NavigationRequest::CommitNavigation()` it will either be made
// less strict for same-origin navigations, or stricter for cross-origin
// navigations that do not explicitly allow top-level navigation without user
// gesture.
policies.can_navigate_top_without_user_gesture =
parent_policies_ ? parent_policies_->can_navigate_top_without_user_gesture
: true;
ComputeSandboxFlags(is_inside_mhtml, frame_sandbox_flags, policies);
policies.is_credentialless = is_credentialless;
return policies;
}
void NavigationPolicyContainerBuilder::ComputePolicies(
const GURL& url,
bool is_inside_mhtml,
network::mojom::WebSandboxFlags frame_sandbox_flags,
bool is_credentialless) {
DCHECK(!HasComputedPolicies());
ComputeIsWebSecureContext();
SetFinalPolicies(ComputeFinalPolicies(
url, is_inside_mhtml, frame_sandbox_flags, is_credentialless));
}
bool NavigationPolicyContainerBuilder::HasComputedPolicies() const {
return host_ != nullptr;
}
void NavigationPolicyContainerBuilder::SetAllowTopNavigationWithoutUserGesture(
bool allow_top) {
host_->SetCanNavigateTopWithoutUserGesture(allow_top);
}
void NavigationPolicyContainerBuilder::SetFinalPolicies(
PolicyContainerPolicies policies) {
DCHECK(!HasComputedPolicies());
host_ = base::MakeRefCounted<PolicyContainerHost>(std::move(policies));
}
const PolicyContainerPolicies& NavigationPolicyContainerBuilder::FinalPolicies()
const {
DCHECK(HasComputedPolicies());
return host_->policies();
}
blink::mojom::PolicyContainerPtr
NavigationPolicyContainerBuilder::CreatePolicyContainerForBlink() {
DCHECK(HasComputedPolicies());
return host_->CreatePolicyContainerForBlink();
}
scoped_refptr<PolicyContainerHost>
NavigationPolicyContainerBuilder::GetPolicyContainerHost() {
DCHECK(HasComputedPolicies());
CHECK(host_);
return host_;
}
scoped_refptr<PolicyContainerHost>
NavigationPolicyContainerBuilder::TakePolicyContainerHost() && {
DCHECK(HasComputedPolicies());
return std::move(host_);
}
void NavigationPolicyContainerBuilder::ResetForCrossDocumentRestart() {
host_ = nullptr;
delivered_policies_ = PolicyContainerPolicies();
}
} // namespace content