Skip to content

Correct ICS text escaping per RFC 5545#941

Merged
GaryJones merged 1 commit intodevelopfrom
GaryJones/fix-ics-description-escaping
Apr 23, 2026
Merged

Correct ICS text escaping per RFC 5545#941
GaryJones merged 1 commit intodevelopfrom
GaryJones/fix-ics-description-escaping

Conversation

@GaryJones
Copy link
Copy Markdown
Contributor

Summary

The calendar module's ICS subscription feed had three long-standing escaping bugs that could produce invalid .ics output for any event text containing common punctuation or user-supplied content.

do_ics_escaping() replaced semicolons with backslash-colon rather than the RFC 5545 backslash-semicolon sequence, so any event text containing a semicolon produced an invalid escape. The same function escaped backslashes as its final step, which meant the backslashes it had just inserted whilst escaping commas and semicolons were themselves doubled on the second pass, corrupting the output for any string that contained those characters. Newlines were not escaped at all, so a stray LF in a post title, taxonomy term or metadata field would terminate the line and break every calendar client's parse of the remainder of the feed.

Two call sites in handle_ics_subscription() also bypassed the escaper entirely. The SUMMARY field concatenated the post status label directly, and the DESCRIPTION field concatenated the label and value of every information field without passing them through. Since taxonomy term names, custom status labels, and filter-supplied field values can all contain any of the special characters, the only safe option is to escape them.

The fix reorders do_ics_escaping() to match RFC 5545 section 3.3.11 (escape backslashes first, then newlines, then semicolons, then commas) and routes the previously-unescaped call sites through it. New tests pin the escaper's behaviour across each character class and their combination so any future regression is caught immediately.

Test plan

  • composer test:integration — the new CalendarIcsEscapingTest cases pass
  • Subscribe to a calendar that includes a post whose title contains a comma, semicolon, backslash or newline, and verify the resulting .ics feed parses cleanly in a calendar client

The calendar's ICS subscription feed had three related issues with text
escaping. do_ics_escaping replaced semicolons with backslash-colon
instead of backslash-semicolon, so any event text containing a
semicolon produced an invalid escape sequence. The same function
escaped backslashes last, meaning the backslashes it had just inserted
when escaping commas and semicolons were themselves doubled, producing
corrupt output for any string containing those characters. Newlines
were not escaped at all, so a stray LF in a post title or taxonomy
term would terminate the line and break every calendar client's parse
of the remainder of the feed.

The DESCRIPTION field also concatenated field labels and values
directly into the feed without passing them through the escaper, and
the SUMMARY field did the same for the post status label. Taxonomy
term names and custom status labels are user-editable, so a comma,
semicolon, backslash or newline in either would corrupt the event.

Fixing the escaper to follow RFC 5545 section 3.3.11 (escape
backslashes first, then newlines, then the separator characters) and
routing every variable field through it closes all three paths with
the minimum number of changes. New tests pin the escaper's behaviour
across each escapable character and their combination.
@GaryJones GaryJones requested a review from a team as a code owner April 23, 2026 23:24
@GaryJones GaryJones self-assigned this Apr 23, 2026
@GaryJones GaryJones added this to the Next milestone Apr 23, 2026
@GaryJones GaryJones merged commit 2066e29 into develop Apr 23, 2026
10 checks passed
@GaryJones GaryJones deleted the GaryJones/fix-ics-description-escaping branch April 23, 2026 23:37
@GaryJones GaryJones mentioned this pull request Apr 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant