summaryrefslogtreecommitdiffstats
path: root/tests/auto/core/certificateerror/tst_certificateerror.cpp
blob: 67e2d8ae4ad6e3ef116f51d126cee9a17f4df03e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <httpsserver.h>
#include <util.h>

#include <QWebEngineCertificateError>
#include <QWebEnginePage>
#include <QWebEngineSettings>

#include <QtTest/QtTest>

class tst_CertificateError : public QObject
{
    Q_OBJECT
public:
    tst_CertificateError() { }

private Q_SLOTS:
    void handleError_data();
    void handleError();
    void fatalError();
};

struct PageWithCertificateErrorHandler : QWebEnginePage
{
    Q_OBJECT

public:
    PageWithCertificateErrorHandler(bool defer, bool accept, QObject *p = nullptr)
        : QWebEnginePage(p), deferError(defer), acceptCertificate(accept)
        , loadSpy(this, &QWebEnginePage::loadFinished)
    {
        connect(this, &PageWithCertificateErrorHandler::certificateError,
                this, &PageWithCertificateErrorHandler::onCertificateError);
    }

    bool deferError, acceptCertificate;

    QSignalSpy loadSpy;
    QScopedPointer<QWebEngineCertificateError> error;

public Q_SLOTS:
    void onCertificateError(QWebEngineCertificateError e)
    {
        error.reset(new QWebEngineCertificateError(e));
        if (deferError) {
            error->defer();
            return;
        }

        if (acceptCertificate)
            error->acceptCertificate();
        else
            error->rejectCertificate();
    }
};

void tst_CertificateError::handleError_data()
{
    QTest::addColumn<bool>("deferError");
    QTest::addColumn<bool>("acceptCertificate");
    QTest::addColumn<QString>("expectedContent");
    QTest::addRow("Reject") << false << false << QString();
    QTest::addRow("DeferReject") << true << false << QString();
    QTest::addRow("DeferAccept") << true << true << "TEST";
}

void tst_CertificateError::handleError()
{
    HttpsServer server(":/resources/server.pem", ":/resources/server.key", "");
    server.setExpectError(false);
    QVERIFY(server.start());

    connect(&server, &HttpsServer::newRequest, [&] (HttpReqRep *rr) {
        rr->setResponseBody(QByteArrayLiteral("<html><body>TEST</body></html>"));
        rr->sendResponse();
    });

    QFETCH(bool, deferError);
    QFETCH(bool, acceptCertificate);
    QFETCH(QString, expectedContent);

    PageWithCertificateErrorHandler page(deferError, acceptCertificate);
    page.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false);

    page.setUrl(server.url());
    QTRY_VERIFY(page.error);
    QVERIFY(page.error->isOverridable());
    auto chain = page.error->certificateChain();
    QCOMPARE(chain.size(), 2);
    QCOMPARE(chain[0].serialNumber(), "15:91:08:23:37:91:ee:51:00:d7:4a:db:d7:8c:3b:31:f8:4f:f3:b3");
    QCOMPARE(chain[1].serialNumber(), "3c:16:83:83:59:c4:2a:65:8f:7a:b2:07:10:14:4e:2d:70:9a:3e:23");

    if (deferError) {
        QCOMPARE(page.loadSpy.size(), 0);
        QCOMPARE(toPlainTextSync(&page), QString());

        if (acceptCertificate)
            page.error->acceptCertificate();
        else
            page.error->rejectCertificate();

        page.error.reset();
    }
    QTRY_COMPARE_WITH_TIMEOUT(page.loadSpy.size(), 1, 30000);
    QCOMPARE(page.loadSpy.takeFirst().value(0).toBool(), acceptCertificate);
    QCOMPARE(toPlainTextSync(&page), expectedContent);
    QVERIFY(server.stop());
}

void tst_CertificateError::fatalError()
{
    PageWithCertificateErrorHandler page(false, false);
    page.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false);
    QSignalSpy loadFinishedSpy(&page, &QWebEnginePage::loadFinished);

    page.setUrl(QUrl("https://revoked.badssl.com"));
    if (!loadFinishedSpy.wait(10000)) {
        QVERIFY2(!page.error, "There shouldn't be any certificate error if not loaded due to missing internet access!");
        QSKIP("Couldn't load page from network, skipping test.");
    }

    // revoked certificate might not be reported as invalid by chromium and the load will silently succeed
    bool failed = !loadFinishedSpy.first().first().toBool(), hasError = bool(page.error);
    QCOMPARE(failed, hasError);
    if (hasError) {
        QVERIFY(!page.error->isOverridable());
        // Fatal certificate errors are implicitly rejected. But second call should not cause crash.
        page.error->rejectCertificate();
    }
}

QTEST_MAIN(tst_CertificateError)
#include <tst_certificateerror.moc>