From e02bcb0855ebee0612cab0f3cd3f9fd494497336 Mon Sep 17 00:00:00 2001 From: Tamas Zakor Date: Fri, 3 May 2019 15:53:25 +0200 Subject: Add path validation for QWebEngineDownloadItem::setPath() Do not set path if it ends with separator or if it matches with an already existing directory name. Task-number: QTBUG-75566 Change-Id: I4b78b28afe034c7589633c569a4945a36b32008e Reviewed-by: Allan Sandfeld Jensen --- src/webengine/api/qquickwebenginedownloaditem.cpp | 12 ++ .../api/qwebenginedownloaditem.cpp | 11 ++ .../tst_qwebenginedownloaditem.cpp | 123 +++++++++++++++++++++ 3 files changed, 146 insertions(+) diff --git a/src/webengine/api/qquickwebenginedownloaditem.cpp b/src/webengine/api/qquickwebenginedownloaditem.cpp index cdb95fa53..7d51ed21d 100644 --- a/src/webengine/api/qquickwebenginedownloaditem.cpp +++ b/src/webengine/api/qquickwebenginedownloaditem.cpp @@ -43,6 +43,8 @@ #include "profile_adapter.h" #include "qquickwebengineprofile_p.h" +#include "QFileInfo" + using QtWebEngineCore::ProfileAdapterClient; QT_BEGIN_NAMESPACE @@ -427,6 +429,16 @@ void QQuickWebEngineDownloadItem::setPath(QString path) return; } if (d->downloadPath != path) { + if (QFileInfo(path).fileName().isEmpty()) { + qWarning("The download path does not include file name."); + return; + } + + if (QFileInfo(path).isDir()) { + qWarning("The download path matches with an already existing directory path."); + return; + } + d->downloadPath = path; Q_EMIT pathChanged(); } diff --git a/src/webenginewidgets/api/qwebenginedownloaditem.cpp b/src/webenginewidgets/api/qwebenginedownloaditem.cpp index 4575f2929..05c6956ea 100644 --- a/src/webenginewidgets/api/qwebenginedownloaditem.cpp +++ b/src/webenginewidgets/api/qwebenginedownloaditem.cpp @@ -43,6 +43,7 @@ #include "profile_adapter.h" #include "qwebengineprofile_p.h" +#include "QFileInfo" QT_BEGIN_NAMESPACE @@ -534,6 +535,16 @@ void QWebEngineDownloadItem::setPath(QString path) return; } + if (QFileInfo(path).fileName().isEmpty()) { + qWarning("The download path does not include file name."); + return; + } + + if (QFileInfo(path).isDir()) { + qWarning("The download path matches with an already existing directory path."); + return; + } + d->downloadPath = path; } diff --git a/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp b/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp index b30fc7258..9732de85c 100644 --- a/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp +++ b/tests/auto/widgets/qwebenginedownloaditem/tst_qwebenginedownloaditem.cpp @@ -70,6 +70,7 @@ private Q_SLOTS: void downloadFileNot2(); void downloadDeleted(); void downloadDeletedByProfile(); + void downloadPathValidation(); private: void saveLink(QPoint linkPos); @@ -844,5 +845,127 @@ void tst_QWebEngineDownloadItem::downloadDeletedByProfile() QTRY_COMPARE(downloadItem.isNull(), true); } +void tst_QWebEngineDownloadItem::downloadPathValidation() +{ + const QString fileName = "test.txt"; + QString downloadPath; + QString originalDownloadPath; + + QTemporaryDir tmpDir; + QVERIFY(tmpDir.isValid()); + + // Set up HTTP server + ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) { + if (rr->requestMethod() == "GET" && rr->requestPath() == ("/" + fileName)) { + rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("application/octet-stream")); + rr->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment")); + rr->setResponseBody(QByteArrayLiteral("a")); + rr->sendResponse(); + } else { + rr->setResponseStatus(404); + rr->sendResponse(); + } + }); + + // Set up profile and download handler + QPointer downloadItem; + ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) { + downloadItem = item; + originalDownloadPath = item->path(); + + item->setPath(downloadPath); + // TODO: Do not cancel download from 5.13. This is for not messing up system download path. + // Use m_profile->setDownloadPath(tmpDir.path()) at initialization. + if (item->path() != downloadPath) + item->cancel(); + else + item->accept(); + + connect(item, &QWebEngineDownloadItem::stateChanged, [&, item](QWebEngineDownloadItem::DownloadState downloadState) { + if (downloadState == QWebEngineDownloadItem::DownloadInterrupted) { + item->cancel(); + } + }); + + connect(item, &QWebEngineDownloadItem::finished, [&, item]() { + QCOMPARE(item->isFinished(), true); + QCOMPARE(item->totalBytes(), item->receivedBytes()); + QVERIFY(item->receivedBytes() > 0); + QCOMPARE(item->page(), m_page); + }); + }); + + QString oldPath = QDir::currentPath(); + QDir::setCurrent(tmpDir.path()); + + // Set only the file name. + downloadItem.clear(); + originalDownloadPath = ""; + downloadPath = fileName; + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadItem); + QTRY_COMPARE(downloadItem->state(), QWebEngineDownloadItem::DownloadCompleted); + QCOMPARE(downloadItem->interruptReason(), QWebEngineDownloadItem::NoReason); + QCOMPARE(downloadItem->path(), fileName); + + // Set only the directory path. + downloadItem.clear(); + originalDownloadPath = ""; + downloadPath = tmpDir.path(); + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadItem); + QTRY_COMPARE(downloadItem->state(), QWebEngineDownloadItem::DownloadCancelled); + QCOMPARE(downloadItem->interruptReason(), QWebEngineDownloadItem::UserCanceled); + QCOMPARE(downloadItem->path(), originalDownloadPath); + + // Set only the directory path with separator. + downloadItem.clear(); + originalDownloadPath = ""; + downloadPath = tmpDir.path() + QDir::separator(); + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadItem); + QTRY_COMPARE(downloadItem->state(), QWebEngineDownloadItem::DownloadCancelled); + QCOMPARE(downloadItem->interruptReason(), QWebEngineDownloadItem::UserCanceled); + QCOMPARE(downloadItem->path(), originalDownloadPath); + + // Set only the directory with the current directory path without ending separator. + downloadItem.clear(); + originalDownloadPath = ""; + downloadPath = "."; + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadItem); + QTRY_COMPARE(downloadItem->state(), QWebEngineDownloadItem::DownloadCancelled); + QCOMPARE(downloadItem->interruptReason(), QWebEngineDownloadItem::UserCanceled); + QCOMPARE(downloadItem->path(), originalDownloadPath); + + // Set only the directory with the current directory path with ending separator. + downloadItem.clear(); + originalDownloadPath = ""; + downloadPath = "./"; + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadItem); + QTRY_COMPARE(downloadItem->state(), QWebEngineDownloadItem::DownloadCancelled); + QCOMPARE(downloadItem->interruptReason(), QWebEngineDownloadItem::UserCanceled); + QCOMPARE(downloadItem->path(), originalDownloadPath); + + + + downloadItem.clear(); + originalDownloadPath = ""; + downloadPath = "..."; + m_page->setUrl(m_server->url("/" + fileName)); + QTRY_VERIFY(downloadItem); + QTRY_COMPARE(downloadItem->state(), QWebEngineDownloadItem::DownloadCancelled); +#if !defined(Q_OS_WIN) + QCOMPARE(downloadItem->interruptReason(), QWebEngineDownloadItem::FileFailed); + QCOMPARE(downloadItem->path(), downloadPath); +#else + // Windows interprets the "..." path as a valid path. It will be the current path. + QCOMPARE(downloadItem->interruptReason(), QWebEngineDownloadItem::UserCanceled); + QCOMPARE(downloadItem->path(), originalDownloadPath); +#endif // !defined(Q_OS_WIN) + QDir::setCurrent(oldPath); +} + QTEST_MAIN(tst_QWebEngineDownloadItem) #include "tst_qwebenginedownloaditem.moc" -- cgit v1.2.3