Skip to content

escape control characters in the cli event summary - #2025

Open
alhudz wants to merge 2 commits into
PyCalendar:mainfrom
alhudz:cli-escape-control-chars
Open

alhudz wants to merge 2 commits into
PyCalendar:mainfrom
alhudz:cli-escape-control-chars

Conversation

@alhudz

@alhudz alhudz commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

Linked issue

  • Closes #ISSUE_NUMBER

Description

icalendar.cli.view() puts ORGANIZER and ATTENDEE values into the printed summary as they are. vText strips C0 controls from SUMMARY, LOCATION, COMMENT and DESCRIPTION, but vCalAddress keeps them, and vText keeps C1 controls such as U+009B. So running icalendar invite.ics on a received file lets the file drive the terminal.

Repro: printf 'BEGIN:VEVENT\r\nORGANIZER:mailto:boss\x1b]0;pwned\x07\x1b[1A\x1b[2K@example.com\r\nDTSTART:20250101T120000Z\r\nEND:VEVENT\r\n' > esc.ics && icalendar esc.ics
Expected: the escape bytes are shown as text.
Actual: the terminal title changes to pwned and the line above the organiser is erased.
Fix: view() now escapes C0 controls other than tab and newline, DEL and C1 controls as \xNN, so stdout and --output files both get plain text. Tab and newline stay as they are, so multi-line descriptions look the same.

Checklist

  • I added a change log entry, following the instructions in all subsections under Change log requirements.
  • I followed icalendar's Artificial intelligence policy and disclosed my Responsible AI use in my commit messages, if applicable.
  • I added or updated tests, if applicable.
  • I ran and ensured all tests pass locally by following Run tests.
  • I added or edited documentation as necessary, both as docstrings to be rendered in the API documentation and narrative documentation, following the Style guide.

Additional information

test_control_characters_are_escaped fails on main and passes with this change. Full suite: 21437 passed. ruff check and ruff format --check are clean.

view() printed ORGANIZER and ATTENDEE values verbatim, so an .ics
file previewed with the icalendar command could send terminal escape
sequences. Escape C0 controls other than tab and newline, DEL and C1
controls as \xNN in the returned text.

AI: Claude Opus 5.5 (claude-opus-5-5)
Prompt: Find a security bug in icalendar, write a minimal fix with a
regression test, and draft the pull request.
Output: The model found that the cli prints vCalAddress values raw,
reproduced it with an .ics containing ESC/OSC sequences, and wrote
this commit, which I reviewed.
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for the pull request, @alhudz! A maintainer will review it shortly.

@github-actions github-actions Bot added the ai-suspicion This contribution is possibly created with lots of AI help without enough human understanding. label Oct 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

One or more PR-quality checks did not pass. Review the PR Quality Checks summary in the workflow run for the failed checks and their details, then make any necessary updates.

Checklist items may remain unchecked while work is in progress. Please tick each item as you complete it.

If you used AI assistance, follow the project's Responsible AI use policy and provide the required disclosures.

@angatha angatha left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Tests pick just some values but I think we should test that all bad ones are always escape to prevent regressions.

output += cli.view(event) + "\n\n"
assert output == PROPER_OUTPUT

def test_control_characters_are_escaped(self):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you change this (or add anew test) with parameterrs for each "bad" character so we can detect if one of thouse is no longer escaped?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added test_each_control_character_is_escaped, parametrised over every C0 control except tab and newline, DEL and every C1 control (63 cases, ids are the hex code). It sets ORGANIZER as a plain string so \r reaches view() too, since the parser treats it as a line break. All 63 fail on main and pass with the fix. I kept the original test as an end-to-end check through from_ical.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whats the reason for excluding thouse three?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only two are excluded, tab (0x09) and newline (0x0a). DEL (0x7f) is in the list and escaped, my earlier wording was ambiguous there.

_escape_control_chars() runs over the whole formatted block, and view() uses \n for its own layout (one attendee per line, indented description lines), so escaping it would put everything on one line. DESCRIPTION also keeps real newlines, which test_output_is_proper checks. Neither of the two can move the cursor back or start an escape sequence, so they can't rewrite what is already on screen. \r is escaped for that reason: it returns to the start of the line and the rest overwrites it.

@niccokunzmann

Copy link
Copy Markdown
Member

Hm. Personally, I think the CLI is really old - it can be improved. I wonder if anyone uses it. It is for people to look at files.

It should probably use parse.

@alhudz

alhudz commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Agreed, it could do with some work. If you mean icalendar.parse from #1917 (#1977 is adding it), that only changes how main() reads the file. The escaping is in view(), so jCal and xCal input would go through it as well once the CLI switches over. I'd leave the switch to that work rather than overlap with #1977.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-suspicion This contribution is possibly created with lots of AI help without enough human understanding.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants