MailJDTourDownloadDocsQualityTestsCoverage

Which source lines and branches the tests execute. Lines 91.5 %, branches 75.0 % (minimum 85 % and 72 %). main @ 399ffe3d · 21 October 2026 · pipeline 7238.

GCC Code Coverage Report


Directory: src/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 42 / 0 / 42
Functions: 100.0% 7 / 0 / 7
Branches: 82.1% 87 / 0 / 106

ui/ExternalContentPolicy.cpp
Line Branch Exec Source
1 #include "ExternalContentPolicy.h"
2
3 #include "UrlSchemeFilter.h"
4 #include <QCoreApplication>
5 #include <QSettings>
6
7 41 bool ExternalContentPolicy::canOpenExternalLink(const QUrl &url) {
8
8/8
✓ Branch 2 → 3 taken 41 times.
✓ Branch 3 → 4 taken 38 times.
✓ Branch 3 → 6 taken 3 times.
✓ Branch 4 → 5 taken 38 times.
✓ Branch 5 → 6 taken 3 times.
✓ Branch 5 → 7 taken 35 times.
✓ Branch 8 → 9 taken 6 times.
✓ Branch 8 → 10 taken 35 times.
41 if (!url.isValid() || url.isRelative()) return false;
9
2/2
✓ Branch 10 → 11 taken 35 times.
✓ Branch 11 → 12 taken 35 times.
35 const QString scheme = url.scheme().toLower();
10
3/3
✓ Branch 13 → 14 taken 35 times.
✓ Branch 14 → 15 taken 10 times.
✓ Branch 14 → 16 taken 25 times.
35 if (!isAllowedExternalScheme(scheme)) return false;
11 // Audit the fully encoded form AND its percent-decoded twin: encodings are
12 // the standard carrier for control characters, and partially decoded
13 // serializations truncate instead of exposing what an opener would receive.
14
1/1
✓ Branch 17 → 18 taken 25 times.
25 const QString encoded = url.toString(QUrl::None);
15
3/3
✓ Branch 18 → 19 taken 25 times.
✓ Branch 19 → 20 taken 25 times.
✓ Branch 20 → 21 taken 25 times.
25 const QString audit = encoded + QUrl::fromPercentEncoding(encoded.toUtf8());
16
2/2
✓ Branch 31 → 25 taken 1258 times.
✓ Branch 31 → 32 taken 21 times.
1279 for (const QChar &character : audit) {
17 1258 const char16_t code = character.unicode();
18 // Control characters and backslashes cannot be part of a link a human
19 // meant: they are carrier signals for scheme confusion and opener
20 // argument injection, not address text.
21
5/6
✓ Branch 26 → 27 taken 1256 times.
✓ Branch 26 → 29 taken 2 times.
✓ Branch 27 → 28 taken 1256 times.
✗ Branch 27 → 29 not taken.
✓ Branch 28 → 29 taken 2 times.
✓ Branch 28 → 30 taken 1254 times.
1258 if (code < 0x20 || code == 0x7F || code == u'\\') return false;
22 }
23 // RFC 6068: a mailto URI has no authority. "mailto://user@host" would hand
24 // the opener a user name and host that are not the recipient.
25
2/2
✓ Branch 34 → 35 taken 6 times.
✓ Branch 34 → 54 taken 15 times.
21 if (scheme == QLatin1String("mailto"))
26
12/19
✓ Branch 36 → 37 taken 6 times.
✓ Branch 38 → 39 taken 4 times.
✓ Branch 38 → 45 taken 2 times.
✓ Branch 40 → 41 taken 4 times.
✓ Branch 41 → 42 taken 4 times.
✓ Branch 43 → 44 taken 3 times.
✓ Branch 43 → 45 taken 1 time.
✓ Branch 47 → 48 taken 4 times.
✓ Branch 47 → 49 taken 2 times.
✓ Branch 49 → 50 taken 4 times.
✓ Branch 49 → 51 taken 2 times.
✓ Branch 51 → 52 taken 6 times.
✗ Branch 51 → 53 not taken.
✗ Branch 102 → 103 not taken.
✗ Branch 102 → 104 not taken.
✗ Branch 106 → 107 not taken.
✗ Branch 106 → 108 not taken.
✗ Branch 111 → 112 not taken.
✗ Branch 111 → 113 not taken.
10 return url.authority().isEmpty() && !url.path().trimmed().isEmpty();
27 // An authority with embedded credentials makes the displayed host differ
28 // from the connected host; mail links have no legitimate use for them.
29
3/3
✓ Branch 55 → 56 taken 15 times.
✓ Branch 58 → 59 taken 3 times.
✓ Branch 58 → 60 taken 12 times.
15 if (!url.userInfo().isEmpty()) return false;
30 // A host that does not survive its own serialization — an IPv6 zone ID
31 // ("[::%25…]") with arbitrary bytes — reaches the opener as another host
32 // than the one shown; links never need a zone ID (fuzz_external_link).
33
1/1
✓ Branch 61 → 62 taken 12 times.
12 const QString host = url.host();
34
3/3
✓ Branch 64 → 65 taken 12 times.
✓ Branch 65 → 66 taken 11 times.
✓ Branch 65 → 74 taken 1 time.
23 if (host.contains(QLatin1Char('%')) ||
35
12/19
✓ Branch 67 → 68 taken 11 times.
✓ Branch 69 → 70 taken 11 times.
✓ Branch 71 → 72 taken 11 times.
✗ Branch 73 → 74 not taken.
✓ Branch 73 → 75 taken 11 times.
✓ Branch 76 → 77 taken 11 times.
✓ Branch 76 → 78 taken 1 time.
✓ Branch 78 → 79 taken 11 times.
✓ Branch 78 → 80 taken 1 time.
✓ Branch 80 → 81 taken 11 times.
✓ Branch 80 → 82 taken 1 time.
✓ Branch 82 → 83 taken 1 time.
✓ Branch 82 → 84 taken 11 times.
✗ Branch 119 → 120 not taken.
✗ Branch 119 → 121 not taken.
✗ Branch 124 → 125 not taken.
✗ Branch 124 → 126 not taken.
✗ Branch 129 → 130 not taken.
✗ Branch 129 → 131 not taken.
23 QUrl::fromEncoded(url.toEncoded(), QUrl::StrictMode).host() != host)
36 1 return false;
37 11 return !host.isEmpty();
38 35 }
39
40 22 ExternalContentPolicy::ExternalContentPolicy(QObject *parent) : QObject(parent) {}
41
42 1650 ExternalContentPolicy *ExternalContentPolicy::instance() {
43
3/4
✓ Branch 2 → 3 taken 22 times.
✓ Branch 2 → 7 taken 1628 times.
✓ Branch 4 → 5 taken 22 times.
✗ Branch 4 → 7 not taken.
1650 static QPointer<ExternalContentPolicy> policy;
44
5/8
✓ Branch 8 → 9 taken 22 times.
✓ Branch 8 → 16 taken 1628 times.
✓ Branch 11 → 12 taken 22 times.
✓ Branch 12 → 13 taken 22 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 22 times.
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 20 not taken.
1650 if (!policy) policy = new ExternalContentPolicy(QCoreApplication::instance());
45 1650 return policy;
46 }
47
48 55 bool ExternalContentPolicy::shouldBlock() const {
49
2/2
✓ Branch 5 → 6 taken 55 times.
✓ Branch 14 → 15 taken 55 times.
220 return QSettings().value(QStringLiteral("view/externalContent"),
50 110 QStringLiteral("block"))
51
1/1
✓ Branch 15 → 16 taken 55 times.
165 .toString() != QStringLiteral("load");
52 }
53
54 48 QString ExternalContentPolicy::mode() const {
55
6/6
✓ Branch 3 → 4 taken 33 times.
✓ Branch 3 → 7 taken 15 times.
✓ Branch 11 → 12 taken 15 times.
✓ Branch 11 → 13 taken 33 times.
✓ Branch 13 → 14 taken 33 times.
✓ Branch 13 → 15 taken 15 times.
96 return shouldBlock() ? QStringLiteral("block") : QStringLiteral("load");
56 }
57
58 11 bool ExternalContentPolicy::setMode(const QString &mode) {
59
6/6
✓ Branch 4 → 5 taken 8 times.
✓ Branch 4 → 9 taken 3 times.
✓ Branch 7 → 8 taken 4 times.
✓ Branch 7 → 9 taken 4 times.
✓ Branch 10 → 11 taken 4 times.
✓ Branch 10 → 12 taken 7 times.
11 if (mode != QLatin1String("block") && mode != QLatin1String("load"))
60 4 return false;
61
1/1
✓ Branch 12 → 13 taken 7 times.
7 QSettings settings;
62
1/1
✓ Branch 21 → 22 taken 7 times.
21 if (settings.value(QStringLiteral("view/externalContent"),
63 14 QStringLiteral("block"))
64
3/3
✓ Branch 22 → 23 taken 7 times.
✓ Branch 31 → 32 taken 1 time.
✓ Branch 31 → 33 taken 6 times.
14 .toString() == mode)
65 1 return true;
66
1/1
✓ Branch 38 → 39 taken 6 times.
12 settings.setValue(QStringLiteral("view/externalContent"), mode);
67
1/1
✓ Branch 42 → 43 taken 6 times.
6 emit changed();
68 6 return true;
69 7 }
70
71 2 void ExternalContentPolicy::notifyChanged() { emit changed(); }
72