Skip to content

Commit

Permalink
[CSP] Implement prefetch-src directive
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=185070
rdar://problem/39821187

Reviewed by Brent Fulgham.

Implement the prefetch-src CSP directive. <link rel=prefetch> 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
  • Loading branch information
rreno committed Oct 17, 2022
1 parent 7bfa59d commit b632f9d
Show file tree
Hide file tree
Showing 17 changed files with 135 additions and 1 deletion.
@@ -0,0 +1 @@
PASS
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="prefetch-src 'self'">
<script>
if (window.testRunner) {
testRunner.waitUntilDone();
testRunner.dumpAsText();
}
function runTest() {
var link = document.createElement("link");
link.rel = "prefetch";
link.href = `${window.origin}/cache/resources/prefetched-main-resource.py`;
window.addEventListener("securitypolicyviolation", () => {
document.getElementById("log").innerText = ("FAIL: prefetch was blocked by CSP");
testRunner.notifyDone();
});
if (internals) {
internals.addPrefetchLoadEventListener(link, () => {
document.getElementById("log").innerText = "PASS";
testRunner.notifyDone();
});
}

document.body.appendChild(link);
}
</script>
</head>
<body onload="runTest()">
<div id="log"></div>
</body>
</html>
@@ -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
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline' 'self'">
<script>
if (window.testRunner) {
testRunner.waitUntilDone();
testRunner.dumpAsText();
}

function runTest() {
let link = document.createElement("link");
link.rel = "prefetch";
link.href = `${window.origin}/cache/resources/prefetched-main-resource.py`;

window.addEventListener("securitypolicyviolation", () => {
document.getElementById("log").innerText = "PASS";
testRunner.notifyDone();
});

if (internals) {
internals.addPrefetchLoadEventListener(link, () => {
document.getElementById("log").innerText = "FAIL: default-src blocked prefetch was allowed.";
testRunner.notifyDone();
});
}

document.body.appendChild(link);
}
</script>
</head>
<body onload="runTest()">
<div id="log"></div>
</body>
</html>
@@ -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
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="prefetch-src 'self'">
<script>
if (window.testRunner) {
testRunner.waitUntilDone();
testRunner.dumpAsText();
}
function runTest() {
var link = document.createElement("link");
link.rel = "prefetch";
link.href = "http://localhost:8000/cache/resources/prefetched-main-resource.py";
window.addEventListener("securitypolicyviolation", () => {
document.getElementById("log").innerText = "PASS";
testRunner.notifyDone();
});
if (internals) {
internals.addPrefetchLoadEventListener(link, () => {
document.getElementById("log").innerText = "FAIL: prefetch was not blocked by CSP.";
testRunner.notifyDone();
});
}

document.body.appendChild(link);
}
</script>
</head>
<body onload="runTest()">
<div id="log"></div>
</body>
</html>
@@ -0,0 +1,2 @@
FAIL: Timed out waiting for notifyDone to be called

@@ -0,0 +1,2 @@
FAIL: Timed out waiting for notifyDone to be called

@@ -0,0 +1,2 @@
FAIL: Timed out waiting for notifyDone to be called

2 changes: 1 addition & 1 deletion Source/WebCore/loader/LinkLoader.cpp
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions Source/WebCore/loader/cache/CachedResourceLoader.cpp
Expand Up @@ -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))
Expand Down
5 changes: 5 additions & 0 deletions Source/WebCore/page/csp/ContentSecurityPolicy.cpp
Expand Up @@ -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()))
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/page/csp/ContentSecurityPolicy.h
Expand Up @@ -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)
Expand Down
10 changes: 10 additions & 0 deletions Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp
Expand Up @@ -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
{
Expand Down Expand Up @@ -672,6 +680,8 @@ void ContentSecurityPolicyDirectiveList::addDirective(ParsedDirective&& directiv
setCSPDirective<ContentSecurityPolicySourceListDirective>(WTFMove(directive), m_frameAncestors);
} else if (equalIgnoringASCIICase(directive.name, ContentSecurityPolicyDirectiveNames::pluginTypes))
setCSPDirective<ContentSecurityPolicyMediaListDirective>(WTFMove(directive), m_pluginTypes);
else if (equalIgnoringASCIICase(directive.name, ContentSecurityPolicyDirectiveNames::prefetchSrc))
setCSPDirective<ContentSecurityPolicySourceListDirective>(WTFMove(directive), m_prefetchSrc);
else if (equalIgnoringASCIICase(directive.name, ContentSecurityPolicyDirectiveNames::sandbox))
applySandboxPolicy(WTFMove(directive));
else if (equalIgnoringASCIICase(directive.name, ContentSecurityPolicyDirectiveNames::reportTo))
Expand Down
2 changes: 2 additions & 0 deletions Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h
Expand Up @@ -65,6 +65,7 @@ class ContentSecurityPolicyDirectiveList {
const ContentSecurityPolicyDirective* violatedDirectiveForFrameAncestor(const Frame&) const;
const ContentSecurityPolicyDirective* violatedDirectiveForFrameAncestorOrigins(const Vector<RefPtr<SecurityOrigin>>&) 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
Expand Down Expand Up @@ -144,6 +145,7 @@ class ContentSecurityPolicyDirectiveList {
#endif
std::unique_ptr<ContentSecurityPolicySourceListDirective> m_mediaSrc;
std::unique_ptr<ContentSecurityPolicySourceListDirective> m_objectSrc;
std::unique_ptr<ContentSecurityPolicySourceListDirective> m_prefetchSrc;
std::unique_ptr<ContentSecurityPolicySourceListDirective> m_scriptSrc;
std::unique_ptr<ContentSecurityPolicySourceListDirective> m_styleSrc;
std::unique_ptr<ContentSecurityPolicySourceListDirective> m_scriptSrcElem;
Expand Down
Expand Up @@ -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;
Expand Down
Expand Up @@ -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;
Expand Down

0 comments on commit b632f9d

Please sign in to comment.