summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2021-10-07 13:54:14 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2022-01-28 20:25:55 +0100
commit034a84d0507a486970bf9ebee18912b319eff7cb (patch)
treef3562bf2eed965db1d3f1aead1a1c63a6ba6c0fe
parentb8673ae8e2eb38af68955dfe95759d97343a8423 (diff)
Improve local scheme access rules
Task-number: QTBUG-96849 Change-Id: Ieb24da12a61e5e37b29ccf2d1a11b7bd863b842e Reviewed-by: Michal Klocek <michal.klocek@qt.io> (cherry picked from commit 3071de1e07be28d763164a037d946281146bf31d) Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
-rw-r--r--src/core/api/qwebengineurlscheme.cpp14
-rw-r--r--src/core/content_browser_client_qt.cpp13
-rw-r--r--src/core/doc/src/qwebenginesettings_lgpl.qdoc5
-rw-r--r--src/core/net/custom_url_loader_factory.cpp21
-rw-r--r--src/core/net/proxying_url_loader_factory_qt.cpp45
-rw-r--r--src/core/web_engine_settings.cpp2
-rw-r--r--tests/auto/core/origins/resources/mixedSchemes.html2
-rw-r--r--tests/auto/core/origins/resources/mixedSchemes_frame.html1
-rw-r--r--tests/auto/core/origins/tst_origins.cpp351
-rw-r--r--tests/auto/httpserver/httpserver.cpp1
10 files changed, 349 insertions, 106 deletions
diff --git a/src/core/api/qwebengineurlscheme.cpp b/src/core/api/qwebengineurlscheme.cpp
index f73992c6b..c187fc840 100644
--- a/src/core/api/qwebengineurlscheme.cpp
+++ b/src/core/api/qwebengineurlscheme.cpp
@@ -171,12 +171,12 @@ public:
Indicates that the URL scheme provides access to local resources. The purpose
of this flag is to prevent network content from accessing local resources.
Only schemes with the \c LocalAccessAllowed flag may load resources from a
- scheme with the \c Local flag. The only builtin schemes with this flag are \c
- file and \c qrc.
+ scheme with the \c LocalScheme flag. The only builtin scheme with this flag is \c
+ file.
\value LocalAccessAllowed
Indicates that content from this scheme should be allowed to load resources
- from schemes with the \c Local flag.
+ from schemes with the \c LocalScheme flag.
\value NoAccessAllowed
Indicates that all content from this scheme should be forced to have unique
@@ -194,10 +194,10 @@ public:
\value CorsEnabled
Enables cross-origin resource sharing (CORS) for this scheme. This flag is
- required in order to, for example, use the scheme with the \l
- {https://fetch.spec.whatwg.org/}{Fetch API}, or to deliver CSS fonts to a
- different origin. The appropriate CORS headers are generated automatically by
- the QWebEngineUrlRequestJob class. (Added in Qt 5.14)
+ required in order for content to be loaded by documents of a different origin,
+ this includes access from other schemes. The appropriate CORS headers are
+ generated automatically by the QWebEngineUrlRequestJob class. By default only
+ \c http and \c https are CORS enabled. (Added in Qt 5.14)
*/
QWebEngineUrlScheme::QWebEngineUrlScheme(QWebEngineUrlSchemePrivate *d) : d(d) {}
diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp
index 2b1485b2d..6201bab27 100644
--- a/src/core/content_browser_client_qt.cpp
+++ b/src/core/content_browser_client_qt.cpp
@@ -1102,15 +1102,10 @@ void ContentBrowserClientQt::RegisterNonNetworkSubresourceURLLoaderFactories(int
// Install file scheme if necessary:
bool install_file_scheme = false;
if (web_contents && !is_background_page) {
- const auto *settings = static_cast<WebContentsDelegateQt *>(web_contents->GetResponsibleWebContents()->GetDelegate())->webEngineSettings();
- if (settings->testAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls)) {
- for (const auto &local_scheme : url::GetLocalSchemes()) {
- if (url.SchemeIs(local_scheme)) {
- install_file_scheme = true;
- break;
- }
- }
- }
+ const std::string scheme = url.scheme();
+ install_file_scheme = base::Contains(url::GetLocalSchemes(), scheme);
+ if (const url::CustomScheme *cs = url::CustomScheme::FindScheme(scheme))
+ install_file_scheme = cs->flags & (url::CustomScheme::LocalAccessAllowed | url::CustomScheme::Local);
}
if (install_file_scheme && factories->find(url::kFileScheme) == factories->end()) {
diff --git a/src/core/doc/src/qwebenginesettings_lgpl.qdoc b/src/core/doc/src/qwebenginesettings_lgpl.qdoc
index bfe2713a2..a08b13f2c 100644
--- a/src/core/doc/src/qwebenginesettings_lgpl.qdoc
+++ b/src/core/doc/src/qwebenginesettings_lgpl.qdoc
@@ -96,10 +96,7 @@
\value LocalStorageEnabled
Enables support for the HTML 5 local storage feature. Enabled by default.
\value LocalContentCanAccessRemoteUrls
- Allows locally loaded documents to ignore cross-origin rules so that they can access
- remote resources that would normally be blocked, since remote resources are
- considered cross-origin for a local document. Remote access that would not be blocked by
- cross-origin rules is still possible when this setting is disabled (default).
+ Allows local origin documents to access remote resources that would normally be blocked.
Note that disabling this setting does not prevent media elements in local files from
accessing remote content. Disabled by default.
\value XSSAuditingEnabled
diff --git a/src/core/net/custom_url_loader_factory.cpp b/src/core/net/custom_url_loader_factory.cpp
index 283576cac..6cb272b1c 100644
--- a/src/core/net/custom_url_loader_factory.cpp
+++ b/src/core/net/custom_url_loader_factory.cpp
@@ -55,6 +55,8 @@
#include "services/network/public/mojom/url_loader.mojom.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
+#include "url/url_util.h"
+#include "url/url_util_qt.h"
#include "api/qwebengineurlscheme.h"
#include "net/url_request_custom_job_proxy.h"
@@ -135,6 +137,7 @@ private:
m_error = 0;
QWebEngineUrlScheme scheme = QWebEngineUrlScheme::schemeByName(QByteArray::fromStdString(request.url.scheme()));
m_corsEnabled = scheme.flags().testFlag(QWebEngineUrlScheme::CorsEnabled);
+ m_isLocal = scheme.flags().testFlag(QWebEngineUrlScheme::LocalScheme);
}
~CustomURLLoader() override = default;
@@ -148,10 +151,21 @@ private:
if (!m_request.request_initiator)
return CompleteWithFailure(net::ERR_INVALID_ARGUMENT);
- // Custom schemes are not covered by CorsURLLoader, so we need to reject CORS requests manually.
- if (!m_corsEnabled && !m_request.request_initiator->IsSameOriginWith(url::Origin::Create(m_request.url)))
- return CompleteWithFailure(network::CorsErrorStatus(network::mojom::CorsError::kCorsDisabledScheme));
+ if (m_isLocal) {
+ std::string fromScheme = m_request.request_initiator->GetTupleOrPrecursorTupleIfOpaque().scheme();
+ const std::vector<std::string> &localSchemes = url::GetLocalSchemes();
+ bool fromLocal = base::Contains(localSchemes, fromScheme);
+ bool hasLocalAccess = fromLocal;
+ if (const url::CustomScheme *cs = url::CustomScheme::FindScheme(fromScheme))
+ hasLocalAccess = cs->flags & (url::CustomScheme::LocalAccessAllowed | url::CustomScheme::Local);
+ if (!hasLocalAccess)
+ return CompleteWithFailure(net::ERR_ACCESS_DENIED);
+ } else if (!m_corsEnabled && !m_request.request_initiator->IsSameOriginWith(url::Origin::Create(m_request.url))) {
+ // Custom schemes are not covered by CorsURLLoader, so we need to reject CORS requests manually.
+ return CompleteWithFailure(network::CorsErrorStatus(network::mojom::CorsError::kCorsDisabledScheme));
+ }
}
+
if (mojo::CreateDataPipe(nullptr, m_pipeProducerHandle, m_pipeConsumerHandle) != MOJO_RESULT_OK)
return CompleteWithFailure(net::ERR_FAILED);
@@ -453,6 +467,7 @@ private:
qint64 m_headerBytesRead = 0;
qint64 m_totalBytesRead = 0;
bool m_corsEnabled;
+ bool m_isLocal;
base::WeakPtrFactory<CustomURLLoader> m_weakPtrFactory{this};
diff --git a/src/core/net/proxying_url_loader_factory_qt.cpp b/src/core/net/proxying_url_loader_factory_qt.cpp
index be2a6814b..9bdebf6be 100644
--- a/src/core/net/proxying_url_loader_factory_qt.cpp
+++ b/src/core/net/proxying_url_loader_factory_qt.cpp
@@ -53,6 +53,7 @@
#include "services/network/public/mojom/early_hints.mojom.h"
#include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
#include "url/url_util.h"
+#include "url/url_util_qt.h"
#include "api/qwebengineurlrequestinfo_p.h"
#include "type_conversion.h"
@@ -168,7 +169,10 @@ private:
const int frame_tree_node_id_;
const int32_t request_id_;
const uint32_t options_;
- bool allowed_cors_ = true;
+ bool allow_local_ = false;
+ bool allow_remote_ = true;
+ bool local_access_ = false;
+ bool remote_access_ = true;
// If the |target_loader_| called OnComplete with an error this stores it.
// That way the destructor can send it to OnReceivedError if safe browsing
@@ -222,22 +226,20 @@ InterceptedRequest::InterceptedRequest(ProfileAdapter *profile_adapter,
base::BindOnce(&InterceptedRequest::OnURLLoaderError, base::Unretained(this)));
if (!disable_web_security && request_.request_initiator) {
const std::vector<std::string> &localSchemes = url::GetLocalSchemes();
- std::string fromScheme = request_.request_initiator->GetTupleOrPrecursorTupleIfOpaque().scheme();
- if (base::Contains(localSchemes, fromScheme)) {
+ const std::string fromScheme = request_.request_initiator->GetTupleOrPrecursorTupleIfOpaque().scheme();
+ const std::string toScheme = request_.url.scheme();
+ const bool fromLocal = base::Contains(localSchemes, fromScheme);
+ const bool toLocal = base::Contains(localSchemes, toScheme);
+ bool hasLocalAccess = false;
+ local_access_ = toLocal;
+ remote_access_ = !toLocal && (toScheme != "data") && (toScheme != "qrc");
+ if (const url::CustomScheme *cs = url::CustomScheme::FindScheme(fromScheme))
+ hasLocalAccess = cs->flags & url::CustomScheme::LocalAccessAllowed;
+ if (fromLocal || toLocal) {
content::WebContents *wc = webContents();
- std::string toScheme = request_.url.scheme();
// local schemes must have universal access, or be accessing something local and have local access.
- if (fromScheme != toScheme) {
- // note allow_file_access_from_file_urls maps to LocalContentCanAccessFileUrls in our API
- // and allow_universal_access_from_file_urls to LocalContentCanAccessRemoteUrls, so we are
- // using them as proxies for our API here.
- if (toScheme == "file")
- allowed_cors_ = wc && wc->GetOrCreateWebPreferences().allow_file_access_from_file_urls;
- else if (!base::Contains(localSchemes, toScheme))
- allowed_cors_ = wc && wc->GetOrCreateWebPreferences().allow_universal_access_from_file_urls;
- else
- allowed_cors_ = true; // We should think about this for future patches
- }
+ allow_local_ = hasLocalAccess || (fromLocal && wc && wc->GetOrCreateWebPreferences().allow_file_access_from_file_urls);
+ allow_remote_ = !fromLocal || (wc && wc->GetOrCreateWebPreferences().allow_remote_access_from_local_urls);
}
}
}
@@ -273,10 +275,15 @@ void InterceptedRequest::Restart()
{
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- // This is a CORS check on the from URL, the normal check on the to URL is applied later
- if (!allowed_cors_ && current_response_->response_type == network::mojom::FetchResponseType::kCors) {
- target_client_->OnComplete(network::URLLoaderCompletionStatus(
- network::CorsErrorStatus(network::mojom::CorsError::kCorsDisabledScheme)));
+ // Check if non-local access is allowed
+ if (!allow_remote_ && remote_access_) {
+ target_client_->OnComplete(network::URLLoaderCompletionStatus(net::ERR_NETWORK_ACCESS_DENIED));
+ delete this;
+ return;
+ }
+ // Check if local access is allowed
+ if (!allow_local_ && local_access_) {
+ target_client_->OnComplete(network::URLLoaderCompletionStatus(net::ERR_ACCESS_DENIED));
delete this;
return;
}
diff --git a/src/core/web_engine_settings.cpp b/src/core/web_engine_settings.cpp
index 007249f42..a04efdd1f 100644
--- a/src/core/web_engine_settings.cpp
+++ b/src/core/web_engine_settings.cpp
@@ -377,7 +377,7 @@ void WebEngineSettings::applySettingsToWebPreferences(blink::web_pref::WebPrefer
prefs->tabs_to_links = testAttribute(QWebEngineSettings::LinksIncludedInFocusChain);
prefs->local_storage_enabled = testAttribute(QWebEngineSettings::LocalStorageEnabled);
prefs->databases_enabled = testAttribute(QWebEngineSettings::LocalStorageEnabled);
- prefs->allow_universal_access_from_file_urls =
+ prefs->allow_remote_access_from_local_urls =
testAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls);
prefs->spatial_navigation_enabled = testAttribute(QWebEngineSettings::SpatialNavigationEnabled);
prefs->allow_file_access_from_file_urls =
diff --git a/tests/auto/core/origins/resources/mixedSchemes.html b/tests/auto/core/origins/resources/mixedSchemes.html
index c73e9ecdc..260372a29 100644
--- a/tests/auto/core/origins/resources/mixedSchemes.html
+++ b/tests/auto/core/origins/resources/mixedSchemes.html
@@ -12,7 +12,7 @@
document.getElementById("iframe").setAttribute("src", url);
// Early fire is OK unless the test is expecting cannotLoad.
// If timeout is too short then a false positive is possible.
- setTimeout(() => { result = result || "cannotLoad"; }, 500);
+ setTimeout(() => { result = result || "cannotLoad"; }, 1000);
}
addEventListener("load", function() {
diff --git a/tests/auto/core/origins/resources/mixedSchemes_frame.html b/tests/auto/core/origins/resources/mixedSchemes_frame.html
index 00c20ba37..40ace2d2f 100644
--- a/tests/auto/core/origins/resources/mixedSchemes_frame.html
+++ b/tests/auto/core/origins/resources/mixedSchemes_frame.html
@@ -3,6 +3,7 @@
<head>
<title>Mixed - Frame</title>
<script>
+ console.log('Frame Loaded');
var canary = true;
parent.canary = true;
</script>
diff --git a/tests/auto/core/origins/tst_origins.cpp b/tests/auto/core/origins/tst_origins.cpp
index 1731bee8a..ce670991a 100644
--- a/tests/auto/core/origins/tst_origins.cpp
+++ b/tests/auto/core/origins/tst_origins.cpp
@@ -142,7 +142,17 @@ void registerSchemes()
QWebEngineUrlScheme::registerScheme(scheme);
}
{
+ QWebEngineUrlScheme scheme(QBAL("localaccess"));
+ scheme.setFlags(QWebEngineUrlScheme::LocalAccessAllowed);
+ QWebEngineUrlScheme::registerScheme(scheme);
+ }
+ {
QWebEngineUrlScheme scheme(QBAL("local"));
+ scheme.setFlags(QWebEngineUrlScheme::LocalScheme);
+ QWebEngineUrlScheme::registerScheme(scheme);
+ }
+ {
+ QWebEngineUrlScheme scheme(QBAL("local-localaccess"));
scheme.setFlags(QWebEngineUrlScheme::LocalScheme | QWebEngineUrlScheme::LocalAccessAllowed);
QWebEngineUrlScheme::registerScheme(scheme);
}
@@ -178,7 +188,9 @@ public:
profile->installUrlSchemeHandler(QBAL("redirect1"), this);
profile->installUrlSchemeHandler(QBAL("redirect2"), this);
profile->installUrlSchemeHandler(QBAL("cors"), this);
+ profile->installUrlSchemeHandler(QBAL("localaccess"), this);
profile->installUrlSchemeHandler(QBAL("local"), this);
+ profile->installUrlSchemeHandler(QBAL("local-localaccess"), this);
profile->installUrlSchemeHandler(QBAL("local-cors"), this);
}
@@ -217,6 +229,21 @@ private:
QList<QUrl> m_requests;
};
+class TestPage : public QWebEnginePage
+{
+public:
+ TestPage(QWebEngineProfile *profile) : QWebEnginePage(profile, nullptr)
+ {
+ }
+ void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel,
+ const QString &message, int,
+ const QString &) override
+ {
+ messages << message;
+ }
+ QStringList messages;
+};
+
class tst_Origins final : public QObject {
Q_OBJECT
@@ -237,6 +264,8 @@ private Q_SLOTS:
void mixedSchemesWithCsp();
void mixedXHR_data();
void mixedXHR();
+ void mixedContent_data();
+ void mixedContent();
#if defined(WEBSOCKETS)
void webSocket();
#endif
@@ -262,7 +291,7 @@ private:
}
QWebEngineProfile m_profile;
- QWebEnginePage *m_page = nullptr;
+ TestPage *m_page = nullptr;
TstUrlSchemeHandler *m_handler = nullptr;
};
@@ -283,7 +312,7 @@ void tst_Origins::cleanupTestCase()
void tst_Origins::init()
{
- m_page = new QWebEnginePage(&m_profile, nullptr);
+ m_page = new TestPage(&m_profile);
}
void tst_Origins::cleanup()
@@ -501,8 +530,6 @@ void tst_Origins::subdirWithoutAccess()
{
ScopedAttribute sa(m_page->settings(), QWebEngineSettings::LocalContentCanAccessFileUrls, false);
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
QVERIFY(verifyLoad("file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath()
+ "/resources/subdir/index.html"));
QCOMPARE(eval(QSL("msg[0]")), QVariant());
@@ -532,9 +559,8 @@ void tst_Origins::fileAccessRemoteUrl()
server.setResourceDirs({ QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + "/resources" });
QVERIFY(server.start());
- ScopedAttribute sa(m_page->settings(), QWebEngineSettings::LocalContentCanAccessRemoteUrls, EnableAccess);
- if (!EnableAccess)
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("blocked by CORS policy")));
+ ScopedAttribute sa1(m_page->settings(), QWebEngineSettings::LocalContentCanAccessRemoteUrls, EnableAccess);
+ ScopedAttribute sa2(m_page->settings(), QWebEngineSettings::ErrorPageEnabled, false);
QVERIFY(verifyLoad("file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath()
+ "/resources/mixedXHR.html"));
@@ -553,35 +579,31 @@ void tst_Origins::fileAccessRemoteUrl()
// file: scheme.
void tst_Origins::mixedSchemes()
{
+ ScopedAttribute sa(m_page->settings(), QWebEngineSettings::ErrorPageEnabled, false);
+
QVERIFY(verifyLoad("file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath()
+ "/resources/mixedSchemes.html"));
eval("setIFrameUrl('file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath()
+ "/resources/mixedSchemes_frame.html')");
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('qrc:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('tst:/resources/mixedSchemes_frame.html')"));
- QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
+ QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("cannotLoad")));
QVERIFY(verifyLoad(QSL("qrc:/resources/mixedSchemes.html")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Not allowed to load local resource")));
eval("setIFrameUrl('file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath()
+ "/resources/mixedSchemes_frame.html')");
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("cannotLoad")));
eval(QSL("setIFrameUrl('qrc:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('tst:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
QVERIFY(verifyLoad(QSL("tst:/resources/mixedSchemes.html")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Not allowed to load local resource")));
eval("setIFrameUrl('file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath()
+ "/resources/mixedSchemes_frame.html')");
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("cannotLoad")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('qrc:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
eval(QSL("setIFrameUrl('tst:/resources/mixedSchemes_frame.html')"));
@@ -590,66 +612,68 @@ void tst_Origins::mixedSchemes()
QVERIFY(verifyLoad(QSL("PathSyntax:/resources/mixedSchemes.html")));
eval(QSL("setIFrameUrl('PathSyntax:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Not allowed to load local resource")));
eval(QSL("setIFrameUrl('PathSyntax-Local:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("cannotLoad")));
eval(QSL("setIFrameUrl('PathSyntax-LocalAccessAllowed:/resources/mixedSchemes_frame.html')"));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('PathSyntax-NoAccessAllowed:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
QVERIFY(verifyLoad(QSL("PathSyntax-LocalAccessAllowed:/resources/mixedSchemes.html")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('PathSyntax:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('PathSyntax-Local:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
eval(QSL("setIFrameUrl('PathSyntax-LocalAccessAllowed:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('PathSyntax-NoAccessAllowed:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
QVERIFY(verifyLoad(QSL("PathSyntax-NoAccessAllowed:/resources/mixedSchemes.html")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('PathSyntax:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Not allowed to load local resource")));
eval(QSL("setIFrameUrl('PathSyntax-Local:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("cannotLoad")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('PathSyntax-LocalAccessAllowed:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('PathSyntax-NoAccessAllowed:/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
QVERIFY(verifyLoad(QSL("HostSyntax://a/resources/mixedSchemes.html")));
eval(QSL("setIFrameUrl('HostSyntax://a/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('HostSyntax://b/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
+
+ QVERIFY(verifyLoad(QSL("local-localaccess:/resources/mixedSchemes.html")));
+ eval("setIFrameUrl('local-cors:/resources/mixedSchemes_frame.html')");
+ QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
+ eval(QSL("setIFrameUrl('local-localaccess:/resources/mixedSchemes_frame.html')"));
+ QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess")));
+ eval(QSL("setIFrameUrl('local:/resources/mixedSchemes_frame.html')"));
+ QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
+
+ QVERIFY(verifyLoad(QSL("local-cors:/resources/mixedSchemes.html")));
+ eval("setIFrameUrl('local:/resources/mixedSchemes_frame.html')");
+ QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
+ eval(QSL("setIFrameUrl('local-cors:/resources/mixedSchemes_frame.html')"));
+ QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess")));
+ eval(QSL("setIFrameUrl('local:/resources/mixedSchemes_frame.html')"));
+ QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
}
// Like mixedSchemes but adds a Content-Security-Policy: frame-src 'none' header.
void tst_Origins::mixedSchemesWithCsp()
{
QVERIFY(verifyLoad(QSL("HostSyntax://a/resources/mixedSchemesWithCsp.html")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("violates the following Content Security Policy")));
eval(QSL("setIFrameUrl('HostSyntax://a/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("violates the following Content Security Policy")));
eval(QSL("setIFrameUrl('HostSyntax://b/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
QVERIFY(verifyLoad(QSL("HostSyntax-ContentSecurityPolicyIgnored://a/resources/mixedSchemesWithCsp.html")));
eval(QSL("setIFrameUrl('HostSyntax-ContentSecurityPolicyIgnored://a/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess")));
- QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError")));
eval(QSL("setIFrameUrl('HostSyntax-ContentSecurityPolicyIgnored://b/resources/mixedSchemes_frame.html')"));
QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess")));
}
@@ -683,55 +707,90 @@ void tst_Origins::mixedXHR_data()
std::pair<const char *, std::vector<
std::pair<const char *, std::vector<QVariant>>>>> data = {
{ "file", {
- { "file", { OK, OK, ERR, OK } },
- { "qrc", { ERR, ERR, ERR, ERR } },
- { "tst", { ERR, ERR, ERR, ERR } },
- { "data", { OK, OK, OK, OK } },
- { "cors", { ERR, OK, ERR, OK } },
- { "local", { ERR, ERR, ERR, ERR } },
- { "local-cors", { OK, OK, OK, OK } }, } },
+ { "file", { OK, OK, ERR, ERR } },
+ { "qrc", { ERR, ERR, ERR, ERR } },
+ { "tst", { ERR, ERR, ERR, ERR } },
+ { "data", { OK, OK, OK, OK } },
+ { "cors", { ERR, OK, ERR, OK } },
+ { "local-localaccess", { OK, OK, ERR, ERR } },
+ { "local-cors", { OK, OK, ERR, ERR } }, } },
{ "qrc", {
- { "file", { ERR, ERR, ERR, ERR } },
- { "qrc", { OK, OK, OK, OK } },
- { "tst", { ERR, ERR, ERR, ERR } },
- { "data", { OK, OK, OK, OK } },
- { "cors", { OK, OK, OK, OK } },
- { "local", { ERR, ERR, ERR, ERR } },
- { "local-cors", { ERR, ERR, ERR, ERR } }, } },
+ { "file", { ERR, ERR, ERR, ERR } },
+ { "qrc", { OK, OK, OK, OK } },
+ { "tst", { ERR, ERR, ERR, ERR } },
+ { "data", { OK, OK, OK, OK } },
+ { "cors", { OK, OK, OK, OK } },
+ { "local-localaccess", { ERR, ERR, ERR, ERR } },
+ { "local-cors", { ERR, ERR, ERR, ERR } }, } },
{ "tst", {
- { "file", { ERR, ERR, ERR, ERR } },
- { "qrc", { ERR, ERR, ERR, ERR } },
- { "tst", { OK, OK, OK, OK } },
- { "data", { OK, OK, OK, OK } },
- { "cors", { OK, OK, OK, OK } },
- { "local", { ERR, ERR, ERR, ERR } },
- { "local-cors", { ERR, ERR, ERR, ERR } }, } },
-
- { "local", {
- { "file", { ERR, ERR, ERR, ERR } },
- { "qrc", { ERR, ERR, ERR, ERR } },
- { "tst", { ERR, ERR, ERR, ERR } },
- { "data", { OK, OK, OK, OK } },
- { "cors", { ERR, OK, ERR, OK } },
- { "local", { OK, OK, ERR, OK } },
- { "local-cors", { OK, OK, OK, OK } }, } },
+ { "file", { ERR, ERR, ERR, ERR } },
+ { "qrc", { ERR, ERR, ERR, ERR } },
+ { "tst", { OK, OK, OK, OK } },
+ { "data", { OK, OK, OK, OK } },
+ { "cors", { OK, OK, OK, OK } },
+ { "local-localaccess", { ERR, ERR, ERR, ERR } },
+ { "local-cors", { ERR, ERR, ERR, ERR } }, } },
+
+ { "cors", { // -local +cors -local-access
+ { "file", { ERR, ERR, ERR, ERR } },
+ { "qrc", { ERR, ERR, ERR, ERR } },
+ { "tst", { ERR, ERR, ERR, ERR } },
+ { "data", { OK, OK, OK, OK } },
+ { "cors", { OK, OK, OK, OK } },
+ { "local-localaccess", { ERR, ERR, ERR, ERR } },
+ { "local-cors", { ERR, ERR, ERR, ERR } }, } },
+
+ { "local", { // +local -cors -local-access
+ { "file", { OK, OK, ERR, ERR } },
+ { "qrc", { ERR, ERR, ERR, ERR } },
+ { "tst", { ERR, ERR, ERR, ERR } },
+ { "data", { OK, OK, OK, OK } },
+ { "cors", { ERR, OK, ERR, OK } },
+ { "local-localaccess", { OK, OK, ERR, ERR } },
+ { "local-cors", { OK, OK, ERR, ERR } }, } },
+
+ { "local-cors", { // +local +cors -local-access
+ { "file", { OK, OK, ERR, ERR } },
+ { "qrc", { ERR, ERR, ERR, ERR } },
+ { "tst", { ERR, ERR, ERR, ERR } },
+ { "data", { OK, OK, OK, OK } },
+ { "cors", { ERR, OK, ERR, OK } },
+ { "local-localaccess", { OK, OK, ERR, ERR } },
+ { "local-cors", { OK, OK, ERR, ERR } }, } },
+
+ { "local-localaccess", { // +local -cors +local-access
+ { "file", { OK, OK, OK, OK } },
+ { "qrc", { ERR, ERR, ERR, ERR } },
+ { "tst", { ERR, ERR, ERR, ERR } },
+ { "data", { OK, OK, OK, OK } },
+ { "cors", { ERR, OK, ERR, OK } },
+ { "local-localaccess", { OK, OK, OK, OK } },
+ { "local-cors", { OK, OK, OK, OK } }, } },
+
+ { "localaccess", { // -local -cors +local-access
+ { "file", { OK, OK, OK, OK } },
+ { "qrc", { ERR, ERR, ERR, ERR } },
+ { "tst", { ERR, ERR, ERR, ERR } },
+ { "data", { OK, OK, OK, OK } },
+ { "cors", { OK, OK, OK, OK } },
+ { "local-localaccess", { OK, OK, OK, OK } },
+ { "local-cors", { OK, OK, OK, OK } }, } },
};
for (auto &&d : data) {
auto schemeFrom = d.first;
for (int i = 0; i < 4; ++i) {
- auto it = settingCombinations.begin() + i;
- bool canAccessFileUrls = it->first, canAccessRemoteUrl = it->second;
+ const auto &it = settingCombinations[i];
+ bool canAccessFileUrls = it.first, canAccessRemoteUrl = it.second;
QVariantMap testPairs;
for (auto &&destSchemes : d.second) {
auto &&destScheme = destSchemes.first;
auto &&expectedResults = destSchemes.second;
- auto rit = expectedResults.begin() + i;
- testPairs[destScheme] = *rit;
+ testPairs[destScheme] = expectedResults[i];
}
QTest::addRow("%s_%s_%s", schemeFrom, (canAccessFileUrls ? "local" : "nolocal"), (canAccessRemoteUrl ? "remote" : "noremote"))
@@ -780,6 +839,174 @@ void tst_Origins::mixedXHR()
.arg(schemeFrom).arg(schemesTo.join(' ')).arg(results.join(' ')).arg(expected.join(' '))));
}
+// Load the main page over one scheme, then load an iframe over a different scheme. This load is not considered CORS.
+void tst_Origins::mixedContent_data()
+{
+ QTest::addColumn<QString>("schemeFrom");
+ QTest::addColumn<bool>("canAccessFileUrls");
+ QTest::addColumn<bool>("canAccessRemoteUrl");
+ QTest::addColumn<QVariantMap>("testPairs");
+
+ bool defaultFileAccess = true;
+ bool defaultRemoteAccess = false;
+ std::vector<std::pair<bool, bool>> settingCombinations = {
+ { defaultFileAccess, defaultRemoteAccess }, // tag: *schemeFrom*_local_noremote
+ { defaultFileAccess, !defaultRemoteAccess }, // tag: *schemeFrom*_local_remote
+ { !defaultFileAccess, defaultRemoteAccess }, // tag: *schemeFrom*_nolocal_noremote
+ { !defaultFileAccess, !defaultRemoteAccess } // tag: *schemeFrom*_nolocal_remote
+ };
+
+ QVariant SLF = QVariant(QSL("canLoadAndAccess")), OK = QVariant(QSL("canLoadButNotAccess")), ERR = QVariant(QSL("cannotLoad"));
+ std::vector<
+ std::pair<const char *, std::vector<
+ std::pair<const char *, std::vector<QVariant>>>>> data = {
+ { "file", {
+ { "file", { SLF, SLF, ERR, ERR } },
+ { "qrc", { OK, OK, OK, OK } },
+ { "tst", { ERR, OK, ERR, OK } },
+ { "data", { OK, OK, OK, OK } },
+ { "cors", { ERR, OK, ERR, OK } },
+ { "local-localaccess", { OK, OK, ERR, ERR } },
+ { "local-cors", { OK, OK, ERR, ERR } },
+ } },
+
+ { "qrc", {
+ { "file", { ERR, ERR, ERR, ERR } },
+ { "qrc", { SLF, SLF, SLF, SLF } },
+ { "tst", { OK, OK, OK, OK } },
+ { "data", { OK, OK, OK, OK } },
+ { "cors", { OK, OK, OK, OK } },
+ { "local-localaccess", { ERR, ERR, ERR, ERR } },
+ { "local-cors", { ERR, ERR, ERR, ERR } }, } },
+
+ { "tst", {
+ { "file", { ERR, ERR, ERR, ERR } },
+ { "qrc", { OK, OK, OK, OK } },
+ { "tst", { SLF, SLF, SLF, SLF } },
+ { "data", { OK, OK, OK, OK } },
+ { "cors", { OK, OK, OK, OK } },
+ { "local-localaccess", { ERR, ERR, ERR, ERR } },
+ { "local-cors", { ERR, ERR, ERR, ERR } }, } },
+
+ { "cors", { // -local +cors -local-access
+ { "file", { ERR, ERR, ERR, ERR } },
+ { "qrc", { OK, OK, OK, OK } },
+ { "tst", { OK, OK, OK, OK } },
+ { "data", { OK, OK, OK, OK } },
+ { "cors", { SLF, SLF, SLF, SLF } },
+ { "local-localaccess", { ERR, ERR, ERR, ERR } },
+ { "local-cors", { ERR, ERR, ERR, ERR } }, } },
+
+ { "local", { // +local -cors -local-access
+ { "file", { OK, OK, ERR, ERR } },
+ { "qrc", { OK, OK, OK, OK } },
+ { "tst", { ERR, OK, ERR, OK } },
+ { "data", { OK, OK, OK, OK } },
+ { "cors", { ERR, OK, ERR, OK } },
+ { "local-localaccess", { OK, OK, ERR, ERR } },
+ { "local-cors", { OK, OK, ERR, ERR } },
+ } },
+
+ { "local-cors", { // +local +cors -local-access
+ { "file", { OK, OK, ERR, ERR } },
+ { "qrc", { OK, OK, OK, OK } },
+ { "tst", { ERR, OK, ERR, OK } },
+ { "data", { OK, OK, OK, OK } },
+ { "cors", { ERR, OK, ERR, OK } },
+ { "local-localaccess", { OK, OK, ERR, ERR } },
+ { "local-cors", { SLF, SLF, ERR, ERR } },
+ } },
+
+ { "local-localaccess", { // +local -cors + OK-access
+ { "file", { OK, OK, OK, OK } },
+ { "qrc", { OK, OK, OK, OK } },
+ { "tst", { ERR, OK, ERR, OK } },
+ { "data", { OK, OK, OK, OK } },
+ { "cors", { ERR, OK, ERR, OK } },
+ { "local-localaccess", { SLF, SLF, OK, OK } }, // ### should probably be: SLF, SLF, SLF, SLF
+ { "local-cors", { OK, OK, OK, OK } },
+ } },
+
+ { "localaccess", { // -local -cors +local-access
+ { "file", { OK, OK, OK, OK } },
+ { "qrc", { OK, OK, OK, OK } },
+ { "tst", { OK, OK, OK, OK } },
+ { "data", { OK, OK, OK, OK } },
+ { "cors", { OK, OK, OK, OK } },
+ { "local-localaccess", { OK, OK, OK, OK } },
+ { "local-cors", { OK, OK, OK, OK } }, } },
+ };
+
+ for (auto &&d : data) {
+ auto schemeFrom = d.first;
+
+ for (int i = 0; i < 4; ++i) {
+ const auto &it = settingCombinations[i];
+ bool canAccessFileUrls = it.first, canAccessRemoteUrl = it.second;
+
+ QVariantMap testPairs;
+ for (auto &&destSchemes : d.second) {
+ auto &&destScheme = destSchemes.first;
+ auto &&expectedResults = destSchemes.second;
+ testPairs[destScheme] = expectedResults[i];
+ }
+
+ QTest::addRow("%s_%s_%s", schemeFrom, (canAccessFileUrls ? "local" : "nolocal"), (canAccessRemoteUrl ? "remote" : "noremote"))
+ << schemeFrom << canAccessFileUrls << canAccessRemoteUrl << testPairs;
+ }
+ }
+}
+
+void tst_Origins::mixedContent()
+{
+ QFETCH(QString, schemeFrom);
+ QFETCH(bool, canAccessFileUrls);
+ QFETCH(bool, canAccessRemoteUrl);
+ QFETCH(QVariantMap, testPairs);
+
+ QString srcDir(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath());
+ auto loadUrl = QString("%1:%2/resources/mixedSchemes.html").arg(schemeFrom).arg(schemeFrom == "file" ? srcDir : "");
+
+ QCOMPARE(testPairs.size(), 7);
+ ScopedAttribute sa2(m_page->settings(), QWebEngineSettings::ErrorPageEnabled, false);
+ ScopedAttribute sa0(m_page->settings(), QWebEngineSettings::LocalContentCanAccessFileUrls, canAccessFileUrls);
+ ScopedAttribute sa1(m_page->settings(), QWebEngineSettings::LocalContentCanAccessRemoteUrls, canAccessRemoteUrl);
+ QVERIFY(verifyLoad(loadUrl));
+
+ auto setIFrameUrl = [&] (const QString &scheme) {
+ if (scheme == "data")
+ return QString("setIFrameUrl('data:,<script>var canary = true; parent.canary = true</script>')");
+ auto frameUrl = QString("%1:%2/resources/mixedSchemes_frame.html").arg(scheme).arg(scheme == "file" ? srcDir : "");
+ return QString("setIFrameUrl('%1')").arg(frameUrl);
+ };
+
+ m_page->messages.clear();
+ QStringList schemesTo, expected, results;
+ for (auto it = testPairs.begin(), end = testPairs.end(); it != end; ++it) {
+
+ auto schemeTo = it.key();
+ auto expectedResult = it.value().toString();
+
+ eval(setIFrameUrl(schemeTo));
+
+ QTRY_COMPARE(eval(QSL("result !== undefined")), QVariant(true));
+ auto result = eval(QSL("result")).toString();
+ // Work-around some combinations missing JS loaded signals:
+ if (m_page->messages.count() > 0) {
+ if (m_page->messages[0] == QSL("Frame Loaded") && result == QSL("cannotLoad"))
+ result = QSL("canLoadButNotAccess");
+ m_page->messages.clear();
+ }
+
+ schemesTo.append(schemeTo.rightJustified(20));
+ results.append(result.rightJustified(20));
+ expected.append(expectedResult.rightJustified(20));
+ }
+ QVERIFY2(results == expected,
+ qPrintable(QString("\nFrom '%1' to:\n\tScheme: %2\n\tActual: %3\n\tExpect: %4")
+ .arg(schemeFrom).arg(schemesTo.join(' ')).arg(results.join(' ')).arg(expected.join(' '))));
+}
+
#if defined(WEBSOCKETS)
class EchoServer : public QObject {
Q_OBJECT
diff --git a/tests/auto/httpserver/httpserver.cpp b/tests/auto/httpserver/httpserver.cpp
index 10147ae6c..ce35ae1f0 100644
--- a/tests/auto/httpserver/httpserver.cpp
+++ b/tests/auto/httpserver/httpserver.cpp
@@ -110,6 +110,7 @@ void HttpServer::handleNewConnection()
if (f.open(QFile::ReadOnly)) {
QMimeType mime = QMimeDatabase().mimeTypeForFileNameAndData(f.fileName(), &f);
rr->setResponseHeader(QByteArrayLiteral("Content-Type"), mime.name().toUtf8());
+ rr->setResponseHeader(QByteArrayLiteral("Access-Control-Allow-Origin"), QByteArrayLiteral("*"));
rr->setResponseBody(f.readAll());
rr->sendResponse();
} else {