Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions ical/parsing/property.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@

# Characters that should be encoded in quotes
_UNSAFE_CHAR_RE = re.compile(r"[,:;]")
_RE_CONTROL_CHARS = re.compile("[\x00-\x08\x0a-\x1f\x7f]")
RE_CONTROL_CHARS = re.compile("[\x00-\x08\x0a-\x1f\x7f]")
_RE_NAME = re.compile("[A-Z0-9-]+")
_NAME_DELIMITERS = (";", ":")
_PARAM_DELIMITERS = (",", ";", ":")
Expand Down Expand Up @@ -242,14 +242,14 @@ def _parse_line(line: str) -> ParsedProperty:
f"Parameter value '{value}' for parameter '{param.name}' is improperly quoted",
detailed_error=line,
)
if _RE_CONTROL_CHARS.search(value):
if RE_CONTROL_CHARS.search(value):
raise CalendarParseError(
f"Invalid parameter value '{value}' for parameter '{param.name}'",
detailed_error=line,
)

property_value = line[pos:]
if _RE_CONTROL_CHARS.search(property_value):
if RE_CONTROL_CHARS.search(property_value):
raise CalendarParseError(
f"Property value contains control characters: {property_value}",
detailed_error=line,
Expand Down
3 changes: 2 additions & 1 deletion ical/types/text.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Library for parsing TEXT values."""

from ical.parsing.property import ParsedProperty
from ical.parsing.property import ParsedProperty, RE_CONTROL_CHARS

from .data_types import DATA_TYPE

Expand Down Expand Up @@ -32,4 +32,5 @@ def __encode_property_value__(cls, value: str) -> str:
if key not in value:
continue
value = value.replace(key, vin)
value = RE_CONTROL_CHARS.sub("", value)
return value
20 changes: 20 additions & 0 deletions tests/test_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -1910,3 +1910,23 @@
event4 = next(iter)
assert event4.recurrence_id == "20241026T043000"
assert event4.dtstart == datetime.datetime(2024, 10, 26, 4, 30, 0)


def test_control_characters(
calendar: Calendar,
store: EventStore,
fetch_events: Callable[..., list[dict[str, Any]]],
snapshot: SnapshotAssertion,
) -> None:
"""Test adding an event to the store and retrieval."""
event = Event(
summary="Hello, You\x08re seeing an invalid character",
start="2022-08-29T09:00:00",
end="2022-08-29T09:30:00",
)
store.add(event)
ics = IcsCalendarStream.calendar_to_ics(calendar)
new_calendar = IcsCalendarStream.calendar_from_ics(ics)
assert len(new_calendar.events) == 1
persisted_event = new_calendar.events[0]
assert persisted_event.summary == "Hello, Youre seeing an invalid character" # codespell:ignore Youre

Check failure on line 1932 in tests/test_store.py

View workflow job for this annotation

GitHub Actions / build

Youre ==> Your, You're

Check failure on line 1932 in tests/test_store.py

View workflow job for this annotation

GitHub Actions / build

Youre ==> Your, You're
14 changes: 14 additions & 0 deletions tests/types/test_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,17 @@ def test_text_from_obj() -> None:
"""Test text when creating from an object."""
model = Model.model_validate({"text_value": "some-value"})
assert model == Model(text_value="some-value")


def test_text_control_characters() -> None:
"""Test that control characters are stripped from text values."""
model = Model.model_validate({"text_value": "some\x01value"})
assert model.__encode_component_root__() == ParsedComponent(
name="Model",
properties=[
ParsedProperty(
name="text_value",
value="somevalue",
)
],
)