diff options
| author | Szabolcs David <davidsz@inf.u-szeged.hu> | 2025-10-20 15:21:31 +0200 |
|---|---|---|
| committer | Szabolcs David <davidsz@inf.u-szeged.hu> | 2025-11-06 15:08:44 +0100 |
| commit | 48ba78f27354a0252d09410203ce4191d6129d57 (patch) | |
| tree | e52b59d9f410c60a14085ff169fba2ad22f4a50f | |
| parent | 2ebb596b43346c5bf93fc9a460e241a812313c0e (diff) | |
Allow redirecting fetch() to cross origin resources
We use CorsURLLoader since the 124-based Chromium. This performs a
safety check on calling CorsURLLoader::OnReceiveRedirect(), since
fetch() requests have more strict rules when accessing cross origin
resources.
This patch extends our dummy response with the Allow-Origin header
(only in case of redirects), so the request interceptor API users
won't hit this wall.
Test case implemented by: Benjamin Terrier
Task-number: QTBUG-140889
Change-Id: I7025fb69b5ac9b8763d61cb2bf0880dcc20721cf
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
5 files changed, 70 insertions, 0 deletions
diff --git a/src/core/net/proxying_url_loader_factory_qt.cpp b/src/core/net/proxying_url_loader_factory_qt.cpp index 08bcbc394..f9e3498ae 100644 --- a/src/core/net/proxying_url_loader_factory_qt.cpp +++ b/src/core/net/proxying_url_loader_factory_qt.cpp @@ -403,6 +403,18 @@ void InterceptedRequest::ContinueAfterIntercept() if (!current_response_) current_response_ = createResponse(request_); current_response_->encoded_data_length = 0; + + if (!current_response_->headers) { + current_response_->headers = base::MakeRefCounted<net::HttpResponseHeaders>(""); + // Assuming that the users of request interceptor API are aware of the security + // risks + current_response_->headers->AddHeader( + network::cors::header_names::kAccessControlAllowOrigin, + url::Origin::Create(request_.url).Serialize()); + current_response_->headers->AddHeader( + network::cors::header_names::kAccessControlAllowCredentials, "true"); + } + target_client_->OnReceiveRedirect(redirectInfo, std::move(current_response_)); return; } diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/CMakeLists.txt b/tests/auto/core/qwebengineurlrequestinterceptor/CMakeLists.txt index 76db8b7e2..3219c44b9 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/CMakeLists.txt +++ b/tests/auto/core/qwebengineurlrequestinterceptor/CMakeLists.txt @@ -19,6 +19,8 @@ set(tst_qwebengineurlrequestinterceptor_resource_files "resources/content.html" "resources/content2.html" "resources/content3.html" + "resources/cors.html" + "resources/cors.js" "resources/favicon.html" "resources/firstparty.html" "resources/fontawesome.woff" diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/resources/cors.html b/tests/auto/core/qwebengineurlrequestinterceptor/resources/cors.html new file mode 100644 index 000000000..b0fb2cfd9 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestinterceptor/resources/cors.html @@ -0,0 +1,8 @@ +<html> + <head> + <link rel="icon" href="data:," /> + </head> + <script src="http://www.qt.io/cors.js"></script> + > + <body></body> +</html> diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/resources/cors.js b/tests/auto/core/qwebengineurlrequestinterceptor/resources/cors.js new file mode 100644 index 000000000..edd338415 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestinterceptor/resources/cors.js @@ -0,0 +1,17 @@ +async function getData() +{ + const url = "https://qt.io/content.html"; + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Response status: ${response.status}`); + } + + const result = await response.json(); + console.log(result); + } catch (error) { + console.error(error.message); + } +} + +getData(); diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp index ad9f91a93..81d654066 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp +++ b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp @@ -54,6 +54,7 @@ private Q_SLOTS: void profilePreventsPageInterception_data(); void profilePreventsPageInterception(); void download(); + void redirect(); }; tst_QWebEngineUrlRequestInterceptor::tst_QWebEngineUrlRequestInterceptor() @@ -1143,5 +1144,35 @@ void tst_QWebEngineUrlRequestInterceptor::download() QCOMPARE(interceptor.requestInfos.at(0).download, true); } +void tst_QWebEngineUrlRequestInterceptor::redirect() +{ + HttpServer server; + server.setResourceDirs({ ":/resources" }); + QVERIFY(server.start()); + + TestRequestInterceptor interceptor; + interceptor.onIntercept = [&](QWebEngineUrlRequestInfo &info) { + const auto url = server.url(info.requestUrl().path()); + if (info.requestUrl() != url) { + qDebug() << "FROM" << info.requestUrl() << "TO" << url; + info.redirect(url); + return false; + } + return true; + }; + + QWebEnginePage page; + page.setUrlRequestInterceptor(&interceptor); + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + + const auto url = QUrl(server.url("/cors.html")); + page.load(url); + QTRY_COMPARE(loadSpy.size(), 1); + // We expect 3 requests: cors.html, cors.js and content.html + // redirected requests are not counted + // We will only get 2 because the request to fetch content.html will never happen + QCOMPARE(interceptor.requestInfos.size(), 3); +} + QTEST_MAIN(tst_QWebEngineUrlRequestInterceptor) #include "tst_qwebengineurlrequestinterceptor.moc" |
