Commit 4473c0ae authored by Antonio Sartori's avatar Antonio Sartori Committed by Chromium LUCI CQ

CSP: Separate parsing and applying in Blink CSP code

This is a small refactoring of the code in blink::CSPDirectiveList
which is parsing new Content Security Policies. This change separates
the raw parsing of the policies from the side effects (applying the
policies and storing state).

This is part of a project to harmonize the CSP code in Blink and in
services/network, and will make it easier to synchronize Content
Security Policies between the two.

Bug: 1021462,1149272
Change-Id: Iace800247ef6165a47adef2601d336a09e930897
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2601368
Commit-Queue: Antonio Sartori <antoniosartori@chromium.org>
Reviewed-by: default avatarMike West <mkwst@chromium.org>
Cr-Commit-Position: refs/heads/master@{#843562}
parent 4d4c8638
...@@ -155,8 +155,7 @@ using network::mojom::ContentSecurityPolicyType; ...@@ -155,8 +155,7 @@ using network::mojom::ContentSecurityPolicyType;
CSPDirectiveList::CSPDirectiveList(ContentSecurityPolicy* policy) CSPDirectiveList::CSPDirectiveList(ContentSecurityPolicy* policy)
: policy_(policy), : policy_(policy),
has_sandbox_policy_(false), block_all_mixed_content_(false),
strict_mixed_content_checking_enforced_(false),
upgrade_insecure_requests_(false), upgrade_insecure_requests_(false),
use_reporting_api_(false) {} use_reporting_api_(false) {}
...@@ -173,25 +172,67 @@ CSPDirectiveList* CSPDirectiveList::Create(ContentSecurityPolicy* policy, ...@@ -173,25 +172,67 @@ CSPDirectiveList* CSPDirectiveList::Create(ContentSecurityPolicy* policy,
directives->Parse(begin, end, should_parse_wasm_eval); directives->Parse(begin, end, should_parse_wasm_eval);
directives->ApplyParsedDirectives();
return directives;
}
void CSPDirectiveList::ApplyParsedDirectives() {
if (block_all_mixed_content_ && !IsReportOnly())
policy_->EnforceStrictMixedContentChecking();
if (RequiresTrustedTypes())
policy_->RequireTrustedTypes();
policy_->EnforceSandboxFlags(sandbox_flags_);
if (upgrade_insecure_requests_)
policy_->UpgradeInsecureRequests();
for (const auto& directive : directives_) {
switch (directive.key) {
case CSPDirectiveName::DefaultSrc:
// TODO(mkwst) It seems unlikely that developers would use different
// algorithms for scripts and styles. We may want to combine the
// usesScriptHashAlgorithms() and usesStyleHashAlgorithms.
policy_->UsesScriptHashAlgorithms(
HashAlgorithmsUsed(directive.value.get()));
policy_->UsesStyleHashAlgorithms(
HashAlgorithmsUsed(directive.value.get()));
break;
case CSPDirectiveName::ScriptSrc:
case CSPDirectiveName::ScriptSrcAttr:
case CSPDirectiveName::ScriptSrcElem:
policy_->UsesScriptHashAlgorithms(
HashAlgorithmsUsed(directive.value.get()));
break;
case CSPDirectiveName::StyleSrc:
case CSPDirectiveName::StyleSrcAttr:
case CSPDirectiveName::StyleSrcElem:
policy_->UsesStyleHashAlgorithms(
HashAlgorithmsUsed(directive.value.get()));
break;
default:
break;
}
}
CSPOperativeDirective directive = CSPOperativeDirective directive =
directives->OperativeDirective(CSPDirectiveName::ScriptSrc); OperativeDirective(CSPDirectiveName::ScriptSrc);
if (!directives->CheckEval(directive.source_list)) { if (!CheckEval(directive.source_list)) {
String message = String message =
"Refused to evaluate a string as JavaScript because 'unsafe-eval' is " "Refused to evaluate a string as JavaScript because 'unsafe-eval' is "
"not an allowed source of script in the following Content Security " "not an allowed source of script in the following Content Security "
"Policy directive: \"" + "Policy directive: \"" +
GetRawDirectiveForMessage(directives->raw_directives_, directive.type) + GetRawDirectiveForMessage(raw_directives_, directive.type) + "\".\n";
"\".\n"; SetEvalDisabledErrorMessage(message);
directives->SetEvalDisabledErrorMessage(message); } else if (RequiresTrustedTypes()) {
} else if (directives->RequiresTrustedTypes()) {
String message = String message =
"Refused to evaluate a string as JavaScript because this document " "Refused to evaluate a string as JavaScript because this document "
"requires 'Trusted Type' assignment."; "requires 'Trusted Type' assignment.";
directives->SetEvalDisabledErrorMessage(message); SetEvalDisabledErrorMessage(message);
} }
return directives;
} }
void CSPDirectiveList::ReportViolation( void CSPDirectiveList::ReportViolation(
...@@ -967,6 +1008,9 @@ bool CSPDirectiveList::ParseDirective(const UChar* begin, ...@@ -967,6 +1008,9 @@ bool CSPDirectiveList::ParseDirective(const UChar* begin,
} }
void CSPDirectiveList::ParseReportTo(const String& name, const String& value) { void CSPDirectiveList::ParseReportTo(const String& name, const String& value) {
if (!base::FeatureList::IsEnabled(network::features::kReporting))
return;
if (!use_reporting_api_) { if (!use_reporting_api_) {
use_reporting_api_ = true; use_reporting_api_ = true;
report_endpoints_.clear(); report_endpoints_.clear();
...@@ -1081,7 +1125,7 @@ void CSPDirectiveList::ParseAndAppendReportEndpoints(const String& value) { ...@@ -1081,7 +1125,7 @@ void CSPDirectiveList::ParseAndAppendReportEndpoints(const String& value) {
: WebFeature::kReportUriSingleEndpoint); : WebFeature::kReportUriSingleEndpoint);
} }
void CSPDirectiveList::ApplySandboxPolicy(const String& name, void CSPDirectiveList::ParseSandboxPolicy(const String& name,
const String& sandbox_policy) { const String& sandbox_policy) {
// Remove sandbox directives in meta policies, per // Remove sandbox directives in meta policies, per
// https://www.w3.org/TR/CSP2/#delivery-html-meta-element. // https://www.w3.org/TR/CSP2/#delivery-html-meta-element.
...@@ -1093,10 +1137,6 @@ void CSPDirectiveList::ApplySandboxPolicy(const String& name, ...@@ -1093,10 +1137,6 @@ void CSPDirectiveList::ApplySandboxPolicy(const String& name,
policy_->ReportInvalidInReportOnly(name); policy_->ReportInvalidInReportOnly(name);
return; return;
} }
if (has_sandbox_policy_) {
policy_->ReportDuplicateDirective(name);
return;
}
using network::mojom::blink::WebSandboxFlags; using network::mojom::blink::WebSandboxFlags;
WebSandboxFlags ignored_flags = WebSandboxFlags ignored_flags =
...@@ -1104,17 +1144,16 @@ void CSPDirectiveList::ApplySandboxPolicy(const String& name, ...@@ -1104,17 +1144,16 @@ void CSPDirectiveList::ApplySandboxPolicy(const String& name,
? WebSandboxFlags::kStorageAccessByUserActivation ? WebSandboxFlags::kStorageAccessByUserActivation
: WebSandboxFlags::kNone; : WebSandboxFlags::kNone;
has_sandbox_policy_ = true;
network::WebSandboxFlagsParsingResult parsed = network::WebSandboxFlagsParsingResult parsed =
network::ParseWebSandboxPolicy(sandbox_policy.Utf8(), ignored_flags); network::ParseWebSandboxPolicy(sandbox_policy.Utf8(), ignored_flags);
policy_->EnforceSandboxFlags(parsed.flags); sandbox_flags_ = parsed.flags;
if (!parsed.error_message.empty()) { if (!parsed.error_message.empty()) {
policy_->ReportInvalidSandboxFlags( policy_->ReportInvalidSandboxFlags(
WebString::FromUTF8(parsed.error_message)); WebString::FromUTF8(parsed.error_message));
} }
} }
void CSPDirectiveList::ApplyTreatAsPublicAddress() { void CSPDirectiveList::ParseTreatAsPublicAddress() {
// Remove treat-as-public-address directives in meta policies, per // Remove treat-as-public-address directives in meta policies, per
// https://wicg.github.io/cors-rfc1918/#csp // https://wicg.github.io/cors-rfc1918/#csp
if (header_->source == ContentSecurityPolicySource::kMeta) { if (header_->source == ContentSecurityPolicySource::kMeta) {
...@@ -1133,34 +1172,22 @@ void CSPDirectiveList::ApplyTreatAsPublicAddress() { ...@@ -1133,34 +1172,22 @@ void CSPDirectiveList::ApplyTreatAsPublicAddress() {
// browser process. // browser process.
} }
void CSPDirectiveList::EnforceStrictMixedContentChecking(const String& name, void CSPDirectiveList::ParseBlockAllMixedContent(const String& name,
const String& value) { const String& value) {
if (strict_mixed_content_checking_enforced_) {
policy_->ReportDuplicateDirective(name);
return;
}
if (!value.IsEmpty()) if (!value.IsEmpty())
policy_->ReportValueForEmptyDirective(name, value); policy_->ReportValueForEmptyDirective(name, value);
strict_mixed_content_checking_enforced_ = true; block_all_mixed_content_ = true;
if (!IsReportOnly())
policy_->EnforceStrictMixedContentChecking();
} }
void CSPDirectiveList::EnableInsecureRequestsUpgrade(const String& name, void CSPDirectiveList::ParseUpgradeInsecureRequests(const String& name,
const String& value) { const String& value) {
if (IsReportOnly()) { if (IsReportOnly()) {
policy_->ReportInvalidInReportOnly(name); policy_->ReportInvalidInReportOnly(name);
return; return;
} }
if (upgrade_insecure_requests_) {
policy_->ReportDuplicateDirective(name);
return;
}
upgrade_insecure_requests_ = true; upgrade_insecure_requests_ = true;
policy_->UpgradeInsecureRequests();
if (!value.IsEmpty()) if (!value.IsEmpty())
policy_->ReportValueForEmptyDirective(name, value); policy_->ReportValueForEmptyDirective(name, value);
} }
...@@ -1184,26 +1211,24 @@ void CSPDirectiveList::AddDirective(const String& name, const String& value) { ...@@ -1184,26 +1211,24 @@ void CSPDirectiveList::AddDirective(const String& name, const String& value) {
switch (type) { switch (type) {
case CSPDirectiveName::BaseURI: case CSPDirectiveName::BaseURI:
directives_.insert(type, CSPSourceListParse(name, value, policy_));
return;
case CSPDirectiveName::BlockAllMixedContent:
EnforceStrictMixedContentChecking(name, value);
return;
case CSPDirectiveName::ChildSrc: case CSPDirectiveName::ChildSrc:
case CSPDirectiveName::ConnectSrc: case CSPDirectiveName::ConnectSrc:
directives_.insert(type, CSPSourceListParse(name, value, policy_));
return;
case CSPDirectiveName::DefaultSrc: case CSPDirectiveName::DefaultSrc:
source_list = CSPSourceListParse(name, value, policy_);
// TODO(mkwst) It seems unlikely that developers would use different
// algorithms for scripts and styles. We may want to combine the
// usesScriptHashAlgorithms() and usesStyleHashAlgorithms.
policy_->UsesScriptHashAlgorithms(HashAlgorithmsUsed(source_list.get()));
policy_->UsesStyleHashAlgorithms(HashAlgorithmsUsed(source_list.get()));
directives_.insert(type, std::move(source_list));
return;
case CSPDirectiveName::FontSrc: case CSPDirectiveName::FontSrc:
case CSPDirectiveName::FormAction: case CSPDirectiveName::FormAction:
case CSPDirectiveName::FrameSrc:
case CSPDirectiveName::ImgSrc:
case CSPDirectiveName::ManifestSrc:
case CSPDirectiveName::MediaSrc:
case CSPDirectiveName::NavigateTo:
case CSPDirectiveName::ObjectSrc:
case CSPDirectiveName::ScriptSrc:
case CSPDirectiveName::ScriptSrcAttr:
case CSPDirectiveName::ScriptSrcElem:
case CSPDirectiveName::StyleSrc:
case CSPDirectiveName::StyleSrcAttr:
case CSPDirectiveName::StyleSrcElem:
case CSPDirectiveName::WorkerSrc:
directives_.insert(type, CSPSourceListParse(name, value, policy_)); directives_.insert(type, CSPSourceListParse(name, value, policy_));
return; return;
case CSPDirectiveName::FrameAncestors: case CSPDirectiveName::FrameAncestors:
...@@ -1215,25 +1240,19 @@ void CSPDirectiveList::AddDirective(const String& name, const String& value) { ...@@ -1215,25 +1240,19 @@ void CSPDirectiveList::AddDirective(const String& name, const String& value) {
directives_.insert(type, CSPSourceListParse(name, value, policy_)); directives_.insert(type, CSPSourceListParse(name, value, policy_));
} }
return; return;
case CSPDirectiveName::FrameSrc:
case CSPDirectiveName::ImgSrc:
case CSPDirectiveName::ManifestSrc:
case CSPDirectiveName::MediaSrc:
case CSPDirectiveName::NavigateTo:
case CSPDirectiveName::ObjectSrc:
directives_.insert(type, CSPSourceListParse(name, value, policy_));
return;
case CSPDirectiveName::PluginTypes:
plugin_types_ = CSPPluginTypesParse(value, policy_);
return;
case CSPDirectiveName::PrefetchSrc: case CSPDirectiveName::PrefetchSrc:
if (!policy_->ExperimentalFeaturesEnabled()) if (!policy_->ExperimentalFeaturesEnabled())
policy_->ReportUnsupportedDirective(name); policy_->ReportUnsupportedDirective(name);
else else
directives_.insert(type, CSPSourceListParse(name, value, policy_)); directives_.insert(type, CSPSourceListParse(name, value, policy_));
return; return;
case CSPDirectiveName::BlockAllMixedContent:
ParseBlockAllMixedContent(name, value);
return;
case CSPDirectiveName::PluginTypes:
plugin_types_ = CSPPluginTypesParse(value, policy_);
return;
case CSPDirectiveName::ReportTo: case CSPDirectiveName::ReportTo:
if (base::FeatureList::IsEnabled(network::features::kReporting))
ParseReportTo(name, value); ParseReportTo(name, value);
return; return;
case CSPDirectiveName::ReportURI: case CSPDirectiveName::ReportURI:
...@@ -1242,41 +1261,22 @@ void CSPDirectiveList::AddDirective(const String& name, const String& value) { ...@@ -1242,41 +1261,22 @@ void CSPDirectiveList::AddDirective(const String& name, const String& value) {
case CSPDirectiveName::RequireTrustedTypesFor: case CSPDirectiveName::RequireTrustedTypesFor:
require_trusted_types_for_ = require_trusted_types_for_ =
CSPRequireTrustedTypesForParse(value, policy_); CSPRequireTrustedTypesForParse(value, policy_);
if (RequiresTrustedTypes())
policy_->RequireTrustedTypes();
return; return;
case CSPDirectiveName::Sandbox: case CSPDirectiveName::Sandbox:
ApplySandboxPolicy(name, value); ParseSandboxPolicy(name, value);
return;
case CSPDirectiveName::ScriptSrc:
case CSPDirectiveName::ScriptSrcAttr:
case CSPDirectiveName::ScriptSrcElem:
source_list = CSPSourceListParse(name, value, policy_);
policy_->UsesScriptHashAlgorithms(HashAlgorithmsUsed(source_list.get()));
directives_.insert(type, std::move(source_list));
return;
case CSPDirectiveName::StyleSrc:
case CSPDirectiveName::StyleSrcAttr:
case CSPDirectiveName::StyleSrcElem:
source_list = CSPSourceListParse(name, value, policy_);
policy_->UsesStyleHashAlgorithms(HashAlgorithmsUsed(source_list.get()));
directives_.insert(type, std::move(source_list));
return; return;
case CSPDirectiveName::TreatAsPublicAddress: case CSPDirectiveName::TreatAsPublicAddress:
ApplyTreatAsPublicAddress(); ParseTreatAsPublicAddress();
return; return;
case CSPDirectiveName::TrustedTypes: case CSPDirectiveName::TrustedTypes:
trusted_types_ = CSPTrustedTypesParse(value, policy_); trusted_types_ = CSPTrustedTypesParse(value, policy_);
return; return;
case CSPDirectiveName::UpgradeInsecureRequests: case CSPDirectiveName::UpgradeInsecureRequests:
EnableInsecureRequestsUpgrade(name, value); ParseUpgradeInsecureRequests(name, value);
return; return;
case CSPDirectiveName::Unknown: case CSPDirectiveName::Unknown:
NOTREACHED(); NOTREACHED();
return; return;
case CSPDirectiveName::WorkerSrc:
directives_.insert(type, CSPSourceListParse(name, value, policy_));
return;
} }
} }
......
...@@ -100,9 +100,7 @@ class CORE_EXPORT CSPDirectiveList final ...@@ -100,9 +100,7 @@ class CORE_EXPORT CSPDirectiveList final
const String& sample, const String& sample,
const String& sample_prefix) const; const String& sample_prefix) const;
bool StrictMixedContentChecking() const { bool StrictMixedContentChecking() const { return block_all_mixed_content_; }
return strict_mixed_content_checking_enforced_;
}
void ReportMixedContent(const KURL& blocked_url, void ReportMixedContent(const KURL& blocked_url,
ResourceRequest::RedirectStatus) const; ResourceRequest::RedirectStatus) const;
...@@ -166,6 +164,7 @@ class CORE_EXPORT CSPDirectiveList final ...@@ -166,6 +164,7 @@ class CORE_EXPORT CSPDirectiveList final
FRIEND_TEST_ALL_PREFIXES(CSPDirectiveListTest, IsMatchingNoncePresent); FRIEND_TEST_ALL_PREFIXES(CSPDirectiveListTest, IsMatchingNoncePresent);
FRIEND_TEST_ALL_PREFIXES(CSPDirectiveListTest, OperativeDirectiveGivenType); FRIEND_TEST_ALL_PREFIXES(CSPDirectiveListTest, OperativeDirectiveGivenType);
void ApplyParsedDirectives();
bool ParseDirective(const UChar* begin, bool ParseDirective(const UChar* begin,
const UChar* end, const UChar* end,
String* name, String* name,
...@@ -175,11 +174,10 @@ class CORE_EXPORT CSPDirectiveList final ...@@ -175,11 +174,10 @@ class CORE_EXPORT CSPDirectiveList final
void ParseAndAppendReportEndpoints(const String& value); void ParseAndAppendReportEndpoints(const String& value);
void ParsePluginTypes(const String& name, const String& value); void ParsePluginTypes(const String& name, const String& value);
void AddDirective(const String& name, const String& value); void AddDirective(const String& name, const String& value);
void ApplySandboxPolicy(const String& name, const String& sandbox_policy); void ParseSandboxPolicy(const String& name, const String& sandbox_policy);
void ApplyTreatAsPublicAddress(); void ParseTreatAsPublicAddress();
void EnforceStrictMixedContentChecking(const String& name, void ParseBlockAllMixedContent(const String& name, const String& value);
const String& value); void ParseUpgradeInsecureRequests(const String& name, const String& value);
void EnableInsecureRequestsUpgrade(const String& name, const String& value);
CSPDirectiveName FallbackDirective(CSPDirectiveName current_directive, CSPDirectiveName FallbackDirective(CSPDirectiveName current_directive,
CSPDirectiveName original_directive) const; CSPDirectiveName original_directive) const;
...@@ -279,9 +277,9 @@ class CORE_EXPORT CSPDirectiveList final ...@@ -279,9 +277,9 @@ class CORE_EXPORT CSPDirectiveList final
HashMap<CSPDirectiveName, String> raw_directives_; HashMap<CSPDirectiveName, String> raw_directives_;
bool has_sandbox_policy_; network::mojom::blink::WebSandboxFlags sandbox_flags_;
bool strict_mixed_content_checking_enforced_; bool block_all_mixed_content_;
bool upgrade_insecure_requests_; bool upgrade_insecure_requests_;
......
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