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% 36 / 0 / 36
Functions: 100.0% 2 / 0 / 2
Branches: 83.3% 95 / 0 / 114

util/AddressClass.h
Line Branch Exec Source
1 #pragma once
2 #include "HostMatch.h"
3 #include <QHostAddress>
4
5 // Local URL admission only. A public DNS name may still resolve privately;
6 // Chromium performs its own DNS resolution, so this is not a rebinding guard.
7 namespace AddressClass {
8 35 inline bool isPublicAddress(const QHostAddress &address) {
9
10/11
✓ Branch 2 → 3 taken 35 times.
✓ Branch 3 → 4 taken 35 times.
✗ Branch 3 → 8 not taken.
✓ Branch 4 → 5 taken 35 times.
✓ Branch 5 → 6 taken 30 times.
✓ Branch 5 → 8 taken 5 times.
✓ Branch 6 → 7 taken 30 times.
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 9 taken 28 times.
✓ Branch 10 → 11 taken 7 times.
✓ Branch 10 → 12 taken 28 times.
35 if (address.isNull() || address.isLoopback() || address.isMulticast()) return false;
10 28 bool mapped = false;
11
1/1
✓ Branch 12 → 13 taken 28 times.
28 const quint32 ipv4 = address.toIPv4Address(&mapped);
12
2/2
✓ Branch 13 → 14 taken 20 times.
✓ Branch 13 → 20 taken 8 times.
28 if (mapped) {
13 struct Range { quint32 network; int bits; };
14 static constexpr Range denied[] = {
15 {0x00000000, 8}, {0x0a000000, 8}, {0x64400000, 10}, {0x7f000000, 8},
16 {0xa9fe0000, 16}, {0xac100000, 12}, {0xc0000000, 24}, {0xc0000200, 24},
17 {0xc0a80000, 16}, {0xc6120000, 15}, {0xc6336400, 24}, {0xcb007100, 24},
18 {0xe0000000, 3}
19 };
20
2/2
✓ Branch 18 → 15 taken 153 times.
✓ Branch 18 → 19 taken 7 times.
160 for (const auto &range : denied) {
21 153 const quint32 mask = 0xffffffffU << (32 - range.bits);
22
2/2
✓ Branch 15 → 16 taken 13 times.
✓ Branch 15 → 17 taken 140 times.
153 if ((ipv4 & mask) == range.network) return false;
23 }
24 7 return true;
25 }
26
2/3
✓ Branch 20 → 21 taken 8 times.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 8 times.
8 if (address.protocol() != QAbstractSocket::IPv6Protocol) return false;
27
2/2
✓ Branch 34 → 25 taken 37 times.
✓ Branch 34 → 35 taken 5 times.
42 for (const auto &range : {"::/96", "fc00::/7", "fe80::/10", "fec0::/10", "ff00::/8", "2001:db8::/32"})
28
5/5
✓ Branch 26 → 27 taken 37 times.
✓ Branch 27 → 28 taken 37 times.
✓ Branch 28 → 29 taken 37 times.
✓ Branch 31 → 32 taken 3 times.
✓ Branch 31 → 33 taken 34 times.
37 if (address.isInSubnet(QHostAddress::parseSubnet(QString::fromLatin1(range)))) return false;
29 // S106-223: a 6to4 address (2002::/16) carries an IPv4 address in its bits
30 // 16…47; it is public only when that IPv4 address is.
31
4/4
✓ Branch 38 → 40 taken 5 times.
✓ Branch 40 → 41 taken 5 times.
✓ Branch 44 → 45 taken 4 times.
✓ Branch 44 → 55 taken 1 time.
5 if (address.isInSubnet(QHostAddress::parseSubnet(QStringLiteral("2002::/16")))) {
32
1/1
✓ Branch 45 → 46 taken 4 times.
4 const Q_IPV6ADDR bytes = address.toIPv6Address();
33 4 const quint32 embedded = (quint32(bytes[2]) << 24) | (quint32(bytes[3]) << 16) | (quint32(bytes[4]) << 8) | bytes[5];
34
2/2
✓ Branch 50 → 51 taken 4 times.
✓ Branch 51 → 52 taken 4 times.
4 return isPublicAddress(QHostAddress(embedded));
35 }
36 // Global unicast currently occupies 2000::/3; fail closed elsewhere.
37
2/2
✓ Branch 58 → 59 taken 1 time.
✓ Branch 59 → 60 taken 1 time.
1 return address.isInSubnet(QHostAddress::parseSubnet(QStringLiteral("2000::/3")));
38 }
39
40 67 inline bool isPublicLiteralOrName(const QString &input) {
41
2/2
✓ Branch 2 → 3 taken 67 times.
✓ Branch 3 → 4 taken 67 times.
67 QString host = input.trimmed().toLower();
42
4/4
✓ Branch 6 → 7 taken 67 times.
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 9 taken 65 times.
✓ Branch 8 → 9 taken 2 times.
67 if (host.endsWith('.')) host.chop(1);
43
3/9
✓ Branch 10 → 11 taken 67 times.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 16 taken 67 times.
✗ Branch 13 → 14 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 16 not taken.
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 23 taken 67 times.
✗ Branch 19 → 20 not taken.
67 if (host.startsWith('[') && host.endsWith(']')) host = host.mid(1, host.size() - 2);
44
7/7
✓ Branch 24 → 25 taken 64 times.
✓ Branch 24 → 28 taken 3 times.
✓ Branch 26 → 27 taken 64 times.
✓ Branch 27 → 28 taken 1 time.
✓ Branch 27 → 29 taken 63 times.
✓ Branch 30 → 31 taken 4 times.
✓ Branch 30 → 32 taken 63 times.
67 if (host.isEmpty() || host.contains('%')) return false;
45 4 static const QRegularExpression numerical(QStringLiteral(
46
4/7
✓ Branch 32 → 33 taken 2 times.
✓ Branch 32 → 45 taken 61 times.
✓ Branch 34 → 35 taken 2 times.
✗ Branch 34 → 45 not taken.
✓ Branch 39 → 40 taken 2 times.
✗ Branch 128 → 129 not taken.
✗ Branch 128 → 130 not taken.
67 "^(?:0x[0-9a-f]+|[0-9]+)(?:\\.(?:0x[0-9a-f]+|[0-9]+)){0,3}$"));
47 4 static const QRegularExpression dottedDecimal(QStringLiteral(
48
4/7
✓ Branch 45 → 46 taken 2 times.
✓ Branch 45 → 58 taken 61 times.
✓ Branch 47 → 48 taken 2 times.
✗ Branch 47 → 58 not taken.
✓ Branch 52 → 53 taken 2 times.
✗ Branch 138 → 139 not taken.
✗ Branch 138 → 140 not taken.
67 "^(?:0|[1-9][0-9]{0,2})(?:\\.(?:0|[1-9][0-9]{0,2})){3}$"));
49
13/18
✓ Branch 59 → 60 taken 63 times.
✓ Branch 60 → 61 taken 63 times.
✓ Branch 61 → 62 taken 23 times.
✓ Branch 61 → 67 taken 40 times.
✓ Branch 63 → 64 taken 23 times.
✓ Branch 64 → 65 taken 23 times.
✓ Branch 65 → 66 taken 5 times.
✓ Branch 65 → 67 taken 18 times.
✓ Branch 68 → 69 taken 23 times.
✓ Branch 68 → 70 taken 40 times.
✓ Branch 70 → 71 taken 63 times.
✗ Branch 70 → 72 not taken.
✓ Branch 72 → 73 taken 5 times.
✓ Branch 72 → 74 taken 58 times.
✗ Branch 141 → 142 not taken.
✗ Branch 141 → 143 not taken.
✗ Branch 146 → 147 not taken.
✗ Branch 146 → 148 not taken.
86 if (numerical.match(host).hasMatch() && !dottedDecimal.match(host).hasMatch()) return false;
50
1/1
✓ Branch 74 → 75 taken 58 times.
58 const QHostAddress address(host);
51
4/4
✓ Branch 75 → 76 taken 58 times.
✓ Branch 76 → 77 taken 31 times.
✓ Branch 76 → 79 taken 27 times.
✓ Branch 77 → 78 taken 31 times.
58 if (!address.isNull()) return isPublicAddress(address);
52
4/4
✓ Branch 80 → 81 taken 27 times.
✓ Branch 81 → 82 taken 27 times.
✓ Branch 83 → 84 taken 1 time.
✓ Branch 83 → 85 taken 26 times.
27 if (numerical.match(host).hasMatch()) return false;
53
1/1
✓ Branch 85 → 86 taken 26 times.
26 host = HostMatch::canonicalName(host);
54
7/7
✓ Branch 89 → 90 taken 25 times.
✓ Branch 89 → 93 taken 1 time.
✓ Branch 91 → 92 taken 25 times.
✓ Branch 92 → 93 taken 3 times.
✓ Branch 92 → 94 taken 22 times.
✓ Branch 95 → 96 taken 4 times.
✓ Branch 95 → 97 taken 22 times.
26 if (host.isEmpty() || !host.contains('.')) return false;
55
2/2
✓ Branch 106 → 99 taken 82 times.
✓ Branch 106 → 107 taken 17 times.
99 for (const auto &suffix : {"localhost", "local", "internal", "home.arpa"})
56
4/4
✓ Branch 100 → 101 taken 82 times.
✓ Branch 101 → 102 taken 82 times.
✓ Branch 103 → 104 taken 5 times.
✓ Branch 103 → 105 taken 77 times.
82 if (HostMatch::isHostOrSubdomain(host, QString::fromLatin1(suffix))) return false;
57 17 return true;
58 67 }
59 }
60