Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(925)

Unified Diff: content/common/content_security_policy/csp_policy.cc

Issue 2612793002: Implement ContentSecurityPolicy on the browser-side. (Closed)
Patch Set: Temporary re-add the parser + transmit parsed CSP over IPC. Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/common/content_security_policy/csp_policy.cc
diff --git a/content/common/content_security_policy/csp_policy.cc b/content/common/content_security_policy/csp_policy.cc
new file mode 100644
index 0000000000000000000000000000000000000000..457962318c6354b011eae0d2b046d45b031609b2
--- /dev/null
+++ b/content/common/content_security_policy/csp_policy.cc
@@ -0,0 +1,240 @@
+// Copyright 2017 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 <sstream>
+
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "content/common/content_security_policy/csp_context.h"
+
+namespace content {
+
+namespace {
+
+static CSPDirective::Name CSPFallback(CSPDirective::Name directive) {
+ if (directive == CSPDirective::FrameSrc)
+ return CSPDirective::ChildSrc;
+ if (directive == CSPDirective::ChildSrc)
+ return CSPDirective::DefaultSrc;
+ return CSPDirective::Unknown;
+}
+
+void ReportUnsupportedDirective(CSPContext* context,
+ const base::StringPiece& name) {
+ std::string message = "Unrecognized Content-Security-Policy directive '" +
+ name.as_string() + "'.\n";
+ context->LogToConsole(message);
+}
+
+void ReportDuplicateDirective(CSPContext* context,
+ const base::StringPiece& directive) {
+ std::string message =
+ "Ignoring duplicate Content-Security-Policy directive '" +
+ directive.as_string() + "'.\n";
+ context->LogToConsole(message);
+}
+
+void ReportInvalidDirectiveInMeta(CSPContext* context,
+ const base::StringPiece& directive) {
+ std::string message =
+ "Content Security Policies delivered via a <meta> element may not "
+ "contain the " +
+ directive.as_string() + " directive.";
+ context->LogToConsole(message);
+}
+
+void AddReportURI(CSPPolicy* policy,
+ CSPContext* context,
+ const base::StringPiece& directive_value) {
+ if (!policy->report_end_points.empty())
+ ReportDuplicateDirective(context, "report-uri");
+ // Remove report-uri in meta policies, per
+ // https://www.w3.org/TR/CSP2/#delivery-html-meta-element.
+ if (policy->source == blink::WebContentSecurityPolicySourceMeta) {
+ ReportInvalidDirectiveInMeta(context, "report-uri");
+ return;
+ }
+
+ for (const base::StringPiece& end_point :
+ base::SplitStringPiece(directive_value, " ", base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY)) {
+ policy->report_end_points.push_back(end_point.as_string());
+ }
+}
+
+void AddDirective(CSPPolicy* policy,
+ CSPContext* context,
+ const base::StringPiece& directive_name,
+ const base::StringPiece& directive_value) {
+ CSPDirective::Name name =
+ CSPDirective::StringToName(directive_name.as_string());
+
+ // Unknown directive
+ if (name == CSPDirective::Unknown) {
+ ReportUnsupportedDirective(context, directive_name);
+ }
+
+ // Report-uri
+ if (name == CSPDirective::ReportURI) {
+ AddReportURI(policy, context, directive_value);
+ return;
+ }
+
+ // Remove frame-ancestors directives in meta policies, per
+ // https://www.w3.org/TR/CSP2/#delivery-html-meta-element.
+ if (name == CSPDirective::FrameAncestors &&
+ policy->source == blink::WebContentSecurityPolicySourceMeta) {
+ ReportInvalidDirectiveInMeta(context, directive_name);
+ return;
+ }
+
+ for (const auto& directive : policy->directives) {
+ if (directive.name == name)
+ ReportDuplicateDirective(context, directive_name);
+ }
+
+ policy->directives.emplace_back(
+ name, CSPSourceList::Parse(context, directive_name, directive_value));
+}
+
+} // namespace
+
+CSPPolicy::CSPPolicy()
+ : disposition(blink::WebContentSecurityPolicyTypeEnforce),
+ source(blink::WebContentSecurityPolicySourceHTTP),
+ directives(),
+ report_end_points() {}
+
+CSPPolicy::CSPPolicy(blink::WebContentSecurityPolicyType disposition,
+ blink::WebContentSecurityPolicySource source,
+ const std::vector<CSPDirective>& directives,
+ const std::vector<std::string>& report_end_points)
+ : disposition(disposition),
+ source(source),
+ directives(directives),
+ report_end_points(report_end_points) {}
+
+CSPPolicy::CSPPolicy(const CSPPolicy&) = default;
+CSPPolicy::~CSPPolicy() = default;
+
+// static
+CSPPolicy CSPPolicy::Parse(CSPContext* context,
+ const ContentSecurityPolicyHeader& header) {
+ CSPPolicy policy;
+ policy.disposition = header.type;
+ policy.source = header.source;
+
+ // Split by ";"
+ for (const base::StringPiece& serialized_directive :
+ base::SplitStringPiece(header.header_value, ";", base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY)) {
+ base::StringPiece directive_name;
+ base::StringPiece directive_value;
+
+ // Split serialized-directive into { directive-name , directive-value }.
+ size_t first_space_pos =
+ serialized_directive.find_first_of(base::kWhitespaceASCII, 0);
+ directive_name = serialized_directive.substr(0, first_space_pos);
+ if (first_space_pos != std::string::npos) {
+ size_t first_non_space_pos = serialized_directive.find_first_not_of(
+ base::kWhitespaceASCII, first_space_pos);
+ directive_value =
+ serialized_directive.substr(first_non_space_pos, std::string::npos);
+ }
+
+ AddDirective(&policy, context, directive_name, directive_value);
+ }
+ return policy;
+}
+
+bool CSPPolicy::Allow(CSPContext* context,
+ CSPDirective::Name directive_name,
+ const GURL& url,
+ bool is_redirect) const {
+ CSPDirective::Name current_directive_name = directive_name;
+ do {
+ // Search for the matching directive.
+ for (const CSPDirective& directive : directives) {
+ if (directive.name == current_directive_name) {
+ return AllowDirective(context, directive_name, directive, url,
+ is_redirect);
+ }
+ }
+
+ current_directive_name = CSPFallback(current_directive_name);
+ } while (current_directive_name != CSPDirective::Unknown);
+ return false;
+}
+
+std::string CSPPolicy::ToString() const {
+ std::stringstream text;
+ bool is_first_policy = true;
+ for (const CSPDirective& directive : directives) {
+ if (!is_first_policy)
+ text << "; ";
+ text << directive.ToString();
+ is_first_policy = false;
+ }
+ return text.str();
+}
+
+bool CSPPolicy::AllowDirective(CSPContext* context,
+ CSPDirective::Name directive_name,
+ const CSPDirective& directive,
+ const GURL& url,
+ bool is_redirect) const {
+ if (directive.source_list.Allow(context, url, is_redirect))
+ return true;
+
+ ReportViolation(context, directive_name, directive, url);
+
+ return disposition == blink::WebContentSecurityPolicyTypeReport;
+}
+
+void CSPPolicy::ReportViolation(CSPContext* context,
+ const CSPDirective::Name directive_name,
+ const CSPDirective& directive,
+ const GURL& url) const {
+ // We should never have a violation against `child-src` or `default-src`
+ // directly; the effective directive should always be one of the explicit
+ // fetch directives.
+ DCHECK_NE(directive_name, CSPDirective::DefaultSrc);
+ DCHECK_NE(directive_name, CSPDirective::ChildSrc);
+
+ std::stringstream message;
+
+ if (disposition == blink::WebContentSecurityPolicyTypeReport)
+ message << "[Report Only] ";
+
+ if (directive_name == CSPDirective::FormAction)
+ message << "Refused to send form data to '";
+ else if (directive_name == CSPDirective::FrameSrc)
+ message << "Refused to frame '";
+ else if (directive_name == CSPDirective::FrameAncestors)
+ message << "Refused to display '";
+
+ // TODO(arthursonzogni): Elide url.
+ message << url
+ << "' because it violates the following Content Security Policy "
+ "directive: \""
+ // TODO(arthursonzogni): Pass the full CSPDirective.
+ << directive.ToString() << "\"";
+
+ if (directive.name != directive_name)
+ message << " Note that '" << CSPDirective::NameToString(directive_name)
+ << "' was not explicitly set, so '"
+ << CSPDirective::NameToString(directive.name)
+ << "' is used as a fallback.";
+
+ message << "\n";
+
+ context->LogToConsole(message.str());
+ context->ReportViolation(CSPDirective::NameToString(directive.name),
+ CSPDirective::NameToString(directive_name),
+ message.str(), url, report_end_points,
+ "", // TODO(arthursonzogni): Pass header.
+ disposition);
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698