| // Copyright 2015 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/csp/directive_list.h" |
| |
| #include "base/base64.h" |
| #include "base/stringprintf.h" |
| #include "cobalt/csp/crypto.h" |
| #include "cobalt/csp/media_list_directive.h" |
| #include "cobalt/csp/source_list_directive.h" |
| |
| namespace cobalt { |
| namespace csp { |
| |
| namespace { |
| |
| std::string GetSha256String(const std::string& content) { |
| DigestValue digest; |
| if (!ComputeDigest(kHashAlgorithmSha256, content.c_str(), content.length(), |
| &digest) || |
| digest.size() == 0) { |
| return "sha256-..."; |
| } |
| |
| base::StringPiece digest_piece(reinterpret_cast<char*>(&digest[0]), |
| digest.size()); |
| std::string encoded; |
| bool ok = base::Base64Encode(digest_piece, &encoded); |
| if (ok) { |
| return "'sha256-" + encoded + "'"; |
| } else { |
| DLOG(WARNING) << "Base64Encode failed on " << content; |
| return "sha256-..."; |
| } |
| } |
| |
| std::string ElidedUrl(const GURL& url) { |
| // Emulate KURL::elidedString() in Blink. |
| |
| std::string url_spec = url.spec(); |
| const size_t len = url_spec.length(); |
| if (len < 1024) { |
| return url_spec; |
| } else { |
| return url_spec.substr(0, 511) + "..." + url_spec.substr(len - 510, 510); |
| } |
| } |
| } // namespace |
| |
| DirectiveList::DirectiveList(ContentSecurityPolicy* policy, |
| const base::StringPiece& text, HeaderType type, |
| HeaderSource source) |
| : policy_(policy), |
| header_type_(type), |
| header_source_(source), |
| report_only_(type == kHeaderTypeReport), |
| has_sandbox_policy_(false), |
| has_suborigin_policy_(false), |
| reflected_xss_disposition_(kReflectedXSSUnset), |
| did_set_referrer_policy_(false), |
| referrer_policy_(kReferrerPolicyDefault), |
| strict_mixed_content_checking_enforced_(false), |
| upgrade_insecure_requests_(false) { |
| Parse(text); |
| if (!CheckEval(OperativeDirective(script_src_.get()))) { |
| std::string message = |
| "Refused to evaluate a string as JavaScript because 'unsafe-eval' is " |
| "not an allowed source of script in the following Content Security " |
| "Policy directive: \"" + |
| OperativeDirective(script_src_.get())->text() + "\".\n"; |
| set_eval_disabled_error_message(message); |
| } |
| if (report_only() && report_endpoints_.empty()) { |
| policy->ReportMissingReportURI(text.as_string()); |
| } |
| } |
| |
| DirectiveList::~DirectiveList() {} |
| |
| void DirectiveList::ReportViolation(const std::string& directive_text, |
| const std::string& effective_directive, |
| const std::string& console_message, |
| const GURL& blocked_url) const { |
| std::string message = |
| report_only_ ? "[Report Only] " + console_message : console_message; |
| policy_->ReportViolation(directive_text, effective_directive, message, |
| blocked_url, report_endpoints_, header_); |
| } |
| |
| void DirectiveList::ReportViolationWithLocation( |
| const std::string& directive_text, const std::string& effective_directive, |
| const std::string& console_message, const GURL& blocked_url, |
| const std::string& context_url, int context_line) const { |
| std::string message = base::StringPrintf( |
| "%s%s %s:%d", report_only_ ? "[Report Only] " : "", |
| console_message.c_str(), context_url.c_str(), context_line); |
| policy_->ReportViolation(directive_text, effective_directive, message, |
| blocked_url, report_endpoints_, header_); |
| } |
| |
| bool DirectiveList::CheckEval(SourceListDirective* directive) const { |
| return !directive || directive->AllowEval(); |
| } |
| |
| bool DirectiveList::CheckInline(SourceListDirective* directive) const { |
| return !directive || |
| (directive->AllowInline() && !directive->hash_or_nonce_present()); |
| } |
| |
| bool DirectiveList::CheckNonce(SourceListDirective* directive, |
| const std::string& nonce) const { |
| return !directive || directive->AllowNonce(nonce); |
| } |
| |
| bool DirectiveList::CheckHash(SourceListDirective* directive, |
| const HashValue& hashValue) const { |
| return !directive || directive->AllowHash(hashValue); |
| } |
| |
| bool DirectiveList::CheckSource( |
| SourceListDirective* directive, const GURL& url, |
| ContentSecurityPolicy::RedirectStatus redirect_status) const { |
| return !directive || directive->Allows(url, redirect_status); |
| } |
| |
| bool DirectiveList::CheckMediaType(MediaListDirective* directive, |
| const std::string& type, |
| const std::string& type_attribute) const { |
| if (!directive) { |
| return true; |
| } |
| std::string trimmed_type_attribute; |
| TrimWhitespaceASCII(type_attribute, TRIM_ALL, &trimmed_type_attribute); |
| |
| if (type_attribute.empty() || trimmed_type_attribute != type) { |
| return false; |
| } |
| return directive->Allows(type); |
| } |
| |
| SourceListDirective* DirectiveList::OperativeDirective( |
| SourceListDirective* directive) const { |
| return directive ? directive : default_src_.get(); |
| } |
| |
| SourceListDirective* DirectiveList::OperativeDirective( |
| SourceListDirective* directive, |
| SourceListDirective* directive_override) const { |
| return directive ? directive : directive_override; |
| } |
| |
| bool DirectiveList::CheckEvalAndReportViolation( |
| SourceListDirective* directive, const std::string& console_message) const { |
| if (CheckEval(directive)) { |
| return true; |
| } |
| |
| std::string suffix; |
| if (directive == default_src_) { |
| suffix = |
| " Note that 'script-src' was not explicitly set, so 'default-src' is " |
| "used as a fallback."; |
| } |
| |
| ReportViolation( |
| directive->text(), ContentSecurityPolicy::kScriptSrc, |
| console_message + "\"" + directive->text() + "\"." + suffix + "\n", |
| GURL()); |
| if (report_only_) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| bool DirectiveList::CheckMediaTypeAndReportViolation( |
| MediaListDirective* directive, const std::string& type, |
| const std::string& type_attribute, |
| const std::string& console_message) const { |
| if (CheckMediaType(directive, type, type_attribute)) { |
| return true; |
| } |
| |
| std::string message = console_message + "\'" + directive->text() + "\'."; |
| if (type_attribute.empty()) { |
| message += |
| " When enforcing the 'plugin-types' directive, the plugin's " |
| "media type must be explicitly declared with a 'type' attribute " |
| "on the containing element (e.g. '<object type=\"[TYPE GOES " |
| "HERE]\" ...>')."; |
| } |
| |
| ReportViolation(directive->text(), ContentSecurityPolicy::kPluginTypes, |
| message + "\n", GURL()); |
| return deny_if_enforcing_policy(); |
| } |
| |
| bool DirectiveList::CheckInlineAndReportViolation( |
| SourceListDirective* directive, const std::string& console_message, |
| const std::string& context_url, int context_line, bool is_script, |
| const std::string& hash_value) const { |
| if (CheckInline(directive)) { |
| return true; |
| } |
| |
| std::string suffix; |
| if (directive->AllowInline() && directive->hash_or_nonce_present()) { |
| // If inline is allowed, but a hash or nonce is present, we ignore |
| // 'unsafe-inline'. Throw a reasonable error. |
| suffix = |
| " Note that 'unsafe-inline' is ignored if either a hash or nonce value " |
| "is present in the source list."; |
| } else if (directive->hash_or_nonce_present()) { |
| suffix = |
| " Either the 'unsafe-inline' keyword, a hash (" + hash_value + |
| "), or a nonce ('nonce-...') is required to enable inline execution."; |
| DigestValue digest_value; |
| HashAlgorithm hash_algorithm; |
| SourceList::ParseHash(hash_value.c_str(), |
| hash_value.c_str() + hash_value.length(), |
| &digest_value, &hash_algorithm); |
| if (directive->AllowHash(HashValue(hash_algorithm, digest_value))) { |
| return true; |
| } |
| } else { |
| suffix = |
| " Either the 'unsafe-inline' keyword, a hash (" + hash_value + |
| "), or a nonce ('nonce-...') is required to enable inline execution."; |
| if (directive == default_src_) |
| suffix = suffix + " Note also that '" + |
| std::string(is_script ? "script" : "style") + |
| "-src' was not explicitly set, so 'default-src' is used as a " |
| "fallback."; |
| } |
| |
| ReportViolationWithLocation( |
| directive->text(), is_script ? ContentSecurityPolicy::kScriptSrc |
| : ContentSecurityPolicy::kStyleSrc, |
| console_message + "\"" + directive->text() + "\"." + suffix + "\n", |
| GURL(), context_url, context_line); |
| |
| if (!report_only_) { |
| if (is_script) { |
| // policy_->ReportBlockedScriptExecutionToInspector(directive->text()); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| bool DirectiveList::CheckSourceAndReportViolation( |
| SourceListDirective* directive, const GURL& url, |
| const std::string& effective_directive, |
| ContentSecurityPolicy::RedirectStatus redirect_status) const { |
| if (CheckSource(directive, url, redirect_status)) { |
| return true; |
| } |
| |
| std::string prefix; |
| if (ContentSecurityPolicy::kBaseURI == effective_directive) { |
| prefix = "Refused to set the document's base URI to '"; |
| } else if (ContentSecurityPolicy::kChildSrc == effective_directive) { |
| prefix = "Refused to create a child context containing '"; |
| } else if (ContentSecurityPolicy::kConnectSrc == effective_directive) { |
| prefix = "Refused to connect to '"; |
| } else if (ContentSecurityPolicy::kFontSrc == effective_directive) { |
| prefix = "Refused to load the font '"; |
| } else if (ContentSecurityPolicy::kFormAction == effective_directive) { |
| prefix = "Refused to send form data to '"; |
| } else if (ContentSecurityPolicy::kFrameSrc == effective_directive) { |
| prefix = "Refused to frame '"; |
| } else if (ContentSecurityPolicy::kImgSrc == effective_directive) { |
| prefix = "Refused to load the image '"; |
| } else if (ContentSecurityPolicy::kLocationSrc == effective_directive) { |
| prefix = "Refused to navigate to '"; |
| } else if (ContentSecurityPolicy::kMediaSrc == effective_directive) { |
| prefix = "Refused to load media from '"; |
| } else if (ContentSecurityPolicy::kManifestSrc == effective_directive) { |
| prefix = "Refused to load manifest from '"; |
| } else if (ContentSecurityPolicy::kObjectSrc == effective_directive) { |
| prefix = "Refused to load plugin data from '"; |
| } else if (ContentSecurityPolicy::kScriptSrc == effective_directive) { |
| prefix = "Refused to load the script '"; |
| } else if (ContentSecurityPolicy::kStyleSrc == effective_directive) { |
| prefix = "Refused to load the stylesheet '"; |
| } |
| |
| std::string suffix = std::string(); |
| if (directive == default_src_) |
| suffix = |
| " Note that '" + effective_directive + |
| "' was not explicitly set, so 'default-src' is used as a fallback."; |
| |
| ReportViolation(directive->text(), effective_directive, |
| prefix + ElidedUrl(url) + |
| "' because it violates the following Content Security " |
| "Policy directive: \"" + |
| directive->text() + "\"." + suffix + "\n", |
| url); |
| return deny_if_enforcing_policy(); |
| } |
| |
| bool DirectiveList::AllowJavaScriptURLs( |
| const std::string& context_url, int context_line, |
| ContentSecurityPolicy::ReportingStatus reporting_status) const { |
| if (reporting_status == ContentSecurityPolicy::kSendReport) { |
| return CheckInlineAndReportViolation( |
| OperativeDirective(script_src_.get()), |
| "Refused to execute JavaScript URL because it violates the following " |
| "Content Security Policy directive: ", |
| context_url, context_line, true, "sha256-..."); |
| } |
| return CheckInline(OperativeDirective(script_src_.get())); |
| } |
| |
| bool DirectiveList::AllowInlineEventHandlers( |
| const std::string& context_url, int context_line, |
| ContentSecurityPolicy::ReportingStatus reporting_status) const { |
| if (reporting_status == ContentSecurityPolicy::kSendReport) { |
| return CheckInlineAndReportViolation( |
| OperativeDirective(script_src_.get()), |
| "Refused to execute inline event handler because it violates the " |
| "following Content Security Policy directive: ", |
| context_url, context_line, true, "sha256-..."); |
| } |
| return CheckInline(OperativeDirective(script_src_.get())); |
| } |
| |
| bool DirectiveList::AllowInlineScript( |
| const std::string& context_url, int context_line, |
| ContentSecurityPolicy::ReportingStatus reporting_status, |
| const std::string& content) const { |
| if (reporting_status == ContentSecurityPolicy::kSendReport) { |
| return CheckInlineAndReportViolation( |
| OperativeDirective(script_src_.get()), |
| "Refused to execute inline script because it violates the following " |
| "Content Security Policy directive: ", |
| context_url, context_line, true, GetSha256String(content)); |
| } |
| return CheckInline(OperativeDirective(script_src_.get())); |
| } |
| |
| bool DirectiveList::AllowInlineStyle( |
| const std::string& context_url, int context_line, |
| ContentSecurityPolicy::ReportingStatus reporting_status, |
| const std::string& content) const { |
| if (reporting_status == ContentSecurityPolicy::kSendReport) { |
| return CheckInlineAndReportViolation( |
| OperativeDirective(style_src_.get()), |
| "Refused to apply inline style because it violates the following " |
| "Content Security Policy directive: ", |
| context_url, context_line, false, GetSha256String(content)); |
| } |
| return CheckInline(OperativeDirective(style_src_.get())); |
| } |
| |
| bool DirectiveList::AllowEval( |
| ContentSecurityPolicy::ReportingStatus reporting_status) const { |
| if (reporting_status == ContentSecurityPolicy::kSendReport) { |
| return CheckEvalAndReportViolation( |
| OperativeDirective(script_src_.get()), |
| "Refused to evaluate a string as JavaScript because 'unsafe-eval' is " |
| "not an allowed source of script in the following Content Security " |
| "Policy directive: "); |
| } |
| return CheckEval(OperativeDirective(script_src_.get())); |
| } |
| |
| bool DirectiveList::AllowScriptFromSource( |
| const GURL& url, ContentSecurityPolicy::RedirectStatus redirect_status, |
| ContentSecurityPolicy::ReportingStatus reporting_status) const { |
| return reporting_status == ContentSecurityPolicy::kSendReport |
| ? CheckSourceAndReportViolation( |
| OperativeDirective(script_src_.get()), url, |
| ContentSecurityPolicy::kScriptSrc, redirect_status) |
| : CheckSource(OperativeDirective(script_src_.get()), url, |
| redirect_status); |
| } |
| |
| bool DirectiveList::AllowObjectFromSource( |
| const GURL& url, ContentSecurityPolicy::RedirectStatus redirect_status, |
| ContentSecurityPolicy::ReportingStatus reporting_status) const { |
| return reporting_status == ContentSecurityPolicy::kSendReport |
| ? CheckSourceAndReportViolation( |
| OperativeDirective(object_src_.get()), url, |
| ContentSecurityPolicy::kObjectSrc, redirect_status) |
| : CheckSource(OperativeDirective(object_src_.get()), url, |
| redirect_status); |
| } |
| |
| bool DirectiveList::AllowImageFromSource( |
| const GURL& url, ContentSecurityPolicy::RedirectStatus redirect_status, |
| ContentSecurityPolicy::ReportingStatus reporting_status) const { |
| return reporting_status == ContentSecurityPolicy::kSendReport |
| ? CheckSourceAndReportViolation( |
| OperativeDirective(img_src_.get()), url, |
| ContentSecurityPolicy::kImgSrc, redirect_status) |
| : CheckSource(OperativeDirective(img_src_.get()), url, |
| redirect_status); |
| } |
| |
| bool DirectiveList::AllowStyleFromSource( |
| const GURL& url, ContentSecurityPolicy::RedirectStatus redirect_status, |
| ContentSecurityPolicy::ReportingStatus reporting_status) const { |
| return reporting_status == ContentSecurityPolicy::kSendReport |
| ? CheckSourceAndReportViolation( |
| OperativeDirective(style_src_.get()), url, |
| ContentSecurityPolicy::kStyleSrc, redirect_status) |
| : CheckSource(OperativeDirective(style_src_.get()), url, |
| redirect_status); |
| } |
| |
| bool DirectiveList::AllowFontFromSource( |
| const GURL& url, ContentSecurityPolicy::RedirectStatus redirect_status, |
| ContentSecurityPolicy::ReportingStatus reporting_status) const { |
| return reporting_status == ContentSecurityPolicy::kSendReport |
| ? CheckSourceAndReportViolation( |
| OperativeDirective(font_src_.get()), url, |
| ContentSecurityPolicy::kFontSrc, redirect_status) |
| : CheckSource(OperativeDirective(font_src_.get()), url, |
| redirect_status); |
| } |
| |
| bool DirectiveList::AllowMediaFromSource( |
| const GURL& url, ContentSecurityPolicy::RedirectStatus redirect_status, |
| ContentSecurityPolicy::ReportingStatus reporting_status) const { |
| return reporting_status == ContentSecurityPolicy::kSendReport |
| ? CheckSourceAndReportViolation( |
| OperativeDirective(media_src_.get()), url, |
| ContentSecurityPolicy::kMediaSrc, redirect_status) |
| : CheckSource(OperativeDirective(media_src_.get()), url, |
| redirect_status); |
| } |
| |
| bool DirectiveList::AllowManifestFromSource( |
| const GURL& url, ContentSecurityPolicy::RedirectStatus redirect_status, |
| ContentSecurityPolicy::ReportingStatus reporting_status) const { |
| return reporting_status == ContentSecurityPolicy::kSendReport |
| ? CheckSourceAndReportViolation( |
| OperativeDirective(manifest_src_.get()), url, |
| ContentSecurityPolicy::kManifestSrc, redirect_status) |
| : CheckSource(OperativeDirective(manifest_src_.get()), url, |
| redirect_status); |
| } |
| |
| bool DirectiveList::AllowConnectToSource( |
| const GURL& url, ContentSecurityPolicy::RedirectStatus redirect_status, |
| ContentSecurityPolicy::ReportingStatus reporting_status) const { |
| return reporting_status == ContentSecurityPolicy::kSendReport |
| ? CheckSourceAndReportViolation( |
| OperativeDirective(connect_src_.get()), url, |
| ContentSecurityPolicy::kConnectSrc, redirect_status) |
| : CheckSource(OperativeDirective(connect_src_.get()), url, |
| redirect_status); |
| } |
| |
| bool DirectiveList::AllowNavigateToSource( |
| const GURL& url, ContentSecurityPolicy::RedirectStatus redirect_status, |
| ContentSecurityPolicy::ReportingStatus reporting_status) const { |
| // No fallback to default for h5vcc-location-src policy, so we don't use |
| // OperativeDirective() in this case. |
| return reporting_status == ContentSecurityPolicy::kSendReport |
| ? CheckSourceAndReportViolation( |
| location_src_.get(), url, |
| ContentSecurityPolicy::kLocationSrc, redirect_status) |
| : CheckSource(location_src_.get(), url, redirect_status); |
| } |
| |
| bool DirectiveList::AllowFormAction( |
| const GURL& url, ContentSecurityPolicy::RedirectStatus redirect_status, |
| ContentSecurityPolicy::ReportingStatus reporting_status) const { |
| return reporting_status == ContentSecurityPolicy::kSendReport |
| ? CheckSourceAndReportViolation(form_action_.get(), url, |
| ContentSecurityPolicy::kFormAction, |
| redirect_status) |
| : CheckSource(form_action_.get(), url, redirect_status); |
| } |
| |
| bool DirectiveList::AllowBaseURI( |
| const GURL& url, ContentSecurityPolicy::RedirectStatus redirect_status, |
| ContentSecurityPolicy::ReportingStatus reporting_status) const { |
| return reporting_status == ContentSecurityPolicy::kSendReport |
| ? CheckSourceAndReportViolation(base_uri_.get(), url, |
| ContentSecurityPolicy::kBaseURI, |
| redirect_status) |
| : CheckSource(base_uri_.get(), url, redirect_status); |
| } |
| |
| bool DirectiveList::AllowScriptNonce(const std::string& nonce) const { |
| return CheckNonce(OperativeDirective(script_src_.get()), nonce); |
| } |
| |
| bool DirectiveList::AllowStyleNonce(const std::string& nonce) const { |
| return CheckNonce(OperativeDirective(style_src_.get()), nonce); |
| } |
| |
| bool DirectiveList::AllowScriptHash(const HashValue& hash_value) const { |
| return CheckHash(OperativeDirective(script_src_.get()), hash_value); |
| } |
| |
| bool DirectiveList::AllowStyleHash(const HashValue& hash_value) const { |
| return CheckHash(OperativeDirective(style_src_.get()), hash_value); |
| } |
| |
| const std::string& DirectiveList::PluginTypesText() const { |
| DCHECK_EQ(has_plugin_types(), true); |
| return plugin_types_->text(); |
| } |
| |
| // policy = directive-list |
| // directive-list = [ directive *( ";" [ directive ] ) ] |
| // |
| void DirectiveList::Parse(const base::StringPiece& text) { |
| const char* begin = text.begin(); |
| const char* end = text.end(); |
| header_ = ToString(begin, end); |
| |
| if (begin == end) { |
| return; |
| } |
| |
| const char* position = begin; |
| while (position < end) { |
| const char* directive_begin = position; |
| SkipUntil(&position, end, ';'); |
| |
| std::string name, value; |
| if (ParseDirective(directive_begin, position, &name, &value)) { |
| DCHECK(!name.empty()); |
| AddDirective(name, value); |
| } |
| |
| DCHECK(position == end || *position == ';'); |
| SkipExactly(&position, end, ';'); |
| } |
| } |
| |
| // directive = *WSP [ directive-name [ WSP directive-value ] ] |
| // directive-name = 1*( ALPHA / DIGIT / "-" ) |
| // directive-value = *( WSP / <VCHAR except ";"> ) |
| // |
| bool DirectiveList::ParseDirective(const char* begin, const char* end, |
| std::string* name, std::string* value) { |
| DCHECK(name && name->empty()); |
| DCHECK(value && value->empty()); |
| |
| const char* position = begin; |
| SkipWhile<IsAsciiWhitespace>(&position, end); |
| |
| // Empty directive (e.g. ";;;"). Exit early. |
| if (position == end) { |
| return false; |
| } |
| |
| const char* name_begin = position; |
| SkipWhile<IsCSPDirectiveNameCharacter>(&position, end); |
| |
| // The directive-name must be non-empty. |
| if (name_begin == position) { |
| SkipWhile<IsNotAsciiWhitespace>(&position, end); |
| policy_->ReportUnsupportedDirective(ToString(name_begin, position)); |
| return false; |
| } |
| |
| *name = ToString(name_begin, position); |
| |
| if (position == end) { |
| return true; |
| } |
| |
| if (!SkipExactly<IsAsciiWhitespace>(&position, end)) { |
| SkipWhile<IsNotAsciiWhitespace>(&position, end); |
| policy_->ReportUnsupportedDirective(ToString(name_begin, position)); |
| return false; |
| } |
| |
| SkipWhile<IsAsciiWhitespace>(&position, end); |
| |
| const char* value_begin = position; |
| SkipWhile<IsCSPDirectiveValueCharacter>(&position, end); |
| |
| if (position != end) { |
| policy_->ReportInvalidDirectiveValueCharacter(*name, |
| ToString(value_begin, end)); |
| return false; |
| } |
| |
| // The directive-value may be empty. |
| if (value_begin == position) { |
| return true; |
| } |
| |
| *value = ToString(value_begin, position); |
| return true; |
| } |
| |
| void DirectiveList::ParseReportURI(const std::string& name, |
| const std::string& value) { |
| if (header_source_ == kHeaderSourceMeta) { |
| // The report-uri, frame-ancestors, and sandbox directives are not supported |
| // inside a meta element. |
| // https://w3c.github.io/webappsec-csp/#meta-element |
| policy_->ReportDirectiveNotSupportedInsideMeta(name); |
| return; |
| } |
| if (!report_endpoints_.empty()) { |
| policy_->ReportDuplicateDirective(name); |
| return; |
| } |
| |
| base::StringPiece characters(value); |
| const char* position = characters.data(); |
| const char* end = position + characters.size(); |
| |
| while (position < end) { |
| SkipWhile<IsAsciiWhitespace>(&position, end); |
| |
| const char* url_begin = position; |
| SkipWhile<IsNotAsciiWhitespace>(&position, end); |
| |
| if (url_begin < position) { |
| std::string url = ToString(url_begin, position); |
| report_endpoints_.push_back(url); |
| } |
| } |
| } |
| |
| void DirectiveList::SetCSPDirective( |
| const std::string& name, const std::string& value, |
| scoped_ptr<SourceListDirective>* directive) { |
| DCHECK(directive); |
| if (*directive) { |
| policy_->ReportDuplicateDirective(name); |
| return; |
| } |
| directive->reset(new SourceListDirective(name, value, policy_)); |
| } |
| |
| void DirectiveList::SetCSPDirective(const std::string& name, |
| const std::string& value, |
| scoped_ptr<MediaListDirective>* directive) { |
| DCHECK(directive); |
| if (*directive) { |
| policy_->ReportDuplicateDirective(name); |
| return; |
| } |
| directive->reset(new MediaListDirective(name, value, policy_)); |
| } |
| |
| void DirectiveList::EnforceStrictMixedContentChecking( |
| const std::string& name, const std::string& value) { |
| if (report_only_) { |
| policy_->ReportInvalidInReportOnly(name); |
| return; |
| } |
| if (strict_mixed_content_checking_enforced_) { |
| policy_->ReportDuplicateDirective(name); |
| return; |
| } |
| strict_mixed_content_checking_enforced_ = true; |
| policy_->set_enforce_strict_mixed_content_checking(); |
| if (!value.empty()) { |
| policy_->ReportValueForEmptyDirective(name, value); |
| } |
| } |
| |
| void DirectiveList::EnableInsecureRequestsUpgrade(const std::string& name, |
| const std::string& value) { |
| if (report_only_) { |
| policy_->ReportInvalidInReportOnly(name); |
| return; |
| } |
| if (upgrade_insecure_requests_) { |
| policy_->ReportDuplicateDirective(name); |
| return; |
| } |
| upgrade_insecure_requests_ = true; |
| |
| // TODO: Do something with this. |
| NOTIMPLEMENTED() << "SetInsecureRequestsPolicy()"; |
| |
| if (!value.empty()) { |
| policy_->ReportValueForEmptyDirective(name, value); |
| } |
| } |
| |
| void DirectiveList::ParseReflectedXSS(const std::string& name, |
| const std::string& value) { |
| if (reflected_xss_disposition_ != kReflectedXSSUnset) { |
| policy_->ReportDuplicateDirective(name); |
| reflected_xss_disposition_ = kReflectedXSSInvalid; |
| return; |
| } |
| |
| if (value.empty()) { |
| reflected_xss_disposition_ = kReflectedXSSInvalid; |
| policy_->ReportInvalidReflectedXSS(value); |
| return; |
| } |
| |
| base::StringPiece characters(value); |
| const char* position = characters.data(); |
| const char* end = position + characters.size(); |
| |
| SkipWhile<IsAsciiWhitespace>(&position, end); |
| const char* begin = position; |
| SkipWhile<IsNotAsciiWhitespace>(&position, end); |
| |
| // value1 |
| // ^ |
| |
| if (LowerCaseEqualsASCII(begin, position, "allow")) { |
| reflected_xss_disposition_ = kAllowReflectedXSS; |
| } else if (LowerCaseEqualsASCII(begin, position, "filter")) { |
| reflected_xss_disposition_ = kFilterReflectedXSS; |
| } else if (LowerCaseEqualsASCII(begin, position, "block")) { |
| reflected_xss_disposition_ = kBlockReflectedXSS; |
| } else { |
| reflected_xss_disposition_ = kReflectedXSSInvalid; |
| policy_->ReportInvalidReflectedXSS(value); |
| return; |
| } |
| |
| SkipWhile<IsAsciiWhitespace>(&position, end); |
| if (position == end && reflected_xss_disposition_ != kReflectedXSSUnset) { |
| return; |
| } |
| |
| // value1 value2 |
| // ^ |
| reflected_xss_disposition_ = kReflectedXSSInvalid; |
| policy_->ReportInvalidReflectedXSS(value); |
| } |
| |
| void DirectiveList::ParseReferrer(const std::string& name, |
| const std::string& value) { |
| if (did_set_referrer_policy_) { |
| policy_->ReportDuplicateDirective(name); |
| referrer_policy_ = kReferrerPolicyNoReferrer; |
| return; |
| } |
| |
| did_set_referrer_policy_ = true; |
| |
| if (value.empty()) { |
| policy_->ReportInvalidReferrer(value); |
| referrer_policy_ = kReferrerPolicyNoReferrer; |
| return; |
| } |
| |
| base::StringPiece characters(value); |
| |
| const char* position = characters.begin(); |
| const char* end = characters.end(); |
| |
| SkipWhile<IsAsciiWhitespace>(&position, end); |
| const char* begin = position; |
| SkipWhile<IsNotAsciiWhitespace>(&position, end); |
| |
| // value1 |
| // ^ |
| if (LowerCaseEqualsASCII(begin, position, "unsafe-url")) { |
| referrer_policy_ = kReferrerPolicyUnsafeUrl; |
| } else if (LowerCaseEqualsASCII(begin, position, "no-referrer")) { |
| referrer_policy_ = kReferrerPolicyNoReferrer; |
| } else if (LowerCaseEqualsASCII(begin, position, |
| "no-referrer-when-downgrade")) { |
| referrer_policy_ = kReferrerPolicyDefault; |
| } else if (LowerCaseEqualsASCII(begin, position, "origin")) { |
| referrer_policy_ = kReferrerPolicyOrigin; |
| } else if (LowerCaseEqualsASCII(begin, position, |
| "origin-when-cross-origin") || |
| LowerCaseEqualsASCII(begin, position, "origin-when-crossorigin")) { |
| referrer_policy_ = kReferrerPolicyOriginWhenCrossOrigin; |
| } else { |
| policy_->ReportInvalidReferrer(value); |
| return; |
| } |
| |
| SkipWhile<IsAsciiWhitespace>(&position, end); |
| if (position == end) { |
| return; |
| } |
| |
| // value1 value2 |
| // ^ |
| referrer_policy_ = kReferrerPolicyNoReferrer; |
| policy_->ReportInvalidReferrer(value); |
| } |
| |
| std::string DirectiveList::ParseSuboriginName(const std::string& policy) { |
| base::StringPiece characters(policy); |
| |
| const char* position = characters.begin(); |
| const char* end = characters.end(); |
| |
| // Parse the name of the suborigin (no spaces, single string) |
| SkipWhile<IsAsciiWhitespace>(&position, end); |
| if (position == end) { |
| policy_->ReportInvalidSuboriginFlags("No suborigin name specified."); |
| return std::string(); |
| } |
| |
| const char* begin = position; |
| |
| SkipWhile<IsAsciiAlphanumeric>(&position, end); |
| if (position != end && !IsAsciiWhitespace(*position)) { |
| policy_->ReportInvalidSuboriginFlags( |
| "Invalid character \'" + std::string(position, 1) + "\' in suborigin."); |
| return std::string(); |
| } |
| size_t length = static_cast<size_t>(position - begin); |
| SkipWhile<IsAsciiWhitespace>(&position, end); |
| if (position != end) { |
| policy_->ReportInvalidSuboriginFlags( |
| "Whitespace is not allowed in suborigin names."); |
| return std::string(); |
| } |
| |
| return std::string(begin, length); |
| } |
| |
| void DirectiveList::AddDirective(const std::string& name, |
| const std::string& value) { |
| DCHECK(!name.empty()); |
| std::string lower_name = StringToLowerASCII(name); |
| if (lower_name == ContentSecurityPolicy::kDefaultSrc) { |
| SetCSPDirective(name, value, &default_src_); |
| } else if (lower_name == ContentSecurityPolicy::kScriptSrc) { |
| SetCSPDirective(name, value, &script_src_); |
| policy_->set_uses_script_hash_algorithms( |
| script_src_->hash_algorithms_used()); |
| } else if (lower_name == ContentSecurityPolicy::kObjectSrc) { |
| SetCSPDirective(name, value, &object_src_); |
| } else if (lower_name == ContentSecurityPolicy::kFrameAncestors) { |
| SetCSPDirective(name, value, &frame_ancestors_); |
| } else if (lower_name == ContentSecurityPolicy::kFrameSrc) { |
| SetCSPDirective(name, value, &frame_src_); |
| } else if (lower_name == ContentSecurityPolicy::kImgSrc) { |
| SetCSPDirective(name, value, &img_src_); |
| } else if (lower_name == ContentSecurityPolicy::kStyleSrc) { |
| SetCSPDirective(name, value, &style_src_); |
| policy_->set_uses_style_hash_algorithms(style_src_->hash_algorithms_used()); |
| } else if (lower_name == ContentSecurityPolicy::kFontSrc) { |
| SetCSPDirective(name, value, &font_src_); |
| } else if (lower_name == ContentSecurityPolicy::kLocationSrc) { |
| SetCSPDirective(name, value, &location_src_); |
| } else if (lower_name == ContentSecurityPolicy::kMediaSrc) { |
| SetCSPDirective(name, value, &media_src_); |
| } else if (lower_name == ContentSecurityPolicy::kConnectSrc) { |
| SetCSPDirective(name, value, &connect_src_); |
| } else if (lower_name == ContentSecurityPolicy::kSandbox) { |
| // TODO: ApplySandboxPolicy(). |
| // ApplySandboxPolicy(name, value); |
| NOTIMPLEMENTED() << ContentSecurityPolicy::kSandbox; |
| } else if (lower_name == ContentSecurityPolicy::kReportURI) { |
| ParseReportURI(name, value); |
| } else if (lower_name == ContentSecurityPolicy::kBaseURI) { |
| SetCSPDirective(name, value, &base_uri_); |
| } else if (lower_name == ContentSecurityPolicy::kChildSrc) { |
| SetCSPDirective(name, value, &child_src_); |
| } else if (lower_name == ContentSecurityPolicy::kFormAction) { |
| SetCSPDirective(name, value, &form_action_); |
| } else if (lower_name == ContentSecurityPolicy::kPluginTypes) { |
| SetCSPDirective(name, value, &plugin_types_); |
| } else if (lower_name == ContentSecurityPolicy::kReflectedXSS) { |
| ParseReflectedXSS(name, value); |
| } else if (lower_name == ContentSecurityPolicy::kReferrer) { |
| ParseReferrer(name, value); |
| } else if (lower_name == ContentSecurityPolicy::kUpgradeInsecureRequests) { |
| EnableInsecureRequestsUpgrade(name, value); |
| } else if (lower_name == ContentSecurityPolicy::kBlockAllMixedContent) { |
| EnforceStrictMixedContentChecking(name, value); |
| } else if (lower_name == ContentSecurityPolicy::kManifestSrc) { |
| SetCSPDirective(name, value, &manifest_src_); |
| } else if (lower_name == ContentSecurityPolicy::kSuborigin) { |
| // TODO: ApplySuboriginPolicy. |
| // ApplySuboriginPolicy(name, value); |
| NOTIMPLEMENTED() << ContentSecurityPolicy::kSuborigin; |
| } else { |
| policy_->ReportUnsupportedDirective(name); |
| } |
| } |
| |
| } // namespace csp |
| } // namespace cobalt |