Feb 27, 2026·5 min read·15 visits
Actual Budget versions prior to 26.2.1 fail to verify file ownership on synchronization endpoints. Any authenticated user can read or overwrite another user's financial data by supplying the target's budget ID in API requests.
A critical authorization flaw exists in Actual Budget's synchronization server, specifically affecting multi-user deployments. The vulnerability allows authenticated users to access, modify, or delete budget files belonging to other users due to missing ownership verification checks in the sync endpoints. This effectively constitutes an Insecure Direct Object Reference (IDOR) where knowledge of a file's UUID is sufficient to grant full access, bypassing intended isolation between users.
Actual Budget is a local-first personal finance application that supports a server component for syncing data across devices. In multi-user configurations, the server acts as a central repository for multiple distinct user accounts. CVE-2026-27638 identifies a Missing Authorization vulnerability (CWE-862) within this synchronization layer.
The core issue lies in the API endpoints responsible for handling budget file operations, such as downloading, uploading, and syncing changes. While the application correctly enforces authentication—requiring a valid session token to interact with the API—it failed to implement resource-level authorization. Consequently, the server did not verify that the authenticated user was the owner of the budget file specified in the request.
The vulnerability stems from a reliance on authentication middleware without complementary object-level permission checks. The server utilizes validateSessionMiddleware to ensure that incoming requests originate from a logged-in user. However, once this check passed, the endpoints trustingly accepted the fileId (typically provided via the X-Actual-File-Id header or JSON body) as the target resource.
In packages/sync-server/src/app-sync.ts, handlers for routes like /sync, /download-user-file, and /upload-user-file would retrieve file metadata from the database based on the provided ID. The logic proceeded to perform the requested operation (read/write) immediately after retrieval. Crucially, the code did not compare the owner field of the retrieved file record against the userId associated with the active session. This omission created a direct path for Insecure Direct Object Reference (IDOR), where the identifier alone acted as the authorization token.
The following comparison highlights the absence of authorization logic in the vulnerable version and the explicit checks introduced in the patch.
Vulnerable Logic (Conceptual Representation):
Before the fix, endpoints would look up a file and immediately return it or process it, assuming that possession of the ID implied permission.
// Vulnerable handler example
app.post('/download-user-file', async (req, res) => {
const fileId = req.headers['x-actual-file-id'];
// DANGER: No check to see if req.userId owns fileId
const file = await db.getFile(fileId);
if (file) {
res.send(file.content);
}
});Patched Logic (Commit 9966c024):
The fix introduces a centralized helper function requireFileAccess which is now invoked at the start of every sync endpoint. This function explicitly validates ownership or administrative privileges.
// New authorization helper
function requireFileAccess(file, userId) {
// 1. Direct ownership check
if (file.owner === userId) {
return null; // Access granted
}
// 2. Admin override for system tasks
if (isAdmin(userId)) {
return null;
}
// 3. Shared access check (new feature support)
if (UserService.countUserAccess(file.id, userId) > 0) {
return null;
}
// Default deny
return 'file-access-not-allowed';
}
// Patched handler usage
app.post('/download-user-file', async (req, res) => {
const fileId = req.headers['x-actual-file-id'];
const file = await db.getFile(fileId);
// Explicit authorization gate
const accessError = requireFileAccess(file, req.userId);
if (accessError) {
return res.status(403).send(accessError);
}
res.send(file.content);
});This change ensures that even if an attacker guesses a valid fileId, the server rejects the request if the database record for that file lists a different owner.
To exploit this vulnerability, an attacker requires a valid account on the target Actual Budget instance. The attack does not require administrative privileges, only a standard user session.
Step 1: Reconnaissance
The attacker must obtain the fileId (a UUID) of a target budget. While UUIDs are generally not enumerable, they may be leaked through shared logs, exposed in client-side errors, or obtained via social engineering. In some configurations or older versions, file IDs might be predictable or sequentially generated (though Actual uses UUIDs by default).
Step 2: Execution The attacker crafts a malicious HTTP request to the synchronization API. For example, to exfiltrate a victim's budget:
POST /download-user-file HTTP/1.1
Host: budget.example.com
X-Actual-Token: <attacker-session-token>
X-Actual-File-Id: <victim-budget-uuid>
Content-Type: application/json
{}Step 3: Impact The server processes the request using the attacker's valid session but the victim's file ID. Without the ownership check, the server returns the victim's full budget database. Conversely, an attacker could upload a corrupted database to the same ID, destroying the victim's data.
The impact of this vulnerability is significant for multi-user deployments, affecting the confidentiality and integrity of financial data.
Confidentiality: An attacker can download complete budget files, exposing sensitive transaction histories, bank balances, and financial habits of other users.
Integrity: An attacker can modify or overwrite budget files. This allows for the injection of fraudulent transactions, deletion of valid records, or complete corruption of the budget database, rendering it unusable for the legitimate owner.
Availability: The /delete-user-file endpoint is also affected. An attacker could permanently delete other users' budget files, causing data loss if no backups exist.
The CVSS v3.1 score is 7.1 (High), driven by the high Integrity impact and the low complexity required to execute the attack once a file ID is known.
The primary remediation is to update the Actual server software.
Patching:
Update to Actual version 26.2.1 or later. This version includes the requireFileAccess logic and database migrations to ensure all existing files have correctly assigned owners.
Configuration Workarounds: If immediate patching is not feasible, administrators should restrict the instance to single-user mode. This vulnerability specifically impacts multi-user configurations where isolation between accounts is assumed. By disabling open registration and ensuring only trusted users have accounts, the risk is mitigated, although the underlying code flaw remains.
Database Audit:
Post-update, administrators should verify that the migration script (1763873600000-backfill-files-owner.js) successfully populated the owner column for all records in the files table. Orphaned files (files with NULL owners) should be manually assigned or audited.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Actual Actual Budget | < 26.2.1 | 26.2.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-862 |
| Vulnerability Type | Missing Authorization / IDOR |
| CVSS v3.1 | 7.1 (High) |
| Attack Vector | Network |
| Privileges Required | Low (Authenticated User) |
| Impact | Data Exfiltration & Integrity Loss |
A vulnerability in the Slack and Mattermost platform adapters for NousResearch hermes-agent permits an unauthenticated remote attacker to execute arbitrary mass mentions. By leveraging prompt injection, an attacker can bypass output sanitization logic and trigger workspace-wide notification exhaustion.
CVE-2026-9306 is a critical unauthenticated Insecure Direct Object Reference (IDOR) vulnerability located in the QuantumNous new-api application, affecting versions up to and including 0.12.1. The flaw is caused by improper middleware ordering combined with a lack of object-level authorization checks. This allows remote, unauthenticated attackers to retrieve sensitive Midjourney images belonging to other users by supplying a valid task identifier.
The instagrapi library prior to version 2.6.9 contains an improper input validation vulnerability within its challenge handling mechanism. Maliciously crafted server responses can manipulate the client into forwarding session cookies and credentials to an external attacker-controlled domain.
GHSA-QQQM-5547-774X is a critical path traversal vulnerability in the FileBrowser Quantum application, specifically within the Go backend package. The vulnerability resides in the HTTP handler responsible for processing bulk file modifications via the public API. Unauthenticated attackers can exploit an order-of-operations flaw in the path sanitization logic to bypass intended directory restrictions. This allows adversaries to arbitrarily read, move, and overwrite files on the underlying filesystem by supplying specially crafted HTTP PATCH requests.
The qs query string parsing and serialization library for Node.js is vulnerable to a synchronous Denial of Service (DoS) attack. The vulnerability manifests as a process-terminating TypeError when processing arrays with null or undefined elements under specific configuration parameters.
The aiosend library prior to version 3.0.6 contains a pre-authentication Denial of Service (DoS) vulnerability in its webhook handling mechanism. The software processes and deserializes incoming JSON payloads before verifying the cryptographic signature, allowing unauthenticated attackers to exhaust server CPU and memory resources by sending large, complex payloads.