Source/WebCore/ChangeLog

 12018-05-06 Daniel Bates <dabates@apple.com>
 2
 3 CSP status-code incorrect for document blocked due to violation of its frame-ancestors directive
 4 https://bugs.webkit.org/show_bug.cgi?id=185366
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Fixes an issue where the status-code in the sent CSP report for an HTTP document blocked because
 9 its frame-ancestors directive was violated would be the status code of the previously loaded
 10 document in the frame. If the previously loaded document was about:blank then this would be 0.
 11
 12 Currently whenever we send a CSP report we ask the document's loader (Document::loader()) for the
 13 HTTP status code for the last response. Document::loader() returns the loader for the last committed
 14 document its frame. For a frame-ancestors violation, a CSP report is sent before the document
 15 that had the frame-ancestors directive has been committed and after it has been associate with a frame.
 16 As a result we are in are in a transient transition state for the frame and hence the last response
 17 for new document's loader (Document::loader()) is actually the last response of the previously loaded
 18 document in the frame. Instead we need to take care to tell CSP about the HTTP status code for the
 19 response associated with the document the CSP came from.
 20
 21 * dom/Document.cpp:
 22 (WebCore::Document::processHttpEquiv):
 23 (WebCore::Document::initSecurityContext):
 24 Pass the HTTP status code to CSP.
 25
 26 * page/csp/ContentSecurityPolicy.cpp:
 27 (WebCore::ContentSecurityPolicy::copyStateFrom):
 28 (WebCore::ContentSecurityPolicy::responseHeaders const):
 29 (WebCore::ContentSecurityPolicy::didReceiveHeaders):
 30 (WebCore::ContentSecurityPolicy::didReceiveHeader):
 31 (WebCore::ContentSecurityPolicy::reportViolation const):
 32 * page/csp/ContentSecurityPolicy.h:
 33 Modify existing functions to take the HTTP status code, store it in a instance variable,
 34 and reference this variable when reporting a violation.
 35
 36 * page/csp/ContentSecurityPolicyResponseHeaders.cpp:
 37 (WebCore::ContentSecurityPolicyResponseHeaders::ContentSecurityPolicyResponseHeaders):
 38 (WebCore::ContentSecurityPolicyResponseHeaders::isolatedCopy const):
 39 * page/csp/ContentSecurityPolicyResponseHeaders.h:
 40 (WebCore::ContentSecurityPolicyResponseHeaders::encode const):
 41 (WebCore::ContentSecurityPolicyResponseHeaders::decode):
 42 Store the HTTP status code along with the response headers.
 43
1442018-05-06 Daniel Bates <dabates@apple.com>
245
346 CSP should only notify Inspector to pause the debugger on the first policy to violate a directive

Source/WebCore/dom/Document.cpp

@@void Document::processHttpEquiv(const String& equiv, const String& content, bool
33173317 }
33183318
33193319 Frame* frame = this->frame();
 3320 auto* documentLoader = frame ? frame->loader().documentLoader() : nullptr;
33203321
33213322 HTTPHeaderName headerName;
33223323 if (!findHTTPHeaderName(equiv, headerName))

@@void Document::processHttpEquiv(const String& equiv, const String& content, bool
33843385
33853386 case HTTPHeaderName::ContentSecurityPolicy:
33863387 if (isInDocumentHead)
3387  contentSecurityPolicy()->didReceiveHeader(content, ContentSecurityPolicyHeaderType::Enforce, ContentSecurityPolicy::PolicyFrom::HTTPEquivMeta);
 3388 contentSecurityPolicy()->didReceiveHeader(content, ContentSecurityPolicyHeaderType::Enforce, ContentSecurityPolicy::PolicyFrom::HTTPEquivMeta, documentLoader ? documentLoader->response().httpStatusCode() : 0);
33883389 break;
33893390
33903391 case HTTPHeaderName::XWebKitCSP:
33913392 if (isInDocumentHead)
3392  contentSecurityPolicy()->didReceiveHeader(content, ContentSecurityPolicyHeaderType::PrefixedEnforce, ContentSecurityPolicy::PolicyFrom::HTTPEquivMeta);
 3393 contentSecurityPolicy()->didReceiveHeader(content, ContentSecurityPolicyHeaderType::PrefixedEnforce, ContentSecurityPolicy::PolicyFrom::HTTPEquivMeta, documentLoader ? documentLoader->response().httpStatusCode() : 0);
33933394 break;
33943395
33953396 default:

@@void Document::initSecurityContext()
55115512 if (shouldEnforceContentDispositionAttachmentSandbox())
55125513 applyContentDispositionAttachmentSandbox();
55135514
 5515 auto* loader = m_frame->loader().documentLoader();
55145516 bool isSecurityOriginUnique = isSandboxed(SandboxOrigin);
5515  if (!isSecurityOriginUnique) {
5516  auto* loader = m_frame->loader().documentLoader();
 5517 if (!isSecurityOriginUnique)
55175518 isSecurityOriginUnique = loader && loader->response().tainting() == ResourceResponse::Tainting::Opaque;
5518  }
55195519
55205520 setSecurityOriginPolicy(SecurityOriginPolicy::create(isSecurityOriginUnique ? SecurityOrigin::createUnique() : SecurityOrigin::create(m_url)));
55215521 setContentSecurityPolicy(std::make_unique<ContentSecurityPolicy>(*this));
55225522
55235523 String overrideContentSecurityPolicy = m_frame->loader().client().overrideContentSecurityPolicy();
55245524 if (!overrideContentSecurityPolicy.isNull())
5525  contentSecurityPolicy()->didReceiveHeader(overrideContentSecurityPolicy, ContentSecurityPolicyHeaderType::Enforce, ContentSecurityPolicy::PolicyFrom::API);
 5525 contentSecurityPolicy()->didReceiveHeader(overrideContentSecurityPolicy, ContentSecurityPolicyHeaderType::Enforce, ContentSecurityPolicy::PolicyFrom::API, loader ? loader->response().httpStatusCode() : 0);
55265526
55275527#if USE(QUICK_LOOK)
55285528 if (shouldEnforceQuickLookSandbox())

Source/WebCore/page/csp/ContentSecurityPolicy.cpp

@@void ContentSecurityPolicy::copyStateFrom(const ContentSecurityPolicy* other)
112112 ASSERT(m_policies.isEmpty());
113113 for (auto& policy : other->m_policies)
114114 didReceiveHeader(policy->header(), policy->headerType(), ContentSecurityPolicy::PolicyFrom::Inherited);
 115 m_httpStatusCode = other->m_httpStatusCode;
115116}
116117
117118void ContentSecurityPolicy::copyUpgradeInsecureRequestStateFrom(const ContentSecurityPolicy& other)

@@ContentSecurityPolicyResponseHeaders ContentSecurityPolicy::responseHeaders() co
166167 result.m_headers.reserveInitialCapacity(m_policies.size());
167168 for (auto& policy : m_policies)
168169 result.m_headers.uncheckedAppend({ policy->header(), policy->headerType() });
 170 result.m_httpStatusCode = m_httpStatusCode;
169171 m_cachedResponseHeaders = WTFMove(result);
170172 }
171173 return *m_cachedResponseHeaders;

@@void ContentSecurityPolicy::didReceiveHeaders(const ContentSecurityPolicyRespons
176178 SetForScope<bool> isReportingEnabled(m_isReportingEnabled, reportParsingErrors == ReportParsingErrors::Yes);
177179 for (auto& header : headers.m_headers)
178180 didReceiveHeader(header.first, header.second, ContentSecurityPolicy::PolicyFrom::HTTPHeader);
 181 m_httpStatusCode = headers.m_httpStatusCode;
179182}
180183
181 void ContentSecurityPolicy::didReceiveHeader(const String& header, ContentSecurityPolicyHeaderType type, ContentSecurityPolicy::PolicyFrom policyFrom)
 184void ContentSecurityPolicy::didReceiveHeader(const String& header, ContentSecurityPolicyHeaderType type, ContentSecurityPolicy::PolicyFrom policyFrom, int httpStatusCode)
182185{
183186 if (m_hasAPIPolicy)
184187 return;
185188
 189 m_httpStatusCode = httpStatusCode;
 190
186191 if (policyFrom == PolicyFrom::API) {
187192 ASSERT(m_policies.isEmpty());
188193 m_hasAPIPolicy = true;

@@void ContentSecurityPolicy::reportViolation(const String& effectiveViolatedDirec
665670 String violatedDirectiveText = violatedDirective;
666671 String originalPolicy = violatedDirectiveList.header();
667672 String referrer = document.referrer();
668  ASSERT(document.loader());
669673 // FIXME: Is it policy to not use the status code for HTTPS, or is that a bug?
670  unsigned short statusCode = document.url().protocolIs("http") && document.loader() ? document.loader()->response().httpStatusCode() : 0;
 674 unsigned short statusCode = m_selfSourceProtocol == "http" ? m_httpStatusCode : 0;
671675
672676 String sourceFile;
673677 int lineNumber = 0;

Source/WebCore/page/csp/ContentSecurityPolicy.h

@@public:
8282 WEBCORE_EXPORT ContentSecurityPolicyResponseHeaders responseHeaders() const;
8383 enum ReportParsingErrors { No, Yes };
8484 WEBCORE_EXPORT void didReceiveHeaders(const ContentSecurityPolicyResponseHeaders&, ReportParsingErrors = ReportParsingErrors::Yes);
85  void didReceiveHeader(const String&, ContentSecurityPolicyHeaderType, ContentSecurityPolicy::PolicyFrom);
 85 void didReceiveHeader(const String&, ContentSecurityPolicyHeaderType, ContentSecurityPolicy::PolicyFrom, int httpStatusCode = 0);
8686
8787 bool allowScriptWithNonce(const String& nonce, bool overrideContentSecurityPolicy = false) const;
8888 bool allowStyleWithNonce(const String& nonce, bool overrideContentSecurityPolicy = false) const;

@@private:
216216 bool m_isReportingEnabled { true };
217217 bool m_upgradeInsecureRequests { false };
218218 bool m_hasAPIPolicy { false };
 219 int m_httpStatusCode { 0 };
219220 OptionSet<ContentSecurityPolicyHashAlgorithm> m_hashAlgorithmsForInlineScripts;
220221 OptionSet<ContentSecurityPolicyHashAlgorithm> m_hashAlgorithmsForInlineStylesheets;
221222 HashSet<SecurityOriginData> m_insecureNavigationRequestsToUpgrade;

Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.cpp

@@ContentSecurityPolicyResponseHeaders::ContentSecurityPolicyResponseHeaders(const
4848 policyValue = response.httpHeaderField(HTTPHeaderName::XWebKitCSPReportOnly);
4949 if (!policyValue.isEmpty())
5050 m_headers.append({ policyValue, ContentSecurityPolicyHeaderType::PrefixedReport });
 51
 52 m_httpStatusCode = response.httpStatusCode();
5153}
5254
5355ContentSecurityPolicyResponseHeaders ContentSecurityPolicyResponseHeaders::isolatedCopy() const

@@ContentSecurityPolicyResponseHeaders ContentSecurityPolicyResponseHeaders::isola
5658 isolatedCopy.m_headers.reserveInitialCapacity(m_headers.size());
5759 for (auto& header : m_headers)
5860 isolatedCopy.m_headers.uncheckedAppend({ header.first.isolatedCopy(), header.second });
 61 isolatedCopy.m_httpStatusCode = m_httpStatusCode;
5962 return isolatedCopy;
6063}
6164

Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.h

@@private:
5454 friend class ContentSecurityPolicy;
5555
5656 Vector<std::pair<String, ContentSecurityPolicyHeaderType>> m_headers;
 57 int m_httpStatusCode { 0 };
5758};
5859
5960template <class Encoder>

@@void ContentSecurityPolicyResponseHeaders::encode(Encoder& encoder) const
6465 encoder << pair.first;
6566 encoder.encodeEnum(pair.second);
6667 }
 68 encoder << m_httpStatusCode;
6769}
6870
6971template <class Decoder>

@@bool ContentSecurityPolicyResponseHeaders::decode(Decoder& decoder, ContentSecur
8385 headers.m_headers.append(std::make_pair(header, headerType));
8486 }
8587
 88 if (!decoder.decode(headers.m_httpStatusCode))
 89 return false;
 90
8691 return true;
8792}
8893

LayoutTests/ChangeLog

 12018-05-06 Daniel Bates <dabates@apple.com>
 2
 3 CSP status-code incorrect for document blocked due to violation of its frame-ancestors directive
 4 https://bugs.webkit.org/show_bug.cgi?id=185366
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Update existing test results now that we send the HTTP status code for the correct document.
 9
 10 * http/tests/security/contentSecurityPolicy/1.1/frame-ancestors/report-frame-ancestors-cross-origin-expected.txt:
 11 * http/tests/security/contentSecurityPolicy/1.1/frame-ancestors/report-frame-ancestors-same-origin-expected.txt:
 12
1132018-05-02 Daniel Bates <dabates@apple.com>
214
315 Add tests to ensure Same-Site cookies are included when performing a top-level redirect

LayoutTests/http/tests/security/contentSecurityPolicy/1.1/frame-ancestors/report-frame-ancestors-cross-origin-expected.txt

@@HTTP_HOST: localhost:8000
55REQUEST_METHOD: POST
66REQUEST_URI: /security/contentSecurityPolicy/resources/save-report.php?test=/security/contentSecurityPolicy/1.1/report-frame-ancestors-cross-origin.html
77=== POST DATA ===
8 {"csp-report":{"document-uri":"http://localhost:8000/security/contentSecurityPolicy/resources/echo-intertag.pl?header=Content-Security-Policy%3A+frame-ancestors+%27none%27%3B+report-uri+save-report.php%3Ftest%3D/security/contentSecurityPolicy/1.1/report-frame-ancestors-cross-origin.html&q=FAIL","referrer":"","violated-directive":"frame-ancestors 'none'","effective-directive":"frame-ancestors","original-policy":"frame-ancestors 'none'; report-uri save-report.php?test=/security/contentSecurityPolicy/1.1/report-frame-ancestors-cross-origin.html","blocked-uri":"http://localhost:8000/security/contentSecurityPolicy/resources/echo-intertag.pl?header=Content-Security-Policy%3A+frame-ancestors+%27none%27%3B+report-uri+save-report.php%3Ftest%3D/security/contentSecurityPolicy/1.1/report-frame-ancestors-cross-origin.html&q=FAIL","status-code":0}}
 8{"csp-report":{"document-uri":"http://localhost:8000/security/contentSecurityPolicy/resources/echo-intertag.pl?header=Content-Security-Policy%3A+frame-ancestors+%27none%27%3B+report-uri+save-report.php%3Ftest%3D/security/contentSecurityPolicy/1.1/report-frame-ancestors-cross-origin.html&q=FAIL","referrer":"","violated-directive":"frame-ancestors 'none'","effective-directive":"frame-ancestors","original-policy":"frame-ancestors 'none'; report-uri save-report.php?test=/security/contentSecurityPolicy/1.1/report-frame-ancestors-cross-origin.html","blocked-uri":"http://localhost:8000/security/contentSecurityPolicy/resources/echo-intertag.pl?header=Content-Security-Policy%3A+frame-ancestors+%27none%27%3B+report-uri+save-report.php%3Ftest%3D/security/contentSecurityPolicy/1.1/report-frame-ancestors-cross-origin.html&q=FAIL","status-code":200}}

LayoutTests/http/tests/security/contentSecurityPolicy/1.1/frame-ancestors/report-frame-ancestors-same-origin-expected.txt

@@HTTP_HOST: 127.0.0.1:8000
55REQUEST_METHOD: POST
66REQUEST_URI: /security/contentSecurityPolicy/resources/save-report.php?test=/security/contentSecurityPolicy/1.1/report-frame-ancestors-same-origin.html
77=== POST DATA ===
8 {"csp-report":{"document-uri":"http://127.0.0.1:8000/security/contentSecurityPolicy/resources/echo-intertag.pl?header=Content-Security-Policy%3A+frame-ancestors+%27none%27%3B+report-uri+save-report.php%3Ftest%3D/security/contentSecurityPolicy/1.1/report-frame-ancestors-same-origin.html&q=FAIL","referrer":"","violated-directive":"frame-ancestors 'none'","effective-directive":"frame-ancestors","original-policy":"frame-ancestors 'none'; report-uri save-report.php?test=/security/contentSecurityPolicy/1.1/report-frame-ancestors-same-origin.html","blocked-uri":"http://127.0.0.1:8000/security/contentSecurityPolicy/resources/echo-intertag.pl?header=Content-Security-Policy%3A+frame-ancestors+%27none%27%3B+report-uri+save-report.php%3Ftest%3D/security/contentSecurityPolicy/1.1/report-frame-ancestors-same-origin.html&q=FAIL","status-code":0}}
 8{"csp-report":{"document-uri":"http://127.0.0.1:8000/security/contentSecurityPolicy/resources/echo-intertag.pl?header=Content-Security-Policy%3A+frame-ancestors+%27none%27%3B+report-uri+save-report.php%3Ftest%3D/security/contentSecurityPolicy/1.1/report-frame-ancestors-same-origin.html&q=FAIL","referrer":"","violated-directive":"frame-ancestors 'none'","effective-directive":"frame-ancestors","original-policy":"frame-ancestors 'none'; report-uri save-report.php?test=/security/contentSecurityPolicy/1.1/report-frame-ancestors-same-origin.html","blocked-uri":"http://127.0.0.1:8000/security/contentSecurityPolicy/resources/echo-intertag.pl?header=Content-Security-Policy%3A+frame-ancestors+%27none%27%3B+report-uri+save-report.php%3Ftest%3D/security/contentSecurityPolicy/1.1/report-frame-ancestors-same-origin.html&q=FAIL","status-code":200}}