aboutsummaryrefslogtreecommitdiffstats
path: root/src/requestinterceptor.cpp
blob: 0ce95ca8c011ed2ab7217193132705ccbb2c7817 (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
#include "requestinterceptor.h"

#include <QWebEngineUrlRequestInfo>

RequestInterceptor::RequestInterceptor(QObject *parent)
    : QWebEngineUrlRequestInterceptor(parent)
{
}

bool RequestInterceptor::shouldAllow(const QUrl &url)
{
    // QUrl::scheme() always normalizes to lowercase (verified: QUrl("HTTP://x/y")
    // .scheme() == "http"), so a lowercase-literal compare cannot be bypassed
    // by unusual casing, in either the allow or the deny direction.
    const QString scheme = url.scheme();

    // The document itself is loaded via setHtml() with a qtmaildir: base URL,
    // so that scheme must pass or nothing renders at all. This is unconditional
    // on any path/host because Task 11's scheme handler is the only thing that
    // can ever originate a qtmaildir: navigation in the first place; the message
    // body cannot cause a request with this scheme, only reference cid:/http(s):.
    if (scheme == QLatin1String("qtmaildir"))
        return true;

    // Inline parts of the current message only.
    if (scheme == QLatin1String("cid")) {
        // QUrl keeps a cid: body in path(), not host() or userName(), even
        // when it contains '@' (verified empirically: QUrl("cid:logo@example.org")
        // .path() == "logo@example.org", host() and userName() are empty).
        // path() also returns the percent-decoded form, so a percent-encoded
        // id (e.g. "%6Cogo@example.org") compares equal to its decoded form,
        // not to some other allowed id: it cannot be used to smuggle a
        // foreign id past the allowlist, only to spell an already-legitimate
        // id differently.
        const QString id = url.path();
        if (m_allowedCids.contains(id))
            return true;
        m_blockedAnything = true;
        return false;
    }

    if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) {
        if (m_allowRemote)
            return true;
        m_blockedAnything = true;
        return false;
    }

    // Everything else, including file:, javascript:, data:, blob:, about:,
    // chrome:, qrc:, filesystem:, protocol-relative URLs (empty scheme with a
    // host), and empty/malformed URLs (empty scheme), is denied
    // unconditionally. There is no flag that enables it.
    m_blockedAnything = true;
    return false;
}

void RequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info)
{
    if (!shouldAllow(info.requestUrl()))
        info.block(true);
}

void RequestInterceptor::resetForNewMessage()
{
    m_allowRemote = false;
    m_blockedAnything = false;
}