diff --git a/CVE-2023-26112.patch b/CVE-2023-26112.patch index 209cce7..fa21013 100644 --- a/CVE-2023-26112.patch +++ b/CVE-2023-26112.patch @@ -8,7 +8,7 @@ Subject: [PATCH] Address CVE-2023-26112 ReDoS src/tests/test_validate_errors.py | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) -diff --git a/validate.py b/validate.py +diff --git a/src/configobj/validate.py b/src/configobj/validate.py index 9267a3f..98d879f 100644 --- a/src/configobj/validate.py +++ b/src/configobj/validate.py @@ -21,4 +21,28 @@ index 9267a3f..98d879f 100644 # this regex takes apart keyword arguments _key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$', re.DOTALL) - +diff --git a/src/tests/test_validate_errors.py b/src/tests/test_validate_errors.py +index 399daa8..f7d6c27 100644 +--- a/src/tests/test_validate_errors.py ++++ b/src/tests/test_validate_errors.py +@@ -3,7 +3,7 @@ + import pytest + + from configobj import ConfigObj, get_extra_values, ParseError, NestingError +-from configobj.validate import Validator ++from configobj.validate import Validator, VdtUnknownCheckError + + @pytest.fixture() + def thisdir(): +@@ -77,3 +77,11 @@ def test_no_parent(tmpdir, specpath): + ini.write('[[haha]]') + with pytest.raises(NestingError): + conf = ConfigObj(str(ini), configspec=specpath, file_error=True) ++ ++ ++def test_re_dos(val): ++ value = "aaa" ++ i = 165100 ++ attack = '\x00'*i + ')' + '('*i ++ with pytest.raises(VdtUnknownCheckError): ++ val.check(attack, value) diff --git a/configobj-5.0.8-gh.tar.gz b/configobj-5.0.8-gh.tar.gz new file mode 100644 index 0000000..0a7bef2 --- /dev/null +++ b/configobj-5.0.8-gh.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:547dc047e31c71d7a8732016336769ed450588f34a7c13077aa7acc7df245eda +size 99071 diff --git a/configobj-5.0.8.tar.gz b/configobj-5.0.8.tar.gz deleted file mode 100644 index 3222fd4..0000000 --- a/configobj-5.0.8.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6f704434a07dc4f4dc7c9a745172c1cad449feb548febd9f7fe362629c627a97 -size 38012 diff --git a/python-configobj.changes b/python-configobj.changes index c797231..fd426e5 100644 --- a/python-configobj.changes +++ b/python-configobj.changes @@ -1,3 +1,23 @@ +------------------------------------------------------------------- +Wed Sep 11 12:08:59 UTC 2024 - Matej Cepl + +- Refresh CVE-2023-26112.patch according to the last state of + gh#DiffSK/configobj!236. + +------------------------------------------------------------------- +Wed Jan 3 16:47:32 UTC 2024 - Ben Greiner + +- Remove six from the rpm requirements. Why would we have + remove_six.patch in the first place if we still require it? +- Enable unit tests + +------------------------------------------------------------------- +Wed Jan 3 09:46:30 UTC 2024 - Dirk Müller + +- require setuptools +- remove python2 logic, this makes no sense after + "remove_six.patch" + ------------------------------------------------------------------- Wed Jul 5 07:29:38 UTC 2023 - Matej Cepl @@ -31,7 +51,7 @@ Thu Apr 13 22:40:36 UTC 2023 - Matej Cepl ------------------------------------------------------------------- Mon Jan 16 21:16:58 UTC 2023 - Dirk Müller -- require setuptools +- require setuptools ------------------------------------------------------------------- Tue Dec 4 12:46:48 UTC 2018 - Matej Cepl diff --git a/python-configobj.spec b/python-configobj.spec index 254ec27..e6b01b4 100644 --- a/python-configobj.spec +++ b/python-configobj.spec @@ -1,7 +1,7 @@ # # spec file for package python-configobj # -# Copyright (c) 2023 SUSE LLC +# Copyright (c) 2024 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -16,7 +16,6 @@ # -%define oldpython python %{?sle15_python_module_pythons} Name: python-configobj Version: 5.0.8 @@ -25,23 +24,22 @@ Summary: Config file reading, writing and validation License: BSD-3-Clause Group: Development/Languages/Python URL: https://github.com/DiffSK/configobj -Source: https://files.pythonhosted.org/packages/source/c/configobj/configobj-%{version}.tar.gz +# No tests in PyPI sdist +Source: https://github.com/DiffSK/configobj/archive/refs/tags/v%{version}.tar.gz#/configobj-%{version}-gh.tar.gz # PATCH-FIX-UPSTREAM https://github.com/DiffSK/configobj/pull/236 Address CVE-2023-26112 ReDoS Patch0: CVE-2023-26112.patch # PATCH-FIX-UPSTREAM remove_six.patch gh#DiffSK/configobj#239 mcepl@suse.com # We don't need six anymore Patch1: remove_six.patch BuildRequires: %{python_module pip} +BuildRequires: %{python_module setuptools} BuildRequires: %{python_module wheel} BuildRequires: fdupes BuildRequires: python-rpm-macros -Requires: python-six BuildArch: noarch -# There are no real docs! -%ifpython2 -Obsoletes: %{oldpython}-configobj-docs < %{version} -Provides: %{oldpython}-configobj-docs = %{version} -%endif +# SECTION test +BuildRequires: %{python_module pytest} +# /SECTION %python_subpackages %description @@ -73,9 +71,12 @@ It has lots of other features though: %pyproject_install %python_expand %fdupes %{buildroot}%{$python_sitelib} +%check +%pytest + %files %{python_files} %{python_sitelib}/configobj %{python_sitelib}/validate -%{python_sitelib}/configobj-%{version}*-info +%{python_sitelib}/configobj-%{version}.dist-info %changelog diff --git a/remove_six.patch b/remove_six.patch index 306baf7..4a64c53 100644 --- a/remove_six.patch +++ b/remove_six.patch @@ -1,11 +1,9 @@ ---- - setup.py | 1 - src/configobj.egg-info/requires.txt | 1 - src/configobj/__init__.py | 49 ++++++++++++++++-------------------- - 3 files changed, 22 insertions(+), 29 deletions(-) +https://github.com/DiffSK/configobj/issues/239 ---- a/setup.py -+++ b/setup.py +Index: configobj-5.0.8/setup.py +=================================================================== +--- configobj-5.0.8.orig/setup.py ++++ configobj-5.0.8/setup.py @@ -41,7 +41,6 @@ DESCRIPTION = 'Config file reading, writ URL = 'https://github.com/DiffSK/configobj' @@ -14,12 +12,10 @@ """ VERSION = '' ---- a/src/configobj.egg-info/requires.txt -+++ b/src/configobj.egg-info/requires.txt -@@ -1 +0,0 @@ --six ---- a/src/configobj/__init__.py -+++ b/src/configobj/__init__.py +Index: configobj-5.0.8/src/configobj/__init__.py +=================================================================== +--- configobj-5.0.8.orig/src/configobj/__init__.py ++++ configobj-5.0.8/src/configobj/__init__.py @@ -19,7 +19,6 @@ import sys from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE @@ -211,3 +207,173 @@ raise ReloadError() filename = self.filename +Index: configobj-5.0.8/src/tests/test_configobj.py +=================================================================== +--- configobj-5.0.8.orig/src/tests/test_configobj.py ++++ configobj-5.0.8/src/tests/test_configobj.py +@@ -1,5 +1,6 @@ + # coding=utf-8 + from __future__ import unicode_literals ++import io + import os + import re + +@@ -8,7 +9,6 @@ from warnings import catch_warnings + from tempfile import NamedTemporaryFile + + import pytest +-import six + + import configobj as co + from configobj import ConfigObj, flatten_errors, ReloadError, DuplicateError, MissingInterpolationOption, InterpolationLoopError, ConfigObjError +@@ -36,13 +36,13 @@ def cfg_lines(config_string_representati + '{!r}'.format(config_string_representation)) + + first_content = lines[line_no_with_content] +- if isinstance(first_content, six.binary_type): ++ if isinstance(first_content, bytes): + first_content = first_content.decode('utf-8') + ws_chars = len(re.search('^(\s*)', first_content).group(1)) + + def yield_stringified_line(): + for line in lines: +- if isinstance(line, six.binary_type): ++ if isinstance(line, bytes): + yield line.decode('utf-8') + else: + yield line +@@ -70,7 +70,7 @@ def cfg_contents(request): + + with NamedTemporaryFile(delete=False, mode='wb') as cfg_file: + for line in lines: +- if isinstance(line, six.binary_type): ++ if isinstance(line, bytes): + cfg_file.write(line + os.linesep.encode('utf-8')) + else: + cfg_file.write((line + os.linesep).encode('utf-8')) +@@ -186,11 +186,7 @@ class TestEncoding(object): + + c = ConfigObj(cfg, encoding='utf8') + +- if six.PY2: +- assert not isinstance(c['test'], str) +- assert isinstance(c['test'], unicode) +- else: +- assert isinstance(c['test'], str) ++ assert isinstance(c['test'], str) + + + #issue #18 +@@ -198,11 +194,7 @@ class TestEncoding(object): + cfg = cfg_contents(b"test = some string") + + c = ConfigObj(cfg) +- if six.PY2: +- assert isinstance(c['test'], str) +- assert not isinstance(c['test'], unicode) +- else: +- assert isinstance(c['test'], str) ++ assert isinstance(c['test'], str) + + #issue #44 + def test_that_encoding_using_list_of_strings(self): +@@ -210,11 +202,7 @@ class TestEncoding(object): + + c = ConfigObj(cfg, encoding='utf8') + +- if six.PY2: +- assert isinstance(c['test'], unicode) +- assert not isinstance(c['test'], str) +- else: +- assert isinstance(c['test'], str) ++ assert isinstance(c['test'], str) + + assert c['test'] == '\U0001f41c' + +@@ -223,7 +211,7 @@ class TestEncoding(object): + c = cfg_contents(ant_cfg) + cfg = ConfigObj(c, encoding='utf-8') + +- assert isinstance(cfg['tags']['bug']['translated'], six.text_type) ++ assert isinstance(cfg['tags']['bug']['translated'], str) + + #issue #44 and #55 + def test_encoding_in_config_files(self, request, ant_cfg): +@@ -233,7 +221,7 @@ class TestEncoding(object): + request.addfinalizer(lambda : os.unlink(cfg_file.name)) + + cfg = ConfigObj(cfg_file.name, encoding='utf-8') +- assert isinstance(cfg['tags']['bug']['translated'], six.text_type) ++ assert isinstance(cfg['tags']['bug']['translated'], str) + cfg.write() + + @pytest.fixture +@@ -500,7 +488,7 @@ def test_unicode_handling(): + 'section': {'test': 'test', 'test2': 'test2'}} + uc = ConfigObj(u, encoding='utf_8', default_encoding='latin-1') + assert uc.BOM +- assert isinstance(uc['test1'], six.text_type) ++ assert isinstance(uc['test1'], str) + assert uc.encoding == 'utf_8' + assert uc.newlines == '\n' + assert len(uc.write()) == 13 +@@ -508,14 +496,14 @@ def test_unicode_handling(): + a_list = uc.write() + assert 'latin1' in str(a_list) + assert len(a_list) == 14 +- assert isinstance(a_list[0], six.binary_type) ++ assert isinstance(a_list[0], bytes) + assert a_list[0].startswith(BOM_UTF8) + + u = u_base.replace('\n', '\r\n').encode('utf-8').splitlines(True) + uc = ConfigObj(u) + assert uc.newlines == '\r\n' + uc.newlines = '\r' +- file_like = six.BytesIO() ++ file_like = io.BytesIO() + uc.write(file_like) + file_like.seek(0) + uc2 = ConfigObj(file_like) +@@ -723,7 +711,7 @@ class TestSectionBehavior(object): + val = section[key] + newkey = key.replace('XXXX', 'CLIENT1') + section.rename(key, newkey) +- if isinstance(val, six.string_types): ++ if isinstance(val, str): + val = val.replace('XXXX', 'CLIENT1') + section[newkey] = val + +@@ -811,7 +799,7 @@ class TestReloading(object): + return content + + def test_handle_no_filename(self): +- for bad_args in ([six.BytesIO()], [], [[]]): ++ for bad_args in ([io.BytesIO()], [], [[]]): + cfg = ConfigObj(*bad_args) + with pytest.raises(ReloadError) as excinfo: + cfg.reload() +@@ -1264,21 +1252,21 @@ class TestEdgeCasesWhenWritingOut(object + def test_newline_terminated(self, empty_cfg): + empty_cfg.newlines = '\n' + empty_cfg['a'] = 'b' +- collector = six.BytesIO() ++ collector = io.BytesIO() + empty_cfg.write(collector) + assert collector.getvalue() == b'a = b\n' + + def test_hash_escaping(self, empty_cfg): + empty_cfg.newlines = '\n' + empty_cfg['#a'] = 'b # something' +- collector = six.BytesIO() ++ collector = io.BytesIO() + empty_cfg.write(collector) + assert collector.getvalue() == b'"#a" = "b # something"\n' + + empty_cfg = ConfigObj() + empty_cfg.newlines = '\n' + empty_cfg['a'] = 'b # something', 'c # something' +- collector = six.BytesIO() ++ collector = io.BytesIO() + empty_cfg.write(collector) + assert collector.getvalue() == b'a = "b # something", "c # something"\n' +