From 7a387b8efe1292fb0eebb4a654bcf014ae176c03 Mon Sep 17 00:00:00 2001 From: Jason Cox Date: Wed, 13 Dec 2023 10:21:24 -0500 Subject: [PATCH] Require matching BEGIN and END lines in vobjects Raise an error when parsing a vobject that has mismatched `BEGIN` and `END` lines (e.g., `BEGIN:FOO` followed by `END:BAR`) or missing `END` lines (e.g., `BEGIN:FOO` with no subsequent `END:FOO`). Fixes #1102. --- AUTHORS.rst | 1 + CHANGELOG.rst | 1 + tests/unit/utils/test_vobject.py | 25 +++++++++++++++++++++++++ vdirsyncer/vobject.py | 11 +++++++++++ 4 files changed, 38 insertions(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index a298bb8..140936c 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -9,6 +9,7 @@ In alphabetical order: - Corey Hinshaw - Kai Herlemann - Hugo Osvaldo Barrera +- Jason Cox - Julian Mehne - Malte Kiefer - Marek Marczykowski-Górecki diff --git a/CHANGELOG.rst b/CHANGELOG.rst index da711e8..815f8e7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,7 @@ Version 0.19.3 - Added a no_delete option to the storage configuration. :gh:`1090` - Fix crash when running ``vdirsyncer repair`` on a collection. :gh:`1019` - Add an option to request vCard v4.0. :gh:`1066` +- Require matching ``BEGIN`` and ``END`` lines in vobjects. :gh:`1103` Version 0.19.2 ============== diff --git a/tests/unit/utils/test_vobject.py b/tests/unit/utils/test_vobject.py index 3745411..150c92f 100644 --- a/tests/unit/utils/test_vobject.py +++ b/tests/unit/utils/test_vobject.py @@ -237,6 +237,31 @@ def test_broken_item(): assert item.parsed is None +def test_mismatched_end(): + with pytest.raises(ValueError) as excinfo: + vobject._Component.parse( + [ + "BEGIN:FOO", + "END:BAR", + ] + ) + + assert "Got END:BAR, expected END:FOO at line 2" in str(excinfo.value) + + +def test_missing_end(): + with pytest.raises(ValueError) as excinfo: + vobject._Component.parse( + [ + "BEGIN:FOO", + "BEGIN:BAR", + "END:BAR", + ] + ) + + assert "Missing END for component(s): FOO" in str(excinfo.value) + + def test_multiple_items(): with pytest.raises(ValueError) as excinfo: vobject._Component.parse( diff --git a/vdirsyncer/vobject.py b/vdirsyncer/vobject.py index d53c75b..aae9fb9 100644 --- a/vdirsyncer/vobject.py +++ b/vdirsyncer/vobject.py @@ -281,6 +281,12 @@ class _Component: stack.append(cls(c_name, [], [])) elif line.startswith("END:"): component = stack.pop() + c_name = line[len("END:") :].strip().upper() + if c_name != component.name: + raise ValueError( + f"Got END:{c_name}, expected END:{component.name}" + + f" at line {_i + 1}" + ) if stack: stack[-1].subcomponents.append(component) else: @@ -291,6 +297,11 @@ class _Component: except IndexError: raise ValueError(f"Parsing error at line {_i + 1}") + if len(stack) > 0: + raise ValueError( + f"Missing END for component(s): {', '.join(c.name for c in stack)}" + ) + if multiple: return rv elif len(rv) != 1: