summaryrefslogtreecommitdiffstats
path: root/tests/test_messageview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_messageview.cpp')
-rw-r--r--tests/test_messageview.cpp74
1 files changed, 74 insertions, 0 deletions
diff --git a/tests/test_messageview.cpp b/tests/test_messageview.cpp
index 57d7b42..e94fb86 100644
--- a/tests/test_messageview.cpp
+++ b/tests/test_messageview.cpp
@@ -47,6 +47,8 @@ private slots:
void headerEscapesUntrustedValues();
void headerOmitsAnAbsentCc();
void detailsDialogIsOfferedForEveryThread();
+ void placeholderRendersAndReportsItself();
+ void aMessageBodyCannotRunAQuery();
private:
QWebEngineView *webViewOf(MessageView *view) const
@@ -492,5 +494,77 @@ void TestMessageView::detailsDialogIsOfferedForEveryThread()
QVERIFY(!button || !button->isVisible());
}
+void TestMessageView::placeholderRendersAndReportsItself()
+{
+ MessageView view;
+ QWebEngineView *web = webViewOf(&view);
+ QVERIFY(web);
+
+ QSignalSpy loaded(web, &QWebEngineView::loadFinished);
+ view.showPlaceholder({ { QStringLiteral("7 unread"),
+ QStringLiteral("tag:unread") } });
+
+ QVERIFY2(loaded.wait(15000), "the placeholder document never loaded");
+ QVERIFY2(loaded.last().at(0).toBool(),
+ "loadFinished reported failure: the navigation was rejected, "
+ "which is what a base-URL mismatch looks like");
+
+ // Rendered, not merely loaded. The wordmark is split across elements by
+ // the accent span, so the helper line is what proves the content arrived.
+ QString text;
+ bool done = false;
+ web->page()->toPlainText([&](const QString &result) {
+ text = result;
+ done = true;
+ });
+ QTRY_VERIFY_WITH_TIMEOUT(done, 15000);
+ QVERIFY2(text.contains(QStringLiteral("7 unread")), qPrintable(text));
+
+ QVERIFY(view.showingPlaceholder());
+}
+
+void TestMessageView::aMessageBodyCannotRunAQuery()
+{
+ // The gate behind queryRequested(). A message body is attacker-controlled
+ // HTML and can carry a qtmaildir-query: link; the view only honours one
+ // while the placeholder is what is displayed, so this asserts the state
+ // that decides it rather than synthesising a click, which would need the
+ // page's protected navigation handler.
+ MessageView view;
+ QSignalSpy queries(&view, &MessageView::queryRequested);
+
+ view.showPlaceholder({ { QStringLiteral("7 unread"),
+ QStringLiteral("tag:unread") } });
+ QVERIFY(view.showingPlaceholder());
+
+ ParsedMessage message;
+ message.ok = true;
+ message.from = QStringLiteral("Mallory <mallory@example.org>");
+ message.subject = QStringLiteral("Click me");
+ message.htmlBody = QStringLiteral(
+ "<a href=\"qtmaildir-query:tag%3Adeleted\">a link</a>");
+
+ ThreadRenderItem item;
+ item.message = message;
+ item.cidPrefix = QStringLiteral("m0");
+ item.expanded = true;
+
+ view.showThread({ item });
+
+ // Showing any message closes the gate, so a link in that message's own
+ // body has nothing to reach.
+ QVERIFY2(!view.showingPlaceholder(),
+ "the gate stayed open while a message was displayed: a link in a "
+ "message body could run a query");
+
+ view.clear();
+ QVERIFY(!view.showingPlaceholder());
+
+ view.showError(QStringLiteral("broken"), QStringLiteral("/tmp/x"));
+ QVERIFY(!view.showingPlaceholder());
+
+ QVERIFY(queries.isEmpty());
+}
+
QTEST_MAIN(TestMessageView)
#include "test_messageview.moc"