Oct 10, 2026·5 min read·3 visits
Vikunja versions prior to 2.6.0 disclose private user email addresses to any authenticated user with read permissions on a task due to over-broad SQL model population in the task assignees endpoint.
An information disclosure vulnerability in Vikunja allows authenticated users with read access to a task to expose private email addresses of assigned users through the API task assignees endpoint due to an unmasked database query.
Vikunja is an open-source, self-hosted project management platform that allows users to organize tasks, assign team members, and collaborate within shared workspaces. Shared projects support granular permission models, allowing users to be granted read-only, write, or administrative access.
An information disclosure flaw exists in the task assignees endpoint (TaskAssginee.ReadAll). When retrieving assignees for a given task, the API returns full user profile objects that include private email addresses. Low-privilege accounts with read-only permissions on a shared project can query this endpoint to extract personal identifiable information (PII) of other project members.
The weakness is classified under CWE-200 (Exposure of Sensitive Information to an Unauthorized Actor). While task assignees must be visible to project members by username or display name, exposing personal email addresses breaches privacy boundaries in multi-tenant environments.
The defect lies within pkg/models/task_assignees.go in the implementation of TaskAssginee.ReadAll(). When a request is made to list assignees for a task, the service constructs an SQL query using XORM that performs an inner join between the task_assignees table and the users table.
Prior to version 2.6.0, the backend query executed Select("users.*")). This wildcard column selection directed the ORM engine to retrieve all database columns from the users table and map them directly into user.User struct instances, populating internal fields including user.Email.
The REST API handler converts these user.User objects directly to JSON without masking or field-level filtering. Although the application verifies that the requesting user has read access to the target task, it fails to sanitize user model fields prior to serialization.
The vulnerability was resolved in pull request #3688 and released in version v2.6.0 under commit 0855e13568ba2466ff83c69aeb6d967a7d1f3e1f. The fix refactors TaskAssginee.ReadAll() to avoid selecting full user rows in the raw SQL query.
In the vulnerable implementation, Select("users.*")) directly populated complete user model instances:
// Vulnerable Implementation in pkg/models/task_assignees.go
var taskAssignees []*user.User
query := s.Table("task_assignees").
Select("users.*").
Join("INNER", "users", "task_assignees.user_id = users.id").
Where(builder.And(
builder.Eq{"task_id": la.TaskID},
))
err = query.Find(&taskAssignees)The patched version selects only the task_assignees.user_id values. It then passes the resulting ID slice to user.GetUsersByIDs(s, assigneeIDs), which handles user retrieval and enforces profile field sanitization by clearing email attributes:
// Patched Implementation in pkg/models/task_assignees.go
var assigneeIDs []int64
query := s.Table("task_assignees").
Select("task_assignees.user_id").
Join("INNER", "users", "task_assignees.user_id = users.id").
Where(builder.And(
builder.Eq{"task_id": la.TaskID},
))
err = query.Find(&assigneeIDs)
if err != nil {
return nil, 0, 0, err
}
// GetUsersByIDs handles profile field blanking
usersByIDs, err := user.GetUsersByIDs(s, assigneeIDs)
if err != nil {
return nil, 0, 0, err
}
var taskAssignees []*user.User
for _, id := range assigneeIDs {
if u, ok := usersByIDs[id]; ok {
taskAssignees = append(taskAssignees, u)
}
}Additionally, regression testing was added in pkg/models/task_assignees_test.go via TestTaskAssigneeReadAll_BlanksEmails to assert that email attributes in returned assignee objects remain empty.
To exploit this issue, an attacker requires valid user authentication on a Vikunja instance and read-level access to at least one task with assigned users. No administrative privileges are required.
The attacker issues an HTTP GET request to the task assignees API endpoint:
GET /api/v1/tasks/30/assignees HTTP/1.1
Host: vikunja.example.com
Authorization: Bearer <JWT_TOKEN>
Accept: application/jsonOn vulnerable instances (< 2.6.0), the application returns a JSON array containing assignee profiles with populated email fields:
[
{
"id": 42,
"username": "johndoe",
"name": "John Doe",
"email": "john.doe@example.com",
"created": "2024-01-15T10:00:00Z"
}
]An attacker can iterate through task identifiers across accessible projects to harvest member email addresses for targeted phishing or reconnaissance.
The primary impact of this flaw is unauthorized access to personal identifiable information (PII). In environments with external users or multi-tenant organizations, exposure of private email addresses violates user privacy expectations and regulatory data protection guidelines.
The issue is rated CVSS 4.3 (Medium) with the vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N. Network access is required (AV:N), attack complexity is low (AC:L), and low privileges are sufficient (PR:L). Impact is limited exclusively to confidentiality (C:L).
This vulnerability does not enable code execution, privilege escalation, or modification of task data. However, collected email addresses facilitate secondary vector attacks such as credential stuffing, spear phishing, and user impersonation.
Administrators should update self-hosted Vikunja instances to version v2.6.0 or later. The patch updates model queries to route user retrieval through sanitized internal getters that remove email fields.
If immediate software updates cannot be deployed, administrators should limit project sharing with unverified external users. Additionally, reverse proxies or Web Application Firewalls (WAF) can be configured to strip the email property from JSON response payloads returning from /api/v1/tasks/*/assignees.
To audit potential historical exploitation, server access logs can be inspected for systematic HTTP GET queries targeting task assignee API paths.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Vikunja Vikunja | < 2.6.0 | v2.6.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-200 |
| CVSSv3 Score | 4.3 (Medium) |
| Attack Vector | Network (Authenticated) |
| Impact | Information Disclosure (PII / Private Email) |
| Exploit Status | Proof-of-Concept |
| Patched Version | v2.6.0 |
The application exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
A cross-project information disclosure vulnerability in Vikunja allows authenticated users with read access to one project to view private task details from unauthorized projects via subtask expansion parameters.
An authorization bypass vulnerability in Vikunja versions prior to v2.6.0 permits authenticated users to delete relationships between tasks across project boundaries without requiring read or write authorization for the target related task.
A path traversal vulnerability in Shiny for Python (posit-dev/py-shiny) versions 1.4.0 through 1.6.3 allows unauthenticated remote attackers to read arbitrary files and traverse directories via crafted _state_id_ query parameters.
A Stored Cross-Site Scripting vulnerability in @tinacms/web-components prior to version 0.2.1 allows low-privileged content authors to execute arbitrary JavaScript code in the context of website visitors via unsanitized URL attributes in custom Markdown rendering components.
A critical origin validation flaw in TinaCMS admin preview allows unauthenticated attackers to bypass cross-origin postMessage checks and execute unauthorized GraphQL queries and mutations under an authenticated editor's context.
@tinacms/cli prior to version 3.0.0 dynamically constructs client source files using string interpolation without properly sanitizing runtime configuration variables. An attacker with permissions to create a branch or pull request can inject arbitrary JavaScript statements via a crafted Git ref name, leading to execution during automated build processes.