VYPR
Low severityNVD Advisory· Published May 7, 2020· Updated Aug 4, 2024

Incorrect Provision of Specified Functionality in qutebrowser

CVE-2020-11054

Description

In qutebrowser versions less than 1.11.1, reloading a page with certificate errors shows a green URL. After a certificate error was overridden by the user, qutebrowser displays the URL as yellow (colors.statusbar.url.warn.fg). However, when the affected website was subsequently loaded again, the URL was mistakenly displayed as green (colors.statusbar.url.success_https). While the user already has seen a certificate error prompt at this point (or set content.ssl_strict to false, which is not recommended), this could still provide a false sense of security. This has been fixed in 1.11.1 and 1.12.0. All versions of qutebrowser are believed to be affected, though versions before v0.11.x couldn't be tested. Backported patches for older versions (greater than or equal to 1.4.0 and less than or equal to 1.10.2) are available, but no further releases are planned.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
qutebrowserPyPI
< 1.11.11.11.1

Affected products

1

Patches

10
1b7946ed14b3

Update changelog

https://github.com/qutebrowser/qutebrowserFlorian BruhinMay 2, 2020via ghsa
1 file changed · +10 1
  • doc/changelog.asciidoc+10 1 modified
    @@ -45,7 +45,16 @@ Fixed
     v1.11.1 (unreleased)
     --------------------
     
    -No changes yet.
    +Security
    +~~~~~~~~
    +
    +- After a certificate error was overridden by the user, qutebrowser displays
    +  the URL as yellow (`colors.statusbar.url.warn.fg`). However, when the
    +  affected website was subsequently loaded again, the URL was mistakenly
    +  displayed as green (`colors.statusbar.url.success_https`). While the user
    +  already has seen a certificate error prompt at this point (or set
    +  `content.ssl_strict` to `false` which is not recommended), this could still
    +  provide a false sense of security. This is now fixed.
     
     v1.11.0 (2020-04-27)
     --------------------
    
d28ed758d077

Security: Remember hosts with ignored cert errors for load status

https://github.com/qutebrowser/qutebrowserFlorian BruhinMay 2, 2020via ghsa
3 files changed · +17 9
  • qutebrowser/browser/browsertab.py+12 4 modified
    @@ -867,6 +867,13 @@ class AbstractTab(QWidget):
         # arg 1: The exit code.
         renderer_process_terminated = pyqtSignal(TerminationStatus, int)
     
    +    # Hosts for which a certificate error happened. Shared between all tabs.
    +    #
    +    # Note that we remember hosts here, without scheme/port:
    +    # QtWebEngine/Chromium also only remembers hostnames, and certificates are
    +    # for a given hostname anyways.
    +    _insecure_hosts = set()  # type: typing.Set[str]
    +
         def __init__(self, *, win_id: int, private: bool,
                      parent: QWidget = None) -> None:
             self.is_private = private
    @@ -884,7 +891,6 @@ def __init__(self, *, win_id: int, private: bool,
             self._layout = miscwidgets.WrapperLayout(self)
             self._widget = typing.cast(QWidget, None)
             self._progress = 0
    -        self._has_ssl_errors = False
             self._load_status = usertypes.LoadStatus.none
             self._tab_event_filter = eventfilter.TabEventFilter(
                 self, parent=self)
    @@ -971,7 +977,6 @@ def _on_url_changed(self, url: QUrl) -> None:
         @pyqtSlot()
         def _on_load_started(self) -> None:
             self._progress = 0
    -        self._has_ssl_errors = False
             self.data.viewing_source = False
             self._set_load_status(usertypes.LoadStatus.loading)
             self.load_started.emit()
    @@ -1030,9 +1035,12 @@ def _update_load_status(self, ok: bool) -> None:
             Needs to be called by subclasses to trigger a load status update, e.g.
             as a response to a loadFinished signal.
             """
    -        if ok and not self._has_ssl_errors:
    +        if ok:
                 if self.url().scheme() == 'https':
    -                self._set_load_status(usertypes.LoadStatus.success_https)
    +                if self.url().host() in self._insecure_hosts:
    +                    self._set_load_status(usertypes.LoadStatus.warn)
    +                else:
    +                    self._set_load_status(usertypes.LoadStatus.success_https)
                 else:
                     self._set_load_status(usertypes.LoadStatus.success)
             elif ok:
    
  • qutebrowser/browser/webengine/webenginetab.py+2 2 modified
    @@ -1450,9 +1450,9 @@ def _on_load_finished(self, ok: bool) -> None:
     
         @pyqtSlot(certificateerror.CertificateErrorWrapper)
         def _on_ssl_errors(self, error):
    -        self._has_ssl_errors = True
    -
             url = error.url()
    +        self._insecure_hosts.add(url.host())
    +
             log.webview.debug("Certificate error: {}".format(error))
     
             if error.is_overridable():
    
  • qutebrowser/browser/webkit/webkittab.py+3 3 modified
    @@ -847,9 +847,9 @@ def _on_navigation_request(self, navigation):
             if navigation.is_main_frame:
                 self.settings.update_for_url(navigation.url)
     
    -    @pyqtSlot()
    -    def _on_ssl_errors(self):
    -        self._has_ssl_errors = True
    +    @pyqtSlot('QNetworkReply*')
    +    def _on_ssl_errors(self, reply):
    +        self._insecure_hosts.add(reply.url().host())
     
         def _connect_signals(self):
             view = self._widget
    
9bd1cf585fcc

Security: Remember hosts with ignored cert errors for load status

https://github.com/qutebrowser/qutebrowserFlorian BruhinMay 2, 2020via ghsa
3 files changed · +17 9
  • qutebrowser/browser/browsertab.py+12 4 modified
    @@ -867,6 +867,13 @@ class AbstractTab(QWidget):
         # arg 1: The exit code.
         renderer_process_terminated = pyqtSignal(TerminationStatus, int)
     
    +    # Hosts for which a certificate error happened. Shared between all tabs.
    +    #
    +    # Note that we remember hosts here, without scheme/port:
    +    # QtWebEngine/Chromium also only remembers hostnames, and certificates are
    +    # for a given hostname anyways.
    +    _insecure_hosts = set()  # type: typing.Set[str]
    +
         def __init__(self, *, win_id: int, private: bool,
                      parent: QWidget = None) -> None:
             self.is_private = private
    @@ -884,7 +891,6 @@ def __init__(self, *, win_id: int, private: bool,
             self._layout = miscwidgets.WrapperLayout(self)
             self._widget = typing.cast(QWidget, None)
             self._progress = 0
    -        self._has_ssl_errors = False
             self._load_status = usertypes.LoadStatus.none
             self._tab_event_filter = eventfilter.TabEventFilter(
                 self, parent=self)
    @@ -971,7 +977,6 @@ def _on_url_changed(self, url: QUrl) -> None:
         @pyqtSlot()
         def _on_load_started(self) -> None:
             self._progress = 0
    -        self._has_ssl_errors = False
             self.data.viewing_source = False
             self._set_load_status(usertypes.LoadStatus.loading)
             self.load_started.emit()
    @@ -1030,9 +1035,12 @@ def _update_load_status(self, ok: bool) -> None:
             Needs to be called by subclasses to trigger a load status update, e.g.
             as a response to a loadFinished signal.
             """
    -        if ok and not self._has_ssl_errors:
    +        if ok:
                 if self.url().scheme() == 'https':
    -                self._set_load_status(usertypes.LoadStatus.success_https)
    +                if self.url().host() in self._insecure_hosts:
    +                    self._set_load_status(usertypes.LoadStatus.warn)
    +                else:
    +                    self._set_load_status(usertypes.LoadStatus.success_https)
                 else:
                     self._set_load_status(usertypes.LoadStatus.success)
             elif ok:
    
  • qutebrowser/browser/webengine/webenginetab.py+2 2 modified
    @@ -1428,9 +1428,9 @@ def _on_load_finished(self, ok: bool) -> None:
     
         @pyqtSlot(certificateerror.CertificateErrorWrapper)
         def _on_ssl_errors(self, error):
    -        self._has_ssl_errors = True
    -
             url = error.url()
    +        self._insecure_hosts.add(url.host())
    +
             log.webview.debug("Certificate error: {}".format(error))
     
             if error.is_overridable():
    
  • qutebrowser/browser/webkit/webkittab.py+3 3 modified
    @@ -847,9 +847,9 @@ def _on_navigation_request(self, navigation):
             if navigation.is_main_frame:
                 self.settings.update_for_url(navigation.url)
     
    -    @pyqtSlot()
    -    def _on_ssl_errors(self):
    -        self._has_ssl_errors = True
    +    @pyqtSlot('QNetworkReply*')
    +    def _on_ssl_errors(self, reply):
    +        self._insecure_hosts.add(reply.url().host())
     
         def _connect_signals(self):
             view = self._widget
    
f5d801251aa5

Security: Remember hosts with ignored cert errors for load status

https://github.com/qutebrowser/qutebrowserFlorian BruhinMay 2, 2020via ghsa
3 files changed · +17 9
  • qutebrowser/browser/browsertab.py+12 4 modified
    @@ -860,6 +860,13 @@ class AbstractTab(QWidget):
         # arg 1: The exit code.
         renderer_process_terminated = pyqtSignal(TerminationStatus, int)
     
    +    # Hosts for which a certificate error happened. Shared between all tabs.
    +    #
    +    # Note that we remember hosts here, without scheme/port:
    +    # QtWebEngine/Chromium also only remembers hostnames, and certificates are
    +    # for a given hostname anyways.
    +    _insecure_hosts = set()  # type: typing.Set[str]
    +
         def __init__(self, *, win_id: int, private: bool,
                      parent: QWidget = None) -> None:
             self.is_private = private
    @@ -877,7 +884,6 @@ def __init__(self, *, win_id: int, private: bool,
             self._layout = miscwidgets.WrapperLayout(self)
             self._widget = None  # type: typing.Optional[QWidget]
             self._progress = 0
    -        self._has_ssl_errors = False
             self._load_status = usertypes.LoadStatus.none
             self._mouse_event_filter = mouse.MouseEventFilter(
                 self, parent=self)
    @@ -965,7 +971,6 @@ def _on_url_changed(self, url: QUrl) -> None:
         @pyqtSlot()
         def _on_load_started(self) -> None:
             self._progress = 0
    -        self._has_ssl_errors = False
             self.data.viewing_source = False
             self._set_load_status(usertypes.LoadStatus.loading)
             self.load_started.emit()
    @@ -1013,9 +1018,12 @@ def _on_load_finished(self, ok: bool) -> None:
     
             sess_manager.save_autosave()
     
    -        if ok and not self._has_ssl_errors:
    +        if ok:
                 if self.url().scheme() == 'https':
    -                self._set_load_status(usertypes.LoadStatus.success_https)
    +                if self.url().host() in self._insecure_hosts:
    +                    self._set_load_status(usertypes.LoadStatus.warn)
    +                else:
    +                    self._set_load_status(usertypes.LoadStatus.success_https)
                 else:
                     self._set_load_status(usertypes.LoadStatus.success)
             elif ok:
    
  • qutebrowser/browser/webengine/webenginetab.py+2 2 modified
    @@ -1385,9 +1385,9 @@ def _on_load_finished(self, ok):
     
         @pyqtSlot(certificateerror.CertificateErrorWrapper)
         def _on_ssl_errors(self, error):
    -        self._has_ssl_errors = True
    -
             url = error.url()
    +        self._insecure_hosts.add(url.host())
    +
             log.webview.debug("Certificate error: {}".format(error))
     
             if error.is_overridable():
    
  • qutebrowser/browser/webkit/webkittab.py+3 3 modified
    @@ -837,9 +837,9 @@ def _on_navigation_request(self, navigation):
             if navigation.is_main_frame:
                 self.settings.update_for_url(navigation.url)
     
    -    @pyqtSlot()
    -    def _on_ssl_errors(self):
    -        self._has_ssl_errors = True
    +    @pyqtSlot('QNetworkReply*')
    +    def _on_ssl_errors(self, reply):
    +        self._insecure_hosts.add(reply.url().host())
     
         def _connect_signals(self):
             view = self._widget
    
6821c236f9ae

Security: Remember hosts with ignored cert errors for load status

https://github.com/qutebrowser/qutebrowserFlorian BruhinMay 2, 2020via ghsa
3 files changed · +17 9
  • qutebrowser/browser/browsertab.py+12 4 modified
    @@ -869,6 +869,13 @@ class AbstractTab(QWidget):
         # arg 1: The exit code.
         renderer_process_terminated = pyqtSignal(TerminationStatus, int)
     
    +    # Hosts for which a certificate error happened. Shared between all tabs.
    +    #
    +    # Note that we remember hosts here, without scheme/port:
    +    # QtWebEngine/Chromium also only remembers hostnames, and certificates are
    +    # for a given hostname anyways.
    +    _insecure_hosts = set()  # type: typing.Set[str]
    +
         def __init__(self, *, win_id: int, private: bool,
                      parent: QWidget = None) -> None:
             self.is_private = private
    @@ -886,7 +893,6 @@ def __init__(self, *, win_id: int, private: bool,
             self._layout = miscwidgets.WrapperLayout(self)
             self._widget = typing.cast(QWidget, None)
             self._progress = 0
    -        self._has_ssl_errors = False
             self._load_status = usertypes.LoadStatus.none
             self._tab_event_filter = eventfilter.TabEventFilter(
                 self, parent=self)
    @@ -973,7 +979,6 @@ def _on_url_changed(self, url: QUrl) -> None:
         @pyqtSlot()
         def _on_load_started(self) -> None:
             self._progress = 0
    -        self._has_ssl_errors = False
             self.data.viewing_source = False
             self._set_load_status(usertypes.LoadStatus.loading)
             self.load_started.emit()
    @@ -1032,9 +1037,12 @@ def _update_load_status(self, ok: bool) -> None:
             Needs to be called by subclasses to trigger a load status update, e.g.
             as a response to a loadFinished signal.
             """
    -        if ok and not self._has_ssl_errors:
    +        if ok:
                 if self.url().scheme() == 'https':
    -                self._set_load_status(usertypes.LoadStatus.success_https)
    +                if self.url().host() in self._insecure_hosts:
    +                    self._set_load_status(usertypes.LoadStatus.warn)
    +                else:
    +                    self._set_load_status(usertypes.LoadStatus.success_https)
                 else:
                     self._set_load_status(usertypes.LoadStatus.success)
             elif ok:
    
  • qutebrowser/browser/webengine/webenginetab.py+2 2 modified
    @@ -1549,9 +1549,9 @@ def _on_load_finished(self, ok: bool) -> None:
     
         @pyqtSlot(certificateerror.CertificateErrorWrapper)
         def _on_ssl_errors(self, error):
    -        self._has_ssl_errors = True
    -
             url = error.url()
    +        self._insecure_hosts.add(url.host())
    +
             log.webview.debug("Certificate error: {}".format(error))
     
             if error.is_overridable():
    
  • qutebrowser/browser/webkit/webkittab.py+3 3 modified
    @@ -849,9 +849,9 @@ def _on_navigation_request(self, navigation):
             if navigation.is_main_frame:
                 self.settings.update_for_url(navigation.url)
     
    -    @pyqtSlot()
    -    def _on_ssl_errors(self):
    -        self._has_ssl_errors = True
    +    @pyqtSlot('QNetworkReply*')
    +    def _on_ssl_errors(self, reply):
    +        self._insecure_hosts.add(reply.url().host())
     
         def _connect_signals(self):
             view = self._widget
    
a45ca9c788f6

Security: Remember hosts with ignored cert errors for load status

https://github.com/qutebrowser/qutebrowserFlorian BruhinMay 2, 2020via ghsa
3 files changed · +17 9
  • qutebrowser/browser/browsertab.py+12 4 modified
    @@ -755,6 +755,13 @@ class AbstractTab(QWidget):
         renderer_process_terminated = pyqtSignal(TerminationStatus, int)
         predicted_navigation = pyqtSignal(QUrl)
     
    +    # Hosts for which a certificate error happened. Shared between all tabs.
    +    #
    +    # Note that we remember hosts here, without scheme/port:
    +    # QtWebEngine/Chromium also only remembers hostnames, and certificates are
    +    # for a given hostname anyways.
    +    _insecure_hosts = set()  # type: typing.Set[str]
    +
         def __init__(self, *, win_id, mode_manager, private, parent=None):
             self.private = private
             self.win_id = win_id
    @@ -771,7 +778,6 @@ def __init__(self, *, win_id, mode_manager, private, parent=None):
             self._layout = miscwidgets.WrapperLayout(self)
             self._widget = None
             self._progress = 0
    -        self._has_ssl_errors = False
             self._mode_manager = mode_manager
             self._load_status = usertypes.LoadStatus.none
             self._mouse_event_filter = mouse.MouseEventFilter(
    @@ -858,7 +864,6 @@ def _on_url_changed(self, url):
         @pyqtSlot()
         def _on_load_started(self):
             self._progress = 0
    -        self._has_ssl_errors = False
             self.data.viewing_source = False
             self._set_load_status(usertypes.LoadStatus.loading)
             self.load_started.emit()
    @@ -917,9 +922,12 @@ def _on_load_finished(self, ok):
             sess_manager = objreg.get('session-manager')
             sess_manager.save_autosave()
     
    -        if ok and not self._has_ssl_errors:
    +        if ok:
                 if self.url().scheme() == 'https':
    -                self._set_load_status(usertypes.LoadStatus.success_https)
    +                if self.url().host() in self._insecure_hosts:
    +                    self._set_load_status(usertypes.LoadStatus.warn)
    +                else:
    +                    self._set_load_status(usertypes.LoadStatus.success_https)
                 else:
                     self._set_load_status(usertypes.LoadStatus.success)
             elif ok:
    
  • qutebrowser/browser/webengine/webenginetab.py+2 2 modified
    @@ -1336,9 +1336,9 @@ def _on_load_finished(self, ok):
     
         @pyqtSlot(certificateerror.CertificateErrorWrapper)
         def _on_ssl_errors(self, error):
    -        self._has_ssl_errors = True
    -
             url = error.url()
    +        self._insecure_hosts.add(url.host())
    +
             log.webview.debug("Certificate error: {}".format(error))
     
             if error.is_overridable():
    
  • qutebrowser/browser/webkit/webkittab.py+3 3 modified
    @@ -818,9 +818,9 @@ def _on_navigation_request(self, navigation):
             if navigation.is_main_frame:
                 self.settings.update_for_url(navigation.url)
     
    -    @pyqtSlot()
    -    def _on_ssl_errors(self):
    -        self._has_ssl_errors = True
    +    @pyqtSlot('QNetworkReply*')
    +    def _on_ssl_errors(self, reply):
    +        self._insecure_hosts.add(reply.url().host())
     
         def _connect_signals(self):
             view = self._widget
    
2281a205c3e7

Security: Remember hosts with ignored cert errors for load status

https://github.com/qutebrowser/qutebrowserFlorian BruhinMay 2, 2020via ghsa
3 files changed · +17 9
  • qutebrowser/browser/browsertab.py+12 4 modified
    @@ -866,6 +866,13 @@ class AbstractTab(QWidget):
         # arg 1: The exit code.
         renderer_process_terminated = pyqtSignal(TerminationStatus, int)
     
    +    # Hosts for which a certificate error happened. Shared between all tabs.
    +    #
    +    # Note that we remember hosts here, without scheme/port:
    +    # QtWebEngine/Chromium also only remembers hostnames, and certificates are
    +    # for a given hostname anyways.
    +    _insecure_hosts = set()  # type: typing.Set[str]
    +
         def __init__(self, *, win_id: int, private: bool,
                      parent: QWidget = None) -> None:
             self.is_private = private
    @@ -883,7 +890,6 @@ def __init__(self, *, win_id: int, private: bool,
             self._layout = miscwidgets.WrapperLayout(self)
             self._widget = None  # type: typing.Optional[QWidget]
             self._progress = 0
    -        self._has_ssl_errors = False
             self._load_status = usertypes.LoadStatus.none
             self._tab_event_filter = eventfilter.TabEventFilter(
                 self, parent=self)
    @@ -971,7 +977,6 @@ def _on_url_changed(self, url: QUrl) -> None:
         @pyqtSlot()
         def _on_load_started(self) -> None:
             self._progress = 0
    -        self._has_ssl_errors = False
             self.data.viewing_source = False
             self._set_load_status(usertypes.LoadStatus.loading)
             self.load_started.emit()
    @@ -1031,9 +1036,12 @@ def _update_load_status(self, ok: bool) -> None:
             Needs to be called by subclasses to trigger a load status update, e.g.
             as a response to a loadFinished signal.
             """
    -        if ok and not self._has_ssl_errors:
    +        if ok:
                 if self.url().scheme() == 'https':
    -                self._set_load_status(usertypes.LoadStatus.success_https)
    +                if self.url().host() in self._insecure_hosts:
    +                    self._set_load_status(usertypes.LoadStatus.warn)
    +                else:
    +                    self._set_load_status(usertypes.LoadStatus.success_https)
                 else:
                     self._set_load_status(usertypes.LoadStatus.success)
             elif ok:
    
  • qutebrowser/browser/webengine/webenginetab.py+2 2 modified
    @@ -1423,9 +1423,9 @@ def _on_load_finished(self, ok: bool) -> None:
     
         @pyqtSlot(certificateerror.CertificateErrorWrapper)
         def _on_ssl_errors(self, error):
    -        self._has_ssl_errors = True
    -
             url = error.url()
    +        self._insecure_hosts.add(url.host())
    +
             log.webview.debug("Certificate error: {}".format(error))
     
             if error.is_overridable():
    
  • qutebrowser/browser/webkit/webkittab.py+3 3 modified
    @@ -851,9 +851,9 @@ def _on_navigation_request(self, navigation):
             if navigation.is_main_frame:
                 self.settings.update_for_url(navigation.url)
     
    -    @pyqtSlot()
    -    def _on_ssl_errors(self):
    -        self._has_ssl_errors = True
    +    @pyqtSlot('QNetworkReply*')
    +    def _on_ssl_errors(self, reply):
    +        self._insecure_hosts.add(reply.url().host())
     
         def _connect_signals(self):
             view = self._widget
    
19f01bb42d02

Security: Remember hosts with ignored cert errors for load status

https://github.com/qutebrowser/qutebrowserFlorian BruhinMay 2, 2020via ghsa
3 files changed · +17 9
  • qutebrowser/browser/browsertab.py+12 4 modified
    @@ -864,6 +864,13 @@ class AbstractTab(QWidget):
         # arg 1: The exit code.
         renderer_process_terminated = pyqtSignal(TerminationStatus, int)
     
    +    # Hosts for which a certificate error happened. Shared between all tabs.
    +    #
    +    # Note that we remember hosts here, without scheme/port:
    +    # QtWebEngine/Chromium also only remembers hostnames, and certificates are
    +    # for a given hostname anyways.
    +    _insecure_hosts = set()  # type: typing.Set[str]
    +
         def __init__(self, *, win_id: int, private: bool,
                      parent: QWidget = None) -> None:
             self.is_private = private
    @@ -881,7 +888,6 @@ def __init__(self, *, win_id: int, private: bool,
             self._layout = miscwidgets.WrapperLayout(self)
             self._widget = None  # type: typing.Optional[QWidget]
             self._progress = 0
    -        self._has_ssl_errors = False
             self._load_status = usertypes.LoadStatus.none
             self._mouse_event_filter = mouse.MouseEventFilter(
                 self, parent=self)
    @@ -969,7 +975,6 @@ def _on_url_changed(self, url: QUrl) -> None:
         @pyqtSlot()
         def _on_load_started(self) -> None:
             self._progress = 0
    -        self._has_ssl_errors = False
             self.data.viewing_source = False
             self._set_load_status(usertypes.LoadStatus.loading)
             self.load_started.emit()
    @@ -1029,9 +1034,12 @@ def _update_load_status(self, ok: bool) -> None:
             Needs to be called by subclasses to trigger a load status update, e.g.
             as a response to a loadFinished signal.
             """
    -        if ok and not self._has_ssl_errors:
    +        if ok:
                 if self.url().scheme() == 'https':
    -                self._set_load_status(usertypes.LoadStatus.success_https)
    +                if self.url().host() in self._insecure_hosts:
    +                    self._set_load_status(usertypes.LoadStatus.warn)
    +                else:
    +                    self._set_load_status(usertypes.LoadStatus.success_https)
                 else:
                     self._set_load_status(usertypes.LoadStatus.success)
             elif ok:
    
  • qutebrowser/browser/webengine/webenginetab.py+2 2 modified
    @@ -1395,9 +1395,9 @@ def _on_load_finished(self, ok: bool) -> None:
     
         @pyqtSlot(certificateerror.CertificateErrorWrapper)
         def _on_ssl_errors(self, error):
    -        self._has_ssl_errors = True
    -
             url = error.url()
    +        self._insecure_hosts.add(url.host())
    +
             log.webview.debug("Certificate error: {}".format(error))
     
             if error.is_overridable():
    
  • qutebrowser/browser/webkit/webkittab.py+3 3 modified
    @@ -851,9 +851,9 @@ def _on_navigation_request(self, navigation):
             if navigation.is_main_frame:
                 self.settings.update_for_url(navigation.url)
     
    -    @pyqtSlot()
    -    def _on_ssl_errors(self):
    -        self._has_ssl_errors = True
    +    @pyqtSlot('QNetworkReply*')
    +    def _on_ssl_errors(self, reply):
    +        self._insecure_hosts.add(reply.url().host())
     
         def _connect_signals(self):
             view = self._widget
    
4020210b193f

Security: Remember hosts with ignored cert errors for load status

https://github.com/qutebrowser/qutebrowserFlorian BruhinMay 2, 2020via ghsa
3 files changed · +17 9
  • qutebrowser/browser/browsertab.py+12 4 modified
    @@ -737,6 +737,13 @@ class AbstractTab(QWidget):
         renderer_process_terminated = pyqtSignal(TerminationStatus, int)
         predicted_navigation = pyqtSignal(QUrl)
     
    +    # Hosts for which a certificate error happened. Shared between all tabs.
    +    #
    +    # Note that we remember hosts here, without scheme/port:
    +    # QtWebEngine/Chromium also only remembers hostnames, and certificates are
    +    # for a given hostname anyways.
    +    _insecure_hosts = set()  # type: typing.Set[str]
    +
         def __init__(self, *, win_id, mode_manager, private, parent=None):
             self.private = private
             self.win_id = win_id
    @@ -753,7 +760,6 @@ def __init__(self, *, win_id, mode_manager, private, parent=None):
             self._layout = miscwidgets.WrapperLayout(self)
             self._widget = None
             self._progress = 0
    -        self._has_ssl_errors = False
             self._mode_manager = mode_manager
             self._load_status = usertypes.LoadStatus.none
             self._mouse_event_filter = mouse.MouseEventFilter(
    @@ -840,7 +846,6 @@ def _on_url_changed(self, url):
         @pyqtSlot()
         def _on_load_started(self):
             self._progress = 0
    -        self._has_ssl_errors = False
             self.data.viewing_source = False
             self._set_load_status(usertypes.LoadStatus.loading)
             self.load_started.emit()
    @@ -899,9 +904,12 @@ def _on_load_finished(self, ok):
             sess_manager = objreg.get('session-manager')
             sess_manager.save_autosave()
     
    -        if ok and not self._has_ssl_errors:
    +        if ok:
                 if self.url().scheme() == 'https':
    -                self._set_load_status(usertypes.LoadStatus.success_https)
    +                if self.url().host() in self._insecure_hosts:
    +                    self._set_load_status(usertypes.LoadStatus.warn)
    +                else:
    +                    self._set_load_status(usertypes.LoadStatus.success_https)
                 else:
                     self._set_load_status(usertypes.LoadStatus.success)
             elif ok:
    
  • qutebrowser/browser/webengine/webenginetab.py+2 2 modified
    @@ -1278,9 +1278,9 @@ def _on_load_finished(self, ok):
     
         @pyqtSlot(certificateerror.CertificateErrorWrapper)
         def _on_ssl_errors(self, error):
    -        self._has_ssl_errors = True
    -
             url = error.url()
    +        self._insecure_hosts.add(url.host())
    +
             log.webview.debug("Certificate error: {}".format(error))
     
             if error.is_overridable():
    
  • qutebrowser/browser/webkit/webkittab.py+3 3 modified
    @@ -808,9 +808,9 @@ def _on_navigation_request(self, navigation):
             if navigation.is_main_frame:
                 self.settings.update_for_url(navigation.url)
     
    -    @pyqtSlot()
    -    def _on_ssl_errors(self):
    -        self._has_ssl_errors = True
    +    @pyqtSlot('QNetworkReply*')
    +    def _on_ssl_errors(self, reply):
    +        self._insecure_hosts.add(reply.url().host())
     
         def _connect_signals(self):
             view = self._widget
    
021ab572a319

Security: Remember hosts with ignored cert errors for load status

https://github.com/qutebrowser/qutebrowserFlorian BruhinMay 2, 2020via ghsa
3 files changed · +17 9
  • qutebrowser/browser/browsertab.py+12 4 modified
    @@ -869,6 +869,13 @@ class AbstractTab(QWidget):
         # arg 1: The exit code.
         renderer_process_terminated = pyqtSignal(TerminationStatus, int)
     
    +    # Hosts for which a certificate error happened. Shared between all tabs.
    +    #
    +    # Note that we remember hosts here, without scheme/port:
    +    # QtWebEngine/Chromium also only remembers hostnames, and certificates are
    +    # for a given hostname anyways.
    +    _insecure_hosts = set()  # type: typing.Set[str]
    +
         def __init__(self, *, win_id: int, private: bool,
                      parent: QWidget = None) -> None:
             self.is_private = private
    @@ -886,7 +893,6 @@ def __init__(self, *, win_id: int, private: bool,
             self._layout = miscwidgets.WrapperLayout(self)
             self._widget = typing.cast(QWidget, None)
             self._progress = 0
    -        self._has_ssl_errors = False
             self._load_status = usertypes.LoadStatus.none
             self._tab_event_filter = eventfilter.TabEventFilter(
                 self, parent=self)
    @@ -973,7 +979,6 @@ def _on_url_changed(self, url: QUrl) -> None:
         @pyqtSlot()
         def _on_load_started(self) -> None:
             self._progress = 0
    -        self._has_ssl_errors = False
             self.data.viewing_source = False
             self._set_load_status(usertypes.LoadStatus.loading)
             self.load_started.emit()
    @@ -1032,9 +1037,12 @@ def _update_load_status(self, ok: bool) -> None:
             Needs to be called by subclasses to trigger a load status update, e.g.
             as a response to a loadFinished signal.
             """
    -        if ok and not self._has_ssl_errors:
    +        if ok:
                 if self.url().scheme() == 'https':
    -                self._set_load_status(usertypes.LoadStatus.success_https)
    +                if self.url().host() in self._insecure_hosts:
    +                    self._set_load_status(usertypes.LoadStatus.warn)
    +                else:
    +                    self._set_load_status(usertypes.LoadStatus.success_https)
                 else:
                     self._set_load_status(usertypes.LoadStatus.success)
             elif ok:
    
  • qutebrowser/browser/webengine/webenginetab.py+2 2 modified
    @@ -1549,9 +1549,9 @@ def _on_load_finished(self, ok: bool) -> None:
     
         @pyqtSlot(certificateerror.CertificateErrorWrapper)
         def _on_ssl_errors(self, error):
    -        self._has_ssl_errors = True
    -
             url = error.url()
    +        self._insecure_hosts.add(url.host())
    +
             log.webview.debug("Certificate error: {}".format(error))
     
             if error.is_overridable():
    
  • qutebrowser/browser/webkit/webkittab.py+3 3 modified
    @@ -849,9 +849,9 @@ def _on_navigation_request(self, navigation):
             if navigation.is_main_frame:
                 self.settings.update_for_url(navigation.url)
     
    -    @pyqtSlot()
    -    def _on_ssl_errors(self):
    -        self._has_ssl_errors = True
    +    @pyqtSlot('QNetworkReply*')
    +    def _on_ssl_errors(self, reply):
    +        self._insecure_hosts.add(reply.url().host())
     
         def _connect_signals(self):
             view = self._widget
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

21

News mentions

0

No linked articles in our index yet.