Skip to content

Commit

Permalink
[OutOfBlinkSandbox] Move parser to services/network.
Browse files Browse the repository at this point in the history
Move the sandbox flags parser from blink to network.

Design doc:
https://docs.google.com/document/d/1PechV73KKMF8leh7uTlyGkR32kD5qILvu3ZeG8TYGfk/edit?usp=drive_web&ouid=112561809712693690020

Bug: 1041376
Change-Id: Ifbae41fa9e8b3ad86e6dca29594b7a1e9c5be8a7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2129699
Commit-Queue: Arthur Sonzogni <arthursonzogni@chromium.org>
Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
Cr-Commit-Position: refs/heads/master@{#761883}
  • Loading branch information
ArthurSonzogni authored and Commit Bot committed Apr 23, 2020
1 parent ce8e90d commit 7a3477d
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 94 deletions.
1 change: 1 addition & 0 deletions services/network/public/cpp/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ jumbo_component("cpp") {
"source_stream_to_data_pipe.h",
"weak_wrapper_shared_url_loader_factory.cc",
"weak_wrapper_shared_url_loader_factory.h",
"web_sandbox_flags.cc",
"web_sandbox_flags.h",
"wrapper_shared_url_loader_factory.cc",
"wrapper_shared_url_loader_factory.h",
Expand Down
89 changes: 89 additions & 0 deletions services/network/public/cpp/web_sandbox_flags.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// 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/public/cpp/web_sandbox_flags.h"
#include <set>
#include "base/stl_util.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "services/network/public/mojom/web_sandbox_flags.mojom.h"

namespace network {

using mojom::WebSandboxFlags;
namespace {

// See: https://infra.spec.whatwg.org/#ascii-whitespace
// This is different from: base::kWhitespaceASCII.
const char* kHtmlWhitespace = " \n\t\r\f";

WebSandboxFlags ParseWebSandboxToken(const base::StringPiece& token) {
constexpr struct {
const char* token;
WebSandboxFlags flags;
} table[] = {
{"allow-downloads", WebSandboxFlags::kDownloads},
{"allow-forms", WebSandboxFlags::kForms},
{"allow-modals", WebSandboxFlags::kModals},
{"allow-orientation-lock", WebSandboxFlags::kOrientationLock},
{"allow-pointer-lock", WebSandboxFlags::kPointerLock},
{"allow-popups", WebSandboxFlags::kPopups},
{"allow-popups-to-escape-sandbox",
WebSandboxFlags::kPropagatesToAuxiliaryBrowsingContexts},
{"allow-presentation", WebSandboxFlags::kPresentationController},
{"allow-same-origin", WebSandboxFlags::kOrigin},
{"allow-scripts",
WebSandboxFlags::kAutomaticFeatures | WebSandboxFlags::kScripts},
{"allow-storage-access-by-user-activation",
WebSandboxFlags::kStorageAccessByUserActivation},
{"allow-top-navigation", WebSandboxFlags::kTopNavigation},
{"allow-top-navigation-by-user-activation",
WebSandboxFlags::kTopNavigationByUserActivation},
};

for (const auto& it : table) {
if (CompareCaseInsensitiveASCII(it.token, token) == 0)
return it.flags;
}

return WebSandboxFlags::kNone; // Not found.
}

} // namespace

// See: http://www.w3.org/TR/html5/the-iframe-element.html#attr-iframe-sandbox
WebSandboxFlagsParsingResult ParseWebSandboxPolicy(
const base::StringPiece& input,
WebSandboxFlags ignored_flags) {
WebSandboxFlagsParsingResult out;
out.flags = WebSandboxFlags::kAll;

std::vector<base::StringPiece> error_tokens;
for (const auto& token :
base::SplitStringPiece(input, kHtmlWhitespace, base::KEEP_WHITESPACE,
base::SPLIT_WANT_NONEMPTY)) {
WebSandboxFlags flags = ~ParseWebSandboxToken(token);
flags |= ignored_flags;
out.flags &= flags;
if (flags == WebSandboxFlags::kAll)
error_tokens.push_back(token);
}

if (!error_tokens.empty()) {
// Some tests expect the order of error tokens to be preserved, while
// removing the duplicates:
// See /fast/frames/sandboxed-iframe-attribute-parsing-03.html
std::set<base::StringPiece> set;
base::EraseIf(error_tokens, [&](auto x) { return !set.insert(x).second; });

out.error_message =
"'" + base::JoinString(error_tokens, "', '") +
(error_tokens.size() > 1 ? "' are invalid sandbox flags."
: "' is an invalid sandbox flag.");
}

return out;
}

} // namespace network
34 changes: 34 additions & 0 deletions services/network/public/cpp/web_sandbox_flags.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
#define SERVICES_NETWORK_PUBLIC_CPP_WEB_SANDBOX_FLAGS_H_

#include <cstdint>
#include <string>
#include "base/component_export.h"
#include "base/strings/string_piece_forward.h"

namespace network {
namespace mojom {
Expand All @@ -28,11 +31,42 @@ inline WebSandboxFlags& operator|=(WebSandboxFlags& a, WebSandboxFlags b) {
return a = a | b;
}

inline WebSandboxFlags& operator&=(WebSandboxFlags& a, WebSandboxFlags b) {
return a = a & b;
}

inline constexpr WebSandboxFlags operator~(WebSandboxFlags flags) {
return static_cast<WebSandboxFlags>(~static_cast<int>(flags));
}

} // namespace mojom

// The output of |ParseSandboxPolicy(input)|.
struct WebSandboxFlagsParsingResult {
// The complement of the parsed WebSandboxFlags policy.
// TODO(arthursonzogni): Update the caller of ParseWebSandboxPolicy(). They
// should directly use the policy instead of its complement.
mojom::WebSandboxFlags flags;

// The console error message to be displayed for invalid input. Empty when
// there are no errors.
std::string error_message;
};

// Parses a WebSandboxPolicy. The input is an unordered set of unique
// space-separated sandbox tokens.
// See: http://www.w3.org/TR/html5/the-iframe-element.html#attr-iframe-sandbox
//
// |ignored_flags| is used by experimental features to ignore some tokens when
// the corresponding feature is off.
//
// Supposed to be called only from a (semi-)sandboxed processes, i.e. from blink
// or from the network process. See: docs/security/rule-of-2.md.
COMPONENT_EXPORT(NETWORK_CPP)
WebSandboxFlagsParsingResult ParseWebSandboxPolicy(
const base::StringPiece& input,
mojom::WebSandboxFlags ignored_flags);

} // namespace network

#endif // SERVICES_NETWORK_PUBLIC_CPP_SANDBOX_FLAGS_H_
23 changes: 16 additions & 7 deletions third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <utility>

#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/web_sandbox_flags.h"
#include "services/network/public/mojom/content_security_policy.mojom-shared.h"
#include "third_party/blink/renderer/bindings/core/v8/source_location.h"
#include "third_party/blink/renderer/core/dom/document.h"
Expand All @@ -22,6 +23,7 @@
#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/network/content_security_policy_parsers.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/wtf/text/base64.h"
#include "third_party/blink/renderer/platform/wtf/text/parsing_utilities.h"
Expand Down Expand Up @@ -1224,14 +1226,21 @@ void CSPDirectiveList::ApplySandboxPolicy(const String& name,
policy_->ReportDuplicateDirective(name);
return;
}

using network::mojom::blink::WebSandboxFlags;
WebSandboxFlags ignored_flags =
!RuntimeEnabledFeatures::StorageAccessAPIEnabled()
? WebSandboxFlags::kStorageAccessByUserActivation
: WebSandboxFlags::kNone;

has_sandbox_policy_ = true;
String invalid_tokens;
SpaceSplitString policy_tokens =
SpaceSplitString(AtomicString(sandbox_policy));
policy_->EnforceSandboxFlags(
ParseSandboxPolicy(policy_tokens, invalid_tokens));
if (!invalid_tokens.IsNull())
policy_->ReportInvalidSandboxFlags(invalid_tokens);
network::WebSandboxFlagsParsingResult parsed =
network::ParseWebSandboxPolicy(sandbox_policy.Utf8(), ignored_flags);
policy_->EnforceSandboxFlags(parsed.flags);
if (!parsed.error_message.empty()) {
policy_->ReportInvalidSandboxFlags(
WebString::FromUTF8(parsed.error_message));
}
}

void CSPDirectiveList::AddTrustedTypes(const String& name,
Expand Down
72 changes: 0 additions & 72 deletions third_party/blink/renderer/core/frame/sandbox_flags.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,6 @@
#include "services/network/public/mojom/web_sandbox_flags.mojom-blink.h"
#include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
#include "third_party/blink/renderer/core/feature_policy/feature_policy_parser.h"
#include "third_party/blink/renderer/core/html/html_iframe_element.h"
#include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"

namespace blink {

Expand Down Expand Up @@ -80,72 +74,6 @@ SandboxFlagsImplementedByFeaturePolicy() {
return mask;
}

network::mojom::blink::WebSandboxFlags ParseSandboxPolicy(
const SpaceSplitString& policy,
String& invalid_tokens_error_message) {
// http://www.w3.org/TR/html5/the-iframe-element.html#attr-iframe-sandbox
// Parse the unordered set of unique space-separated tokens.
network::mojom::blink::WebSandboxFlags flags =
network::mojom::blink::WebSandboxFlags::kAll;
unsigned length = policy.size();
unsigned number_of_token_errors = 0;
StringBuilder token_errors;

using SandboxFlags = network::mojom::blink::WebSandboxFlags;

for (unsigned index = 0; index < length; index++) {
// Turn off the corresponding sandbox flag if it's set as "allowed".
String sandbox_token(policy[index]);
if (EqualIgnoringASCIICase(sandbox_token, "allow-same-origin")) {
flags = flags & ~SandboxFlags::kOrigin;
} else if (EqualIgnoringASCIICase(sandbox_token, "allow-forms")) {
flags = flags & ~SandboxFlags::kForms;
} else if (EqualIgnoringASCIICase(sandbox_token, "allow-scripts")) {
flags = flags & ~SandboxFlags::kScripts;
flags = flags & ~SandboxFlags::kAutomaticFeatures;
} else if (EqualIgnoringASCIICase(sandbox_token, "allow-top-navigation")) {
flags = flags & ~SandboxFlags::kTopNavigation;
} else if (EqualIgnoringASCIICase(sandbox_token, "allow-popups")) {
flags = flags & ~SandboxFlags::kPopups;
} else if (EqualIgnoringASCIICase(sandbox_token, "allow-pointer-lock")) {
flags = flags & ~SandboxFlags::kPointerLock;
} else if (EqualIgnoringASCIICase(sandbox_token,
"allow-orientation-lock")) {
flags = flags & ~SandboxFlags::kOrientationLock;
} else if (EqualIgnoringASCIICase(sandbox_token,
"allow-popups-to-escape-sandbox")) {
flags = flags & ~SandboxFlags::kPropagatesToAuxiliaryBrowsingContexts;
} else if (EqualIgnoringASCIICase(sandbox_token, "allow-modals")) {
flags = flags & ~SandboxFlags::kModals;
} else if (EqualIgnoringASCIICase(sandbox_token, "allow-presentation")) {
flags = flags & ~SandboxFlags::kPresentationController;
} else if (EqualIgnoringASCIICase(
sandbox_token, "allow-top-navigation-by-user-activation")) {
flags = flags & ~SandboxFlags::kTopNavigationByUserActivation;
} else if (EqualIgnoringASCIICase(sandbox_token, "allow-downloads")) {
flags = flags & ~SandboxFlags::kDownloads;
} else if (RuntimeEnabledFeatures::StorageAccessAPIEnabled() &&
EqualIgnoringASCIICase(
sandbox_token, "allow-storage-access-by-user-activation")) {
flags = flags & ~SandboxFlags::kStorageAccessByUserActivation;
} else {
token_errors.Append(token_errors.IsEmpty() ? "'" : ", '");
token_errors.Append(sandbox_token);
token_errors.Append("'");
number_of_token_errors++;
}
}

if (number_of_token_errors) {
token_errors.Append(number_of_token_errors > 1
? " are invalid sandbox flags."
: " is an invalid sandbox flag.");
invalid_tokens_error_message = token_errors.ToString();
}

return flags;
}

// Removes a certain set of flags from |sandbox_flags| for which we have feature
// policies implemented.
network::mojom::blink::WebSandboxFlags
Expand Down
5 changes: 0 additions & 5 deletions third_party/blink/renderer/core/frame/sandbox_flags.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
#include "services/network/public/mojom/web_sandbox_flags.mojom-blink-forward.h"
#include "third_party/blink/public/common/feature_policy/feature_policy.h"
#include "third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom-blink-forward.h"
#include "third_party/blink/renderer/core/dom/space_split_string.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"

namespace blink {
Expand All @@ -43,10 +42,6 @@ using SandboxFlagFeaturePolicyPairs =
// yet migrated to using feature policies.
const SandboxFlagFeaturePolicyPairs& SandboxFlagsWithFeaturePolicies();

network::mojom::blink::WebSandboxFlags ParseSandboxPolicy(
const SpaceSplitString& policy,
String& invalid_tokens_error_message);

// With FeaturePolicyForSandbox most sandbox flags will be represented with
// features. This method returns the part of sandbox flags which were not mapped
// to corresponding features.
Expand Down
31 changes: 21 additions & 10 deletions third_party/blink/renderer/core/html/html_iframe_element.cc
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,30 @@ void HTMLIFrameElement::ParseAttribute(
FrameOwnerPropertiesChanged();
} else if (name == html_names::kSandboxAttr) {
sandbox_->DidUpdateAttributeValue(params.old_value, value);
String invalid_tokens;
bool feature_policy_for_sandbox =
RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled();

network::mojom::blink::WebSandboxFlags current_flags =
value.IsNull()
? network::mojom::blink::WebSandboxFlags::kNone
: ParseSandboxPolicy(sandbox_->TokenSet(), invalid_tokens);
network::mojom::blink::WebSandboxFlags::kNone;
if (!value.IsNull()) {
using network::mojom::blink::WebSandboxFlags;
WebSandboxFlags ignored_flags =
!RuntimeEnabledFeatures::StorageAccessAPIEnabled()
? WebSandboxFlags::kStorageAccessByUserActivation
: WebSandboxFlags::kNone;

auto parsed = network::ParseWebSandboxPolicy(sandbox_->value().Utf8(),
ignored_flags);
current_flags = parsed.flags;
if (!parsed.error_message.empty()) {
GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
mojom::blink::ConsoleMessageSource::kOther,
mojom::blink::ConsoleMessageLevel::kError,
WebString::FromUTF8(
"Error while parsing the 'sandbox' attribute: " +
parsed.error_message)));
}
}
SetAllowedToDownload(
(current_flags & network::mojom::blink::WebSandboxFlags::kDownloads) ==
network::mojom::blink::WebSandboxFlags::kNone);
Expand All @@ -178,12 +195,6 @@ void HTMLIFrameElement::ParseAttribute(
current_flags & ~sandbox_to_set;
}
SetSandboxFlags(sandbox_to_set);
if (!invalid_tokens.IsNull()) {
GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
mojom::ConsoleMessageSource::kOther,
mojom::ConsoleMessageLevel::kError,
"Error while parsing the 'sandbox' attribute: " + invalid_tokens));
}
if (RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled()) {
Vector<String> messages;
UpdateContainerPolicy(&messages);
Expand Down

0 comments on commit 7a3477d

Please sign in to comment.