{"id":"CVE-2026-70489","aliases":["GHSA-73cq-mcgh-379c","PYSEC-2026-3644"],"url":"https://o3.security/vulnerability/CVE-2026-70489","summary":"Open WebUI: Instance-wide stall via automation recurrence rules that force multi-second parsing","details":"## Summary\nIn every affected release, automation recurrence parsing anchors minutely and hourly rules at a fixed date of 2000-01-01 and then walks forward one interval at a time to find the next run. A single `FREQ=MINUTELY` rule therefore enumerates roughly a quarter-century of occurrences, synchronously, on the event loop that also serves the scheduler, HTTP and WebSocket traffic. Nothing bounds the walk, and nothing moves it off the loop.\n\n## Preconditions\nAny user who can create an automation. `USER_PERMISSIONS_FEATURES_AUTOMATIONS` defaults to `false`, so on a default deployment only an admin can reach the create path; it becomes reachable by ordinary users on any deployment that has granted the automations feature, which is the normal way to make the feature usable. `UVICORN_WORKERS` defaults to 1, so there is no second worker to absorb the stall. The rule needs no unusual syntax: `FREQ=MINUTELY` with no `DTSTART`, or with a `DTSTART` set well in the past, is enough.\n\n## Impact\nAvailability, against every other user of the instance. One evaluation of `RRULE:FREQ=MINUTELY` takes 18.9 s of blocking CPU; adding a ten-value `BYSECOND` list multiplies the walk and takes 64.2 s. `FREQ=HOURLY` costs 0.34 s and is not materially exploitable on its own. The cost does not stop at creation: once the automation is stored, the scheduler recomputes the next run for every claimed row on each poll, so the same walk repeats on a default 10 s interval and the instance stays wedged rather than recovering. Instances that have not enabled the automations feature for non-admin users are exposed only to an admin doing this.\n\n## Fix\nFixed in 0.11.0. Sub-daily rules are now anchored to the current clock instead of the year-2000 date, so the walk starts at the next occurrence rather than a quarter-century behind it. A caller-supplied `DTSTART` is honoured only when the number of occurrences it implies stays under a fixed bound, and is otherwise replaced by the clock-aligned anchor. The same rules that cost 18.9 s and 64.2 s now cost under a millisecond. Upgrading fully resolves the issue, no configuration change is required.\n\n## Root cause\nAffected component: `backend/open_webui/utils/automations.py`, `_parse_rule`, reached from the automation create, update and toggle handlers in `backend/open_webui/routers/automations.py` and from the scheduler's claim path in `backend/open_webui/models/automations.py`. Affected setup: every build from 0.9.0 onward, since that is when the automations feature shipped.\n\nThe fixed anchor existed to make sub-daily intervals snap to clock boundaries, so that \"every 5 minutes\" lands on :00, :05, :10 rather than drifting from whenever the automation happened to be created. Snapping only needs a reference point of the right phase, but the implementation used a literal far-past date as that reference and left the recurrence library to walk forward from it. The distance between the anchor and the present is therefore attacker-influenced work that grows with real time, and it was never treated as a cost that needed a bound or a thread.\n\n## Proof of concept\nMeasured cost of a single next-run computation against the shipped 0.10.2 parser:\n\n| rule | cost |\n| --- | --- |\n| `RRULE:FREQ=MINUTELY` | 18,880 ms |\n| `RRULE:FREQ=MINUTELY;BYSECOND=0,1,2,3,4,5,6,7,8,9` | 64,236 ms |\n| `DTSTART:20000101T000000` + `RRULE:FREQ=MINUTELY` | 26,237 ms |\n| `RRULE:FREQ=HOURLY` | 339 ms |\n\nMeasured at function level deliberately. Persisting such an automation has the scheduler repeat the walk on every poll and wedge the instance indefinitely, which is the actual impact but makes a live end-to-end run destructive to the test instance.\n\n## Credits\nReported by @Classic298.","published":"2026-08-04T20:42:31.752Z","modified":"2026-10-28T02:31:06.174689219Z","cvss":{"score":6.5,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H"},"epss":{"score":0.00359,"percentile":0.29495,"asOf":"2026-09-17"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"PyPI","name":"open-webui","fixedVersion":"0.11.0"}],"fix":{"url":"https://github.com/open-webui/open-webui/commit/c4ae8c86786fed521960466f6d8eef8af22c2946","label":"open-webui/open-webui@c4ae8c8"},"references":[{"type":"WEB","url":"https://github.com/open-webui/open-webui/releases/tag/v0.11.0"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/70xxx/CVE-2026-70489.json"},{"type":"ADVISORY","url":"https://github.com/open-webui/open-webui/security/advisories/GHSA-73cq-mcgh-379c"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-70489"},{"type":"FIX","url":"https://github.com/open-webui/open-webui/commit/c4ae8c86786fed521960466f6d8eef8af22c2946"},{"type":"PACKAGE","url":"https://github.com/open-webui/open-webui"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-10-28T02:31:06.174689219Z"}}