From b632f9d274f316c51291f608c024595ea3f3fad6 Mon Sep 17 00:00:00 2001 From: Ryan Reno Date: Mon, 17 Oct 2022 14:35:39 -0700 Subject: [PATCH] [CSP] Implement prefetch-src directive https://bugs.webkit.org/show_bug.cgi?id=185070 rdar://problem/39821187 Reviewed by Brent Fulgham. Implement the prefetch-src CSP directive. is behind a runtime flag. If a user chooses to enable LinkPrefetch then the prefetch-src directive will apply to any resources that may be prefetched. In the default case, we can parse the directive but will not take any action since we won't perform prefetches. * LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src: These tests mirror the same behavior being tested by the WPT suite but since we don't support onload/onerror events for prefetched link resources we need to use our own test infrastructure to cover this behavior. * LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-allowed-expected.txt: Added. * LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-allowed.html: Added. * LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-by-default-expected.txt: Added. * LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-by-default.html: Added. * LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-expected.txt: Added. * LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked.html: Added. * LayoutTests/platform/win/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-allowed-expected.txt: Added. * LayoutTests/platform/win/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-by-default-expected.txt: Added. * LayoutTests/platform/win/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-expected.txt: Added. * Source/WebCore/loader/LinkLoader.cpp: (WebCore::LinkLoader::prefetchIfNeeded): * Source/WebCore/loader/cache/CachedResourceLoader.cpp: (WebCore::CachedResourceLoader::allowedByContentSecurityPolicy const): * Source/WebCore/page/csp/ContentSecurityPolicy.cpp: (WebCore::ContentSecurityPolicy::allowPrefetchFromSource const): * Source/WebCore/page/csp/ContentSecurityPolicy.h: * Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp: (WebCore::ContentSecurityPolicyDirectiveList::violatedDirectiveForPrefetch const): (WebCore::ContentSecurityPolicyDirectiveList::addDirective): * Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h: * Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.cpp: * Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.h: Canonical link: https://commits.webkit.org/255653@main --- .../prefetch-allowed-expected.txt | 1 + .../prefetch-src/prefetch-allowed.html | 32 +++++++++++++++++ .../prefetch-blocked-by-default-expected.txt | 2 ++ .../prefetch-blocked-by-default.html | 35 +++++++++++++++++++ .../prefetch-blocked-expected.txt | 2 ++ .../prefetch-src/prefetch-blocked.html | 32 +++++++++++++++++ .../prefetch-allowed-expected.txt | 2 ++ .../prefetch-blocked-by-default-expected.txt | 2 ++ .../prefetch-blocked-expected.txt | 2 ++ Source/WebCore/loader/LinkLoader.cpp | 2 +- .../loader/cache/CachedResourceLoader.cpp | 4 +++ .../page/csp/ContentSecurityPolicy.cpp | 5 +++ .../WebCore/page/csp/ContentSecurityPolicy.h | 1 + .../ContentSecurityPolicyDirectiveList.cpp | 10 ++++++ .../csp/ContentSecurityPolicyDirectiveList.h | 2 ++ .../ContentSecurityPolicyDirectiveNames.cpp | 1 + .../csp/ContentSecurityPolicyDirectiveNames.h | 1 + 17 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-allowed-expected.txt create mode 100644 LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-allowed.html create mode 100644 LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-by-default-expected.txt create mode 100644 LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-by-default.html create mode 100644 LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-expected.txt create mode 100644 LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked.html create mode 100644 LayoutTests/platform/win/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-allowed-expected.txt create mode 100644 LayoutTests/platform/win/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-by-default-expected.txt create mode 100644 LayoutTests/platform/win/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-expected.txt diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-allowed-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-allowed-expected.txt new file mode 100644 index 000000000000..7ef22e9a431a --- /dev/null +++ b/LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-allowed-expected.txt @@ -0,0 +1 @@ +PASS diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-allowed.html b/LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-allowed.html new file mode 100644 index 000000000000..0f0d5d32549c --- /dev/null +++ b/LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-allowed.html @@ -0,0 +1,32 @@ + + + + + + + +
+ + diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-by-default-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-by-default-expected.txt new file mode 100644 index 000000000000..4e9cbbb14f5e --- /dev/null +++ b/LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-by-default-expected.txt @@ -0,0 +1,2 @@ +CONSOLE MESSAGE: Refused to load http://127.0.0.1:8000/cache/resources/prefetched-main-resource.py because it appears in neither the prefetch-src directive nor the default-src directive of the Content Security Policy. +PASS diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-by-default.html b/LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-by-default.html new file mode 100644 index 000000000000..de090edca746 --- /dev/null +++ b/LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-by-default.html @@ -0,0 +1,35 @@ + + + + + + + +
+ + diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-expected.txt new file mode 100644 index 000000000000..065d10e5233f --- /dev/null +++ b/LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-expected.txt @@ -0,0 +1,2 @@ +CONSOLE MESSAGE: Refused to load http://localhost:8000/cache/resources/prefetched-main-resource.py because it does not appear in the prefetch-src directive of the Content Security Policy. +PASS diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked.html b/LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked.html new file mode 100644 index 000000000000..4a694556636a --- /dev/null +++ b/LayoutTests/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked.html @@ -0,0 +1,32 @@ + + + + + + + +
+ + diff --git a/LayoutTests/platform/win/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-allowed-expected.txt b/LayoutTests/platform/win/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-allowed-expected.txt new file mode 100644 index 000000000000..6ee3f713ff29 --- /dev/null +++ b/LayoutTests/platform/win/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-allowed-expected.txt @@ -0,0 +1,2 @@ +FAIL: Timed out waiting for notifyDone to be called + diff --git a/LayoutTests/platform/win/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-by-default-expected.txt b/LayoutTests/platform/win/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-by-default-expected.txt new file mode 100644 index 000000000000..6ee3f713ff29 --- /dev/null +++ b/LayoutTests/platform/win/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-by-default-expected.txt @@ -0,0 +1,2 @@ +FAIL: Timed out waiting for notifyDone to be called + diff --git a/LayoutTests/platform/win/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-expected.txt b/LayoutTests/platform/win/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-expected.txt new file mode 100644 index 000000000000..6ee3f713ff29 --- /dev/null +++ b/LayoutTests/platform/win/http/tests/security/contentSecurityPolicy/prefetch-src/prefetch-blocked-expected.txt @@ -0,0 +1,2 @@ +FAIL: Timed out waiting for notifyDone to be called + diff --git a/Source/WebCore/loader/LinkLoader.cpp b/Source/WebCore/loader/LinkLoader.cpp index 792474bfbefe..192fead3718a 100644 --- a/Source/WebCore/loader/LinkLoader.cpp +++ b/Source/WebCore/loader/LinkLoader.cpp @@ -296,7 +296,7 @@ void LinkLoader::prefetchIfNeeded(const LinkLoadParameters& params, Document& do // - third-party iframes cannot trigger prefetches // - Number of prefetches of a given page is limited (to 1 maybe?) ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions(); - options.contentSecurityPolicyImposition = ContentSecurityPolicyImposition::SkipPolicyCheck; + options.contentSecurityPolicyImposition = ContentSecurityPolicyImposition::DoPolicyCheck; options.certificateInfoPolicy = CertificateInfoPolicy::IncludeCertificateInfo; options.credentials = FetchOptions::Credentials::SameOrigin; options.redirect = FetchOptions::Redirect::Manual; diff --git a/Source/WebCore/loader/cache/CachedResourceLoader.cpp b/Source/WebCore/loader/cache/CachedResourceLoader.cpp index bb1debb0ed1f..c898f311168a 100644 --- a/Source/WebCore/loader/cache/CachedResourceLoader.cpp +++ b/Source/WebCore/loader/cache/CachedResourceLoader.cpp @@ -518,6 +518,10 @@ bool CachedResourceLoader::allowedByContentSecurityPolicy(CachedResource::Type t if (!m_document->contentSecurityPolicy()->allowImageFromSource(url, redirectResponseReceived, preRedirectURL)) return false; break; + case CachedResource::Type::LinkPrefetch: + if (!m_document->contentSecurityPolicy()->allowPrefetchFromSource(url, redirectResponseReceived, preRedirectURL)) + return false; + break; case CachedResource::Type::SVGFontResource: case CachedResource::Type::FontResource: if (!m_document->contentSecurityPolicy()->allowFontFromSource(url, redirectResponseReceived, preRedirectURL)) diff --git a/Source/WebCore/page/csp/ContentSecurityPolicy.cpp b/Source/WebCore/page/csp/ContentSecurityPolicy.cpp index 13432f468580..a2a73d5c2030 100644 --- a/Source/WebCore/page/csp/ContentSecurityPolicy.cpp +++ b/Source/WebCore/page/csp/ContentSecurityPolicy.cpp @@ -669,6 +669,11 @@ bool ContentSecurityPolicy::allowImageFromSource(const URL& url, RedirectRespons return allowResourceFromSource(url, redirectResponseReceived, &ContentSecurityPolicyDirectiveList::violatedDirectiveForImage, preRedirectURL); } +bool ContentSecurityPolicy::allowPrefetchFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived, const URL& preRedirectURL) const +{ + return allowResourceFromSource(url, redirectResponseReceived, &ContentSecurityPolicyDirectiveList::violatedDirectiveForPrefetch, preRedirectURL); +} + bool ContentSecurityPolicy::allowStyleFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived, const URL& preRedirectURL, const String& nonce) const { if (LegacySchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol())) diff --git a/Source/WebCore/page/csp/ContentSecurityPolicy.h b/Source/WebCore/page/csp/ContentSecurityPolicy.h index d2a1b0e614ee..3a9197798132 100644 --- a/Source/WebCore/page/csp/ContentSecurityPolicy.h +++ b/Source/WebCore/page/csp/ContentSecurityPolicy.h @@ -129,6 +129,7 @@ class ContentSecurityPolicy { WEBCORE_EXPORT bool allowScriptFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No, const URL& preRedirectURL = URL(), const String& = nullString(), const String& nonce = nullString()) const; WEBCORE_EXPORT bool allowWorkerFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No, const URL& preRedirectURL = URL()) const; bool allowImageFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No, const URL& preRedirectURL = URL()) const; + bool allowPrefetchFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No, const URL& preRedirectURL = URL()) const; bool allowStyleFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No, const URL& preRedirectURL = URL(), const String& nonce = nullString()) const; bool allowFontFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No, const URL& preRedirectURL = URL()) const; #if ENABLE(APPLICATION_MANIFEST) diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp index f890b706cc65..a38f64d449dc 100644 --- a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp +++ b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp @@ -361,6 +361,14 @@ const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violat return operativeDirective; } +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForPrefetch(const URL& url, bool didReceiveRedirectResponse) const +{ + auto* operativeDirective = this->operativeDirective(m_prefetchSrc.get(), ContentSecurityPolicyDirectiveNames::prefetchSrc); + if (checkSource(operativeDirective, url, didReceiveRedirectResponse)) + return nullptr; + return operativeDirective; +} + #if ENABLE(APPLICATION_MANIFEST) const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForManifest(const URL& url, bool didReceiveRedirectResponse) const { @@ -672,6 +680,8 @@ void ContentSecurityPolicyDirectiveList::addDirective(ParsedDirective&& directiv setCSPDirective(WTFMove(directive), m_frameAncestors); } else if (equalIgnoringASCIICase(directive.name, ContentSecurityPolicyDirectiveNames::pluginTypes)) setCSPDirective(WTFMove(directive), m_pluginTypes); + else if (equalIgnoringASCIICase(directive.name, ContentSecurityPolicyDirectiveNames::prefetchSrc)) + setCSPDirective(WTFMove(directive), m_prefetchSrc); else if (equalIgnoringASCIICase(directive.name, ContentSecurityPolicyDirectiveNames::sandbox)) applySandboxPolicy(WTFMove(directive)); else if (equalIgnoringASCIICase(directive.name, ContentSecurityPolicyDirectiveNames::reportTo)) diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h index 3d01afef8e49..1ada30747616 100644 --- a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h +++ b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h @@ -65,6 +65,7 @@ class ContentSecurityPolicyDirectiveList { const ContentSecurityPolicyDirective* violatedDirectiveForFrameAncestor(const Frame&) const; const ContentSecurityPolicyDirective* violatedDirectiveForFrameAncestorOrigins(const Vector>&) const; const ContentSecurityPolicyDirective* violatedDirectiveForImage(const URL&, bool didReceiveRedirectResponse) const; + const ContentSecurityPolicyDirective* violatedDirectiveForPrefetch(const URL&, bool didReceiveRedirectResponse) const; #if ENABLE(APPLICATION_MANIFEST) const ContentSecurityPolicyDirective* violatedDirectiveForManifest(const URL&, bool didReceiveRedirectResponse) const; #endif @@ -144,6 +145,7 @@ class ContentSecurityPolicyDirectiveList { #endif std::unique_ptr m_mediaSrc; std::unique_ptr m_objectSrc; + std::unique_ptr m_prefetchSrc; std::unique_ptr m_scriptSrc; std::unique_ptr m_styleSrc; std::unique_ptr m_scriptSrcElem; diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.cpp b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.cpp index 1950ff120a8f..7ccbc8b4acdd 100644 --- a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.cpp +++ b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.cpp @@ -47,6 +47,7 @@ ASCIILiteral imgSrc = "img-src"_s; ASCIILiteral mediaSrc = "media-src"_s; ASCIILiteral objectSrc = "object-src"_s; ASCIILiteral pluginTypes = "plugin-types"_s; +ASCIILiteral prefetchSrc = "prefetch-src"_s; ASCIILiteral reportTo = "report-to"_s; ASCIILiteral reportURI = "report-uri"_s; ASCIILiteral sandbox = "sandbox"_s; diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.h b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.h index c38117da0f01..d213c173349a 100644 --- a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.h +++ b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.h @@ -46,6 +46,7 @@ extern ASCIILiteral manifestSrc; extern ASCIILiteral mediaSrc; extern ASCIILiteral objectSrc; extern ASCIILiteral pluginTypes; +extern ASCIILiteral prefetchSrc; extern ASCIILiteral reportURI; extern ASCIILiteral reportTo; extern ASCIILiteral sandbox;