The vulnerability is a Denial of Service caused by an algorithmic complexity issue in the addRepeatIntervalToTime function within the pkg/models/tasks.go file. This function is responsible for calculating the next due date for a repeating task. The vulnerability is triggered when a repeating task with a due date set far in the past and a very short repetition interval (e.g., 1 second) is marked as "done".
The vulnerable addRepeatIntervalToTime function used a simple loop to increment the task's due date by the repeat interval until it passed the current time. With a date in the distant past, this resulted in billions of loop iterations, consuming 100% of a CPU core and holding a database connection for an extended period (minutes per request). An attacker could launch a few of these requests concurrently to exhaust the database connection pool and render the entire Vikunja instance unresponsive.
The patch replaces this inefficient O(n) loop with a constant-time O(1) arithmetic calculation to determine the next due date directly.
The analysis of the commits reveals the following key functions:
-
models.addRepeatIntervalToTime: This is the core vulnerable function containing the DoS loop. The patch in commit 22cb5ce73327a811671f788349ca1a0fbf6f882b directly replaces the looping logic with a safe arithmetic calculation.
-
models.updateDone: This function is called when a task is marked as done. The regression test added in commit 6b9dba649317d4c87d553ec89260c0d90580507a specifically calls updateDone to simulate the exploit, confirming it's the caller of the vulnerable addRepeatIntervalToTime function during exploitation. This function would be present in the stack trace of a request that triggers the DoS.
-
models.createTask and models.Task.updateSingleTask: These functions are responsible for creating and updating tasks. They were modified in commit e47bd5b4228e1f8490778ec47b74241646c53315 to add validation for the repeat_after field. While they don't contain the DoS loop itself, they were the entry point for the malicious input. The added validation serves as a defense-in-depth measure. These functions process the malicious input used to set up the attack.
Therefore, a runtime profile during exploitation would show the CPU time being spent inside models.addRepeatIntervalToTime, which was called by models.updateDone. The functions models.createTask and models.Task.updateSingleTask would have been called in prior requests to create the malicious task.