summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Bruhin <me@the-compiler.org>2024-05-05 20:36:09 +0200
committerGitHub <noreply@github.com>2024-05-05 20:36:09 +0200
commitef62208ce998797c9a1f0110a528bbd11c402357 (patch)
treeebf2cc0cae57455d6e13ae75251439c5b3611aa8
parent5152296f7f9d3a656e5b1fac5b5af648863c6224 (diff)
parent2edfd459a42bdaa23b2e2f61df876265e9444548 (diff)
downloadqutebrowser-ef62208ce998797c9a1f0110a528bbd11c402357.tar.gz
qutebrowser-ef62208ce998797c9a1f0110a528bbd11c402357.zip
Merge pull request #8181 from qutebrowser/fill-in-security-version
Improve Chromium (security) version output
-rw-r--r--qutebrowser/html/version.html2
-rw-r--r--qutebrowser/utils/version.py215
-rw-r--r--tests/unit/utils/test_version.py70
3 files changed, 180 insertions, 107 deletions
diff --git a/qutebrowser/html/version.html b/qutebrowser/html/version.html
index 643929088..666414b26 100644
--- a/qutebrowser/html/version.html
+++ b/qutebrowser/html/version.html
@@ -19,8 +19,8 @@ html { margin-left: 10px; }
{% block content %}
{{ super() }}
<h1>Version info</h1>
-<pre>{{ version }}</pre>
<button onclick="paste_version()">Yank pastebin URL for version info</button>
+<pre>{{ version }}</pre>
<h1>Copyright info</h1>
<p>{{ copyright }}</p>
diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py
index 32d5357db..775f57bd0 100644
--- a/qutebrowser/utils/version.py
+++ b/qutebrowser/utils/version.py
@@ -538,7 +538,19 @@ class WebEngineVersions:
chromium_security: Optional[str] = None
chromium_major: Optional[int] = dataclasses.field(init=False)
- _CHROMIUM_VERSIONS: ClassVar[Dict[utils.VersionNumber, str]] = {
+ _BASES: ClassVar[Dict[int, str]] = {
+ 83: '83.0.4103.122', # ~2020-06-24
+ 87: '87.0.4280.144', # ~2020-12-02
+ 90: '90.0.4430.228', # 2021-06-22
+ 94: '94.0.4606.126', # 2021-11-17
+ 102: '102.0.5005.177', # ~2022-05-24
+ # (.220 claimed by code, .181 claimed by CHROMIUM_VERSION)
+ 108: '108.0.5359.220', # ~2022-12-23
+ 112: '112.0.5615.213', # ~2023-04-18
+ 118: '118.0.5993.220', # ~2023-10-24
+ }
+
+ _CHROMIUM_VERSIONS: ClassVar[Dict[utils.VersionNumber, Tuple[str, Optional[str]]]] = {
# ====== UNSUPPORTED =====
# Qt 5.12: Chromium 69
@@ -559,73 +571,61 @@ class WebEngineVersions:
# 5.15.1: Security fixes up to 85.0.4183.83 (2020-08-25)
# ====== SUPPORTED =====
-
- # Qt 5.15.2: Chromium 83
- # 83.0.4103.122 (~2020-06-24)
- # 5.15.2: Security fixes up to 86.0.4240.183 (2020-11-02)
- utils.VersionNumber(5, 15, 2): '83.0.4103.122',
-
- # Qt 5.15.3: Chromium 87
- # 87.0.4280.144 (~2020-12-02)
- # 5.15.3: Security fixes up to 88.0.4324.150 (2021-02-04)
- # 5.15.4: Security fixes up to ???
- # 5.15.5: Security fixes up to ???
- # 5.15.6: Security fixes up to ???
- # 5.15.7: Security fixes up to 94.0.4606.61 (2021-09-24)
- # 5.15.8: Security fixes up to 96.0.4664.110 (2021-12-13)
- # 5.15.9: Security fixes up to 98.0.4758.102 (2022-02-14)
- # 5.15.10: Security fixes up to ???
- # 5.15.11: Security fixes up to ???
- utils.VersionNumber(5, 15): '87.0.4280.144', # >= 5.15.3
-
- # Qt 6.2: Chromium 90
- # 90.0.4430.228 (2021-06-22)
- # 6.2.0: Security fixes up to 93.0.4577.63 (2021-08-31)
- # 6.2.1: Security fixes up to 94.0.4606.61 (2021-09-24)
- # 6.2.2: Security fixes up to 96.0.4664.45 (2021-11-15)
- # 6.2.3: Security fixes up to 96.0.4664.45 (2021-11-15)
- # 6.2.4: Security fixes up to 98.0.4758.102 (2022-02-14)
- # 6.2.5: Security fixes up to ???
- # 6.2.6: Security fixes up to ???
- # 6.2.7: Security fixes up to ???
- utils.VersionNumber(6, 2): '90.0.4430.228',
-
- # Qt 6.3: Chromium 94
- # 94.0.4606.126 (2021-11-17)
- # 6.3.0: Security fixes up to 99.0.4844.84 (2022-03-25)
- # 6.3.1: Security fixes up to 101.0.4951.64 (2022-05-10)
- # 6.3.2: Security fixes up to 104.0.5112.81 (2022-08-01)
- utils.VersionNumber(6, 3): '94.0.4606.126',
-
- # Qt 6.4: Chromium 102
- # 102.0.5005.177 (~2022-05-24)
- # 6.4.0: Security fixes up to 104.0.5112.102 (2022-08-16)
- # 6.4.1: Security fixes up to 107.0.5304.88 (2022-10-27)
- # 6.4.2: Security fixes up to 108.0.5359.94 (2022-12-02)
- # 6.4.3: Security fixes up to 110.0.5481.78 (2023-02-07)
- utils.VersionNumber(6, 4): '102.0.5005.177',
-
- # Qt 6.5: Chromium 108
- # 108.0.5359.220 (~2022-12-23)
- # (.220 claimed by code, .181 claimed by CHROMIUM_VERSION)
- # 6.5.0: Security fixes up to 110.0.5481.104 (2023-02-16)
- # 6.5.1: Security fixes up to 112.0.5615.138 (2023-04-18)
- # 6.5.2: Security fixes up to 114.0.5735.133 (2023-06-13)
- # 6.5.3: Security fixes up to 117.0.5938.63 (2023-09-12)
- utils.VersionNumber(6, 5): '108.0.5359.220',
-
- # Qt 6.6: Chromium 112
- # 112.0.5615.213 (~2023-04-18)
- # 6.6.0: Security fixes up to 117.0.5938.63 (2023-09-12)
- # 6.6.1: Security fixes up to 119.0.6045.123 (2023-11-07)
- # 6.6.2: Security fixes up to 121.0.6167.160 (2024-02-06)
- # 6.6.3: Security fixes up to 122.0.6261.128 (2024-03-12)
- utils.VersionNumber(6, 6): '112.0.5615.213',
-
- # Qt 6.7: Chromium 118
- # 118.0.5993.220 (~2023-10-24)
- # 6.6.0: Security fixes up to 122.0.6261.128 (?) (2024-03-12)
- utils.VersionNumber(6, 7): '118.0.5993.220',
+ # base security
+ ## Qt 5.15
+ utils.VersionNumber(5, 15, 2): (_BASES[83], '86.0.4240.183'), # 2020-11-02
+ utils.VersionNumber(5, 15): (_BASES[87], None), # >= 5.15.3
+ utils.VersionNumber(5, 15, 3): (_BASES[87], '88.0.4324.150'), # 2021-02-04
+ # 5.15.4 to 5.15.6: unknown security fixes
+ utils.VersionNumber(5, 15, 7): (_BASES[87], '94.0.4606.61'), # 2021-09-24
+ utils.VersionNumber(5, 15, 8): (_BASES[87], '96.0.4664.110'), # 2021-12-13
+ utils.VersionNumber(5, 15, 9): (_BASES[87], '98.0.4758.102'), # 2022-02-14
+ utils.VersionNumber(5, 15, 10): (_BASES[87], '98.0.4758.102'), # (?) 2022-02-14
+ utils.VersionNumber(5, 15, 11): (_BASES[87], '98.0.4758.102'), # (?) 2022-02-14
+ utils.VersionNumber(5, 15, 12): (_BASES[87], '98.0.4758.102'), # (?) 2022-02-14
+ utils.VersionNumber(5, 15, 13): (_BASES[87], '108.0.5359.124'), # 2022-12-13
+ utils.VersionNumber(5, 15, 14): (_BASES[87], '113.0.5672.64'), # 2023-05-02
+ # 5.15.15: unknown security fixes
+ utils.VersionNumber(5, 15, 16): (_BASES[87], '119.0.6045.123'), # 2023-11-07
+ utils.VersionNumber(5, 15, 17): (_BASES[87], '123.0.6312.58'), # 2024-03-19
+
+
+ ## Qt 6.2
+ utils.VersionNumber(6, 2): (_BASES[90], '93.0.4577.63'), # 2021-08-31
+ utils.VersionNumber(6, 2, 1): (_BASES[90], '94.0.4606.61'), # 2021-09-24
+ utils.VersionNumber(6, 2, 2): (_BASES[90], '96.0.4664.45'), # 2021-11-15
+ utils.VersionNumber(6, 2, 3): (_BASES[90], '96.0.4664.45'), # 2021-11-15
+ utils.VersionNumber(6, 2, 4): (_BASES[90], '98.0.4758.102'), # 2022-02-14
+ # 6.2.5 / 6.2.6: unknown security fixes
+ utils.VersionNumber(6, 2, 7): (_BASES[90], '107.0.5304.110'), # 2022-11-08
+ utils.VersionNumber(6, 2, 8): (_BASES[90], '111.0.5563.110'), # 2023-03-21
+
+ ## Qt 6.3
+ utils.VersionNumber(6, 3): (_BASES[94], '99.0.4844.84'), # 2022-03-25
+ utils.VersionNumber(6, 3, 1): (_BASES[94], '101.0.4951.64'), # 2022-05-10
+ utils.VersionNumber(6, 3, 2): (_BASES[94], '104.0.5112.81'), # 2022-08-01
+
+ ## Qt 6.4
+ utils.VersionNumber(6, 4): (_BASES[102], '104.0.5112.102'), # 2022-08-16
+ utils.VersionNumber(6, 4, 1): (_BASES[102], '107.0.5304.88'), # 2022-10-27
+ utils.VersionNumber(6, 4, 2): (_BASES[102], '108.0.5359.94'), # 2022-12-02
+ utils.VersionNumber(6, 4, 3): (_BASES[102], '110.0.5481.78'), # 2023-02-07
+
+ ## Qt 6.5
+ utils.VersionNumber(6, 5): (_BASES[108], '110.0.5481.104'), # 2023-02-16
+ utils.VersionNumber(6, 5, 1): (_BASES[108], '112.0.5615.138'), # 2023-04-18
+ utils.VersionNumber(6, 5, 2): (_BASES[108], '114.0.5735.133'), # 2023-06-13
+ utils.VersionNumber(6, 5, 3): (_BASES[108], '117.0.5938.63'), # 2023-09-12
+
+ ## Qt 6.6
+ utils.VersionNumber(6, 6): (_BASES[112], '117.0.5938.63'), # 2023-09-12
+ utils.VersionNumber(6, 6, 1): (_BASES[112], '119.0.6045.123'), # 2023-11-07
+ utils.VersionNumber(6, 6, 2): (_BASES[112], '121.0.6167.160'), # 2024-02-06
+ utils.VersionNumber(6, 6, 3): (_BASES[112], '122.0.6261.128'), # 2024-03-12
+
+ ## Qt 6.7
+ utils.VersionNumber(6, 7): (_BASES[118], '122.0.6261.128'), # 2024-03-12
+ utils.VersionNumber(6, 7, 1): (_BASES[118], '124.0.6367.78'), # (?) 2024-04-24
}
def __post_init__(self) -> None:
@@ -636,27 +636,37 @@ class WebEngineVersions:
self.chromium_major = int(self.chromium.split('.')[0])
def __str__(self) -> str:
- s = f'QtWebEngine {self.webengine}'
+ lines = [f'QtWebEngine {self.webengine}']
if self.chromium is not None:
- s += f', based on Chromium {self.chromium}'
+ lines.append(f' based on Chromium {self.chromium}')
if self.chromium_security is not None:
- s += f', with security patches up to {self.chromium_security} (plus any distribution patches)'
- if self.source != 'UA':
- s += f' (from {self.source})'
- return s
+ lines.append(f' with security patches up to {self.chromium_security} (plus any distribution patches)')
+ lines.append(f' (source: {self.source})')
+ return "\n".join(lines)
@classmethod
def from_ua(cls, ua: 'websettings.UserAgent') -> 'WebEngineVersions':
"""Get the versions parsed from a user agent.
- This is the most reliable and "default" way to get this information (at least
- until QtWebEngine adds an API for it). However, it needs a fully initialized
- QtWebEngine, and we sometimes need this information before that is available.
+ This is the most reliable and "default" way to get this information for
+ older Qt versions that don't provide an API for it. However, it needs a
+ fully initialized QtWebEngine, and we sometimes need this information
+ before that is available.
"""
assert ua.qt_version is not None, ua
+ webengine = utils.VersionNumber.parse(ua.qt_version)
+ chromium_inferred, chromium_security = cls._infer_chromium_version(webengine)
+ if ua.upstream_browser_version != chromium_inferred: # pragma: no cover
+ # should never happen, but let's play it safe
+ log.misc.debug(
+ f"Chromium version mismatch: {ua.upstream_browser_version} (UA) != "
+ f"{chromium_inferred} (inferred)")
+ chromium_security = None
+
return cls(
- webengine=utils.VersionNumber.parse(ua.qt_version),
+ webengine=webengine,
chromium=ua.upstream_browser_version,
+ chromium_security=chromium_security,
source='UA',
)
@@ -671,9 +681,19 @@ class WebEngineVersions:
sometimes mix and match Qt/QtWebEngine versions, so this is a more reliable
(though hackish) way to get a more accurate result.
"""
+ webengine = utils.VersionNumber.parse(versions.webengine)
+ chromium_inferred, chromium_security = cls._infer_chromium_version(webengine)
+ if versions.chromium != chromium_inferred: # pragma: no cover
+ # should never happen, but let's play it safe
+ log.misc.debug(
+ f"Chromium version mismatch: {versions.chromium} (ELF) != "
+ f"{chromium_inferred} (inferred)")
+ chromium_security = None
+
return cls(
- webengine=utils.VersionNumber.parse(versions.webengine),
+ webengine=webengine,
chromium=versions.chromium,
+ chromium_security=chromium_security,
source='ELF',
)
@@ -681,21 +701,29 @@ class WebEngineVersions:
def _infer_chromium_version(
cls,
pyqt_webengine_version: utils.VersionNumber,
- ) -> Optional[str]:
- """Infer the Chromium version based on the PyQtWebEngine version."""
- chromium_version = cls._CHROMIUM_VERSIONS.get(pyqt_webengine_version)
+ ) -> Tuple[Optional[str], Optional[str]]:
+ """Infer the Chromium version based on the PyQtWebEngine version.
+
+ Returns:
+ A tuple of the Chromium version and the security patch version.
+ """
+ chromium_version, security_version = cls._CHROMIUM_VERSIONS.get(
+ pyqt_webengine_version, (None, None))
if chromium_version is not None:
- return chromium_version
+ return chromium_version, security_version
# 5.15 patch versions change their QtWebEngine version, but no changes are
# expected after 5.15.3 and 5.15.[01] are unsupported.
- if pyqt_webengine_version == utils.VersionNumber(5, 15, 2):
- minor_version = pyqt_webengine_version
- else:
- # e.g. 5.14.2 -> 5.14
- minor_version = pyqt_webengine_version.strip_patch()
+ assert pyqt_webengine_version != utils.VersionNumber(5, 15, 2)
+
+ # e.g. 5.15.4 -> 5.15
+ # we ignore the security version as that one will have changed from .0
+ # and is thus unknown.
+ minor_version = pyqt_webengine_version.strip_patch()
+ chromium_ver, _security_ver = cls._CHROMIUM_VERSIONS.get(
+ minor_version, (None, None))
- return cls._CHROMIUM_VERSIONS.get(minor_version)
+ return chromium_ver, None
@classmethod
def from_api(
@@ -730,9 +758,11 @@ class WebEngineVersions:
a PyQtWebEngine-Qt{,5} package from PyPI, so we could query its exact version.
"""
parsed = utils.VersionNumber.parse(pyqt_webengine_qt_version)
+ chromium, chromium_security = cls._infer_chromium_version(parsed)
return cls(
webengine=parsed,
- chromium=cls._infer_chromium_version(parsed),
+ chromium=chromium,
+ chromium_security=chromium_security,
source=source,
)
@@ -775,9 +805,12 @@ class WebEngineVersions:
if frozen:
parsed = utils.VersionNumber(5, 15, 2)
+ chromium, chromium_security = cls._infer_chromium_version(parsed)
+
return cls(
webengine=parsed,
- chromium=cls._infer_chromium_version(parsed),
+ chromium=chromium,
+ chromium_security=chromium_security,
source=source,
)
diff --git a/tests/unit/utils/test_version.py b/tests/unit/utils/test_version.py
index f24bf2a7a..5d2863100 100644
--- a/tests/unit/utils/test_version.py
+++ b/tests/unit/utils/test_version.py
@@ -899,21 +899,32 @@ class TestWebEngineVersions:
webengine=utils.VersionNumber(5, 15, 2),
chromium=None,
source='UA'),
- "QtWebEngine 5.15.2",
+ (
+ "QtWebEngine 5.15.2\n"
+ " (source: UA)"
+ ),
),
(
version.WebEngineVersions(
webengine=utils.VersionNumber(5, 15, 2),
chromium='87.0.4280.144',
source='UA'),
- "QtWebEngine 5.15.2, based on Chromium 87.0.4280.144",
+ (
+ "QtWebEngine 5.15.2\n"
+ " based on Chromium 87.0.4280.144\n"
+ " (source: UA)"
+ ),
),
(
version.WebEngineVersions(
webengine=utils.VersionNumber(5, 15, 2),
chromium='87.0.4280.144',
source='faked'),
- "QtWebEngine 5.15.2, based on Chromium 87.0.4280.144 (from faked)",
+ (
+ "QtWebEngine 5.15.2\n"
+ " based on Chromium 87.0.4280.144\n"
+ " (source: faked)"
+ ),
),
(
version.WebEngineVersions(
@@ -922,8 +933,10 @@ class TestWebEngineVersions:
chromium_security='9000.1',
source='faked'),
(
- "QtWebEngine 5.15.2, based on Chromium 87.0.4280.144, with security "
- "patches up to 9000.1 (plus any distribution patches) (from faked)"
+ "QtWebEngine 5.15.2\n"
+ " based on Chromium 87.0.4280.144\n"
+ " with security patches up to 9000.1 (plus any distribution patches)\n"
+ " (source: faked)"
),
),
])
@@ -961,6 +974,7 @@ class TestWebEngineVersions:
expected = version.WebEngineVersions(
webengine=utils.VersionNumber(5, 15, 2),
chromium='83.0.4103.122',
+ chromium_security='86.0.4240.183',
source='UA',
)
assert version.WebEngineVersions.from_ua(ua) == expected
@@ -970,21 +984,27 @@ class TestWebEngineVersions:
expected = version.WebEngineVersions(
webengine=utils.VersionNumber(5, 15, 2),
chromium='83.0.4103.122',
+ chromium_security='86.0.4240.183',
source='ELF',
)
assert version.WebEngineVersions.from_elf(elf_version) == expected
- @pytest.mark.parametrize('pyqt_version, chromium_version', [
- ('5.15.2', '83.0.4103.122'),
- ('5.15.3', '87.0.4280.144'),
- ('5.15.4', '87.0.4280.144'),
- ('5.15.5', '87.0.4280.144'),
- ('6.2.0', '90.0.4430.228'),
- ('6.3.0', '94.0.4606.126'),
+ @pytest.mark.parametrize('pyqt_version, chromium_version, security_version', [
+ ('5.15.2', '83.0.4103.122', '86.0.4240.183'),
+ ('5.15.3', '87.0.4280.144', '88.0.4324.150'),
+ ('5.15.4', '87.0.4280.144', None),
+ ('5.15.5', '87.0.4280.144', None),
+ ('5.15.6', '87.0.4280.144', None),
+ ('5.15.7', '87.0.4280.144', '94.0.4606.61'),
+ ('6.2.0', '90.0.4430.228', '93.0.4577.63'),
+ ('6.2.99', '90.0.4430.228', None),
+ ('6.3.0', '94.0.4606.126', '99.0.4844.84'),
+ ('6.99.0', None, None),
])
- def test_from_pyqt(self, freezer, pyqt_version, chromium_version):
- if freezer and pyqt_version in ['5.15.3', '5.15.4', '5.15.5']:
+ def test_from_pyqt(self, freezer, pyqt_version, chromium_version, security_version):
+ if freezer and utils.VersionNumber(5, 15, 3) <= utils.VersionNumber.parse(pyqt_version) < utils.VersionNumber(6):
chromium_version = '83.0.4103.122'
+ security_version = '86.0.4240.183'
expected_pyqt_version = '5.15.2'
else:
expected_pyqt_version = pyqt_version
@@ -992,6 +1012,7 @@ class TestWebEngineVersions:
expected = version.WebEngineVersions(
webengine=utils.VersionNumber.parse(expected_pyqt_version),
chromium=chromium_version,
+ chromium_security=security_version,
source='PyQt',
)
assert version.WebEngineVersions.from_pyqt(pyqt_version) == expected
@@ -1049,6 +1070,25 @@ class TestWebEngineVersions:
security = utils.VersionNumber.parse(qWebEngineChromiumSecurityPatchVersion())
assert security >= base
+ def test_chromium_security_version_dict(self, qapp):
+ """Check if we infer the QtWebEngine security version properly.
+
+ Note this test mostly tests that our overview in version.py (also
+ intended for human readers) is accurate. The code we call here is never
+ going to be called in real-life situations, as the API is available.
+ """
+ try:
+ from qutebrowser.qt.webenginecore import (
+ qWebEngineVersion,
+ qWebEngineChromiumSecurityPatchVersion,
+ )
+ except ImportError:
+ pytest.skip("Requires QtWebEngine 6.3+")
+
+ inferred = version.WebEngineVersions.from_webengine(
+ qWebEngineVersion(), source="API")
+ assert inferred.chromium_security == qWebEngineChromiumSecurityPatchVersion()
+
class FakeQSslSocket:
@@ -1319,7 +1359,7 @@ def test_version_info(params, stubs, monkeypatch, config_stub):
else:
monkeypatch.delattr(version, 'qtutils.qWebKitVersion', raising=False)
patches['objects.backend'] = usertypes.Backend.QtWebEngine
- substitutions['backend'] = 'QtWebEngine 1.2.3 (from faked)'
+ substitutions['backend'] = 'QtWebEngine 1.2.3\n (source: faked)'
if params.known_distribution:
patches['distribution'] = lambda: version.DistributionInfo(