From 7335d3efb04be65c2ea27ee7f8187ce77436b47a Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 1 Jul 2020 15:24:25 +0200 Subject: configfiles: Handle invalid structure during migrations (cherry picked from commit 8d05f0282a271bfd45e614238bd1b555c58b3fc1) --- qutebrowser/config/configfiles.py | 29 ++++++++++++++++++++++------- tests/unit/config/test_configfiles.py | 12 ++++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/qutebrowser/config/configfiles.py b/qutebrowser/config/configfiles.py index f251ca3bd..df7f83163 100644 --- a/qutebrowser/config/configfiles.py +++ b/qutebrowser/config/configfiles.py @@ -224,7 +224,7 @@ class YamlConfig(QObject): migrations.changed.connect(self._mark_changed) migrations.migrate() - self._validate(settings) + self._validate_names(settings) self._build_values(settings) def _load_settings_object(self, yaml_data: typing.Any) -> '_SettingsType': @@ -272,7 +272,7 @@ class YamlConfig(QObject): if errors: raise configexc.ConfigFileErrors('autoconfig.yml', errors) - def _validate(self, settings: _SettingsType) -> None: + def _validate_names(self, settings: _SettingsType) -> None: """Make sure all settings exist.""" unknown = [] for name in settings: @@ -407,7 +407,10 @@ class YamlMigrations(QObject): def _migrate_font_replacements(self) -> None: """Replace 'monospace' replacements by 'default_family'.""" - for name in self._settings: + for name, values in self._settings.items(): + if not isinstance(values, dict): + continue + try: opt = configdata.DATA[name] except KeyError: @@ -416,7 +419,7 @@ class YamlMigrations(QObject): if not isinstance(opt.typ, configtypes.FontBase): continue - for scope, val in self._settings[name].items(): + for scope, val in values.items(): if isinstance(val, str) and val.endswith(' monospace'): new_val = val.replace('monospace', 'default_family') self._settings[name][scope] = new_val @@ -428,7 +431,11 @@ class YamlMigrations(QObject): if name not in self._settings: return - for scope, val in self._settings[name].items(): + values = self._settings[name] + if not isinstance(values, dict): + return + + for scope, val in values.items(): if isinstance(val, bool): new_value = true_value if val else false_value self._settings[name][scope] = new_value @@ -454,7 +461,11 @@ class YamlMigrations(QObject): if name not in self._settings: return - for scope, val in self._settings[name].items(): + values = self._settings[name] + if not isinstance(values, dict): + return + + for scope, val in values.items(): if val is None: self._settings[name][scope] = value self.changed.emit() @@ -478,7 +489,11 @@ class YamlMigrations(QObject): if name not in self._settings: return - for scope, val in self._settings[name].items(): + values = self._settings[name] + if not isinstance(values, dict): + return + + for scope, val in values.items(): if isinstance(val, str): new_val = re.sub(source, target, val) if new_val != val: diff --git a/tests/unit/config/test_configfiles.py b/tests/unit/config/test_configfiles.py index 3436d2cf4..de3ab34cf 100644 --- a/tests/unit/config/test_configfiles.py +++ b/tests/unit/config/test_configfiles.py @@ -330,6 +330,18 @@ class TestYaml: assert str(error.exception).splitlines()[0] == exception assert error.traceback is None + @pytest.mark.parametrize('value', [ + 42, # value is not a dict + {'https://': True}, # Invalid pattern + {True: True}, # No string pattern + ]) + def test_invalid_in_migrations(self, value, yaml, autoconfig): + """Make sure migrations work fine with an invalid structure.""" + config = {key: value for key in configdata.DATA} + autoconfig.write(config) + with pytest.raises(configexc.ConfigFileErrors): + yaml.load() + def test_legacy_migration(self, yaml, autoconfig, qtbot): autoconfig.write_toplevel({ 'config_version': 1, -- cgit v1.2.3-54-g00ecf