github pixari/OpenTodo v0.14.1
OpenTodo v0.14.1

latest release: v0.14.2
2 hours ago

OpenTodo 0.14.1 is a security release; please upgrade. It fixes the findings of an internal security audit: a shared project could take over another person's private task if its id was known, a saved view or a crafted email or calendar could slow down or crash the server, and an old session could add a new way to sign in without signing in again. It also makes the sync clock follow newer edits, so a device whose clock runs behind no longer loses its own changes, and fixes a long task scrolling the whole page in the side panel.

Upgrade notes

  • Replace the image or the binary and restart. Upgrade the server before the devices, as for every release.
  • Scripts and API clients may need a different token: changing the owner's webhook, maps, MCP and audit settings now needs an admin token, the email-to-task routes refuse read and agent tokens, and adding a passkey, two-factor authentication or a single sign-on link needs a recent sign-in. (HTTP API)
  • One migration (0048_caldav_resources_per_account): the names calendar apps chose for tasks are now kept per account; each existing name is kept for every account that can see its task. No data is rewritten otherwise and no environment variable changes. Replace the image or the binary and restart; there are no down-migrations, so back up first.
  • Ids that two accounts already share stay as they are. Earlier versions could store one id in two places (through the problem fixed below, or when two accounts imported the same export file). The server now reads and writes the oldest copy each person can see, so nothing is lost and nothing needs repair. To check whether your database has such ids, run SELECT entity, entity_id, group_concat(DISTINCT scope_id) FROM scope_fields WHERE entity IN ('project','task','section','field','comment','attachment','time_entry','rule') GROUP BY entity, entity_id HAVING COUNT(DISTINCT scope_id) > 1; with sqlite3 on a copy of <data>/opentodo.db.

Added

  • n8n nodes. A separate package, installed from npm as n8n-nodes-opentodo once it is released (Settings → Community nodes in a self-hosted n8n), adds an OpenTodo node that creates, reads, changes, completes and deletes tasks, with quick-add syntax if you like, and manages projects, labels and comments, also as a tool for n8n's AI Agent. An OpenTodo Trigger starts a workflow when tasks or projects change: it sets up a webhook in your account, checks every delivery's signature and removes the webhook when the workflow is turned off. It needs an API token and no change on the server. (n8n)

Security

  • A shared project can no longer take over a private task. Someone who shared a project with you and knew the id of one of your private tasks (ids appear in task pages, public links, exports and calendar apps) could create a task with that id in the shared project. Your later edits to the private task then reached that project's members, and they could delete it. Every task, project, section, field, comment, attachment, time entry and automation rule id now names one item on the server: such a write is refused like any other write you may not make, and it reveals nothing about your item. Found in an internal audit. (Sync protocol)
  • Calendar apps of different accounts no longer share resource names. One account's calendar app could rename or drop the name another account's app had given a task they both see, which made items vanish or appear twice in the other person's calendar. Found in an internal audit. (CalDAV)
  • A project can only join your own workspaces. Anyone could mark one of their projects as part of a workspace they did not belong to, which stopped the workspace's owner from deleting it. Such a change is now refused, and values written before are ignored. Found in an internal audit.
  • Adding a way to sign in needs a recent sign-in (found in an internal audit). Adding a passkey, setting up an authenticator app or linking a single sign-on account now asks for your password or a code when you last signed in more than ten minutes ago, like creating an API token already did, and doing one of them no longer counts as a recent sign-in. Before, someone with access to a long-open session could add their own passkey, authenticator app or provider account and keep access after a password change. Scripts that enrol with an admin token are unaffected. (Account security)
  • A saved view can no longer take the server down. A signed-in person, including a visitor of a public demo, could save a view with a specially crafted filter query that crashed the server while checking it, stopping it for everyone (on a hosted server, for every tenant). Filter queries are now limited to 4,096 characters and 64 levels of parentheses and !, in the app and on the server; a longer or deeper query is refused with a message like any other mistake in a query. Queries people write, and every query the filter builder makes, are far below both limits. Found in an internal audit. (Filters)
  • Importing a Things database can no longer tie up the server. A specially prepared file uploaded for a Things import could make the server compute values while reading it, keeping a processor busy or using a lot of memory for each preview. The importer now refuses a file whose Things tables compute their values (Things' own databases never do) and any value over 1 MB, before or while reading it; real Things databases import as before. Found in an internal audit. (Import)
  • An import can no longer reach another account's attached file. Importing a ZIP export that named the id of a file attached in someone else's account could link that file into the importing account, letting it be downloaded there. An import now stores each file itself and refuses one whose id already belongs to a stored file the importing account does not have; the row is listed among the import's errors. Found in an internal audit. (Import)
  • Reading your email-to-task settings no longer creates an address. Before, simply opening the settings (or any token, even a read-only one, reading them) created an active address that accepts mail from anyone. Now the address is created when you turn email to task on, and read-only and assistant tokens can neither see nor create it. Addresses that already exist keep working. (Email to task)
  • The owner's security settings need an admin token. Changing whether webhooks may reach private networks, the map providers, the AI assistant (MCP) switches or audit log retention now takes a signed-in session or an admin API token; a full token is refused, as for the other security settings. (API)
  • Adding people to projects and workspaces by email is limited to 30 attempts per account per hour, so the share dialog cannot be used to find out which accounts exist on the server or to push projects at many people, and each person added to a project is recorded in the audit log as project.member_added. (Sharing)
  • A change made on a device whose clock runs slightly behind no longer loses to the older value it replaces. Devices, ot vault and the server now move their clock past every change they receive, so the next edit is always stamped after the value it overwrites. Before, an edit made shortly after receiving a value from a device or server whose clock was a few seconds ahead (too little for the clock correction to catch) could be silently undone by that older value. With end-to-end encryption this could leave a value, such as a task's origin written by the email inbox, in plaintext on the server, because the device's encrypted rewrite lost to it and was never retried. Found in an internal audit. (Sync protocol)
  • An email can no longer stall email to task. Every message in the mailbox is read before it is matched to an account, and the HTML of a specially written message (tags that never end, or links, marks and lists nested thousands deep) took time or memory that grew with the square of its size: a few hundred kilobytes kept the mail poller busy for minutes, a 10 MB message for hours. Reading HTML now takes time in proportion to its size, nesting deeper than 32 levels is flattened, and the text of a tag that never ends is kept as text. Found by fuzzing. (Email to task)
  • A calendar server can no longer tie up calendar sync or subscriptions. A multistatus answer whose text came in many small pieces between comments, or a DURATION with a very long run of digits, took time that grew with the square of its size. Both are linear now, a duration over nine digits per unit or longer than a century is refused, and a huge one no longer wraps around to a negative length. The same applies to CalDAV requests from your own apps. Found by fuzzing. (Calendar feeds, Calendar sync)

Fixed

  • Names with accents or non-Latin letters are no longer cut in the middle of a character. API token, passkey, app password and single sign-on names, sign-up names and the device names recorded for sessions are kept within their length limits by bytes; a limit falling inside a character such as "ë" or "日" stored half of it, which then showed as "�". They are now cut before that character, and the same applies to error messages from push services, mail servers and calendar feeds. Found in an internal audit.
  • A database read that fails part way is no longer taken as complete. The cleanup of notification channel secrets, at start and with the regular maintenance, could remove the tokens of channels that still exist when reading the channel list stopped early with an error; it now removes nothing then. A Things import that hits a read error stops with the error instead of importing part of the database, and the webhook and notification channel workers retry instead of sending a partial batch. Found in an internal audit.
  • ot vault no longer changes tasks by rewriting a file. In some files the vault wrote a line that read back differently, so the next sync sent a change to the server: a task's location, origin or custom field holding a bracket or a line break, a title that began like a field ([note::) or ended in a marker such as 📅, a recurrence directly followed by another field, unusual spaces between words, a top-level task indented by a space (prose after it became its note), a note line of only spaces, prose starting with ---, and old Mac line ends. A stray ] in unknown text is now written as \]. Each of these now reads back exactly as written. Found by fuzzing. (Vault)
  • A long task in the side panel no longer scrolls the whole page. With a long note, the window got a second scroll bar and the page moved instead of the panel, pushing the comment box out of view. Only the panel scrolls now. (The task panel)

Install

  • Docker: docker run -d -p 8080:8080 -v opentodo:/data ghcr.io/pixari/opentodo:0.14.1
  • Binary: download opentodo-<os>-<arch> below, then run it (data goes to ./data).
  • Command-line client: ot-<os>-<arch>. Browser extension: opentodo-browser-extension-chrome.zip and opentodo-browser-extension-firefox.zip.

Verify downloads with sha256sum -c SHA256SUMS. Self-hosting guides: docs/self-hosting.

Don't miss a new OpenTodo release

NewReleases is sending notifications on new releases.