@actual-app/sync-server

CVE-2026-33318

CVE-2026-33318 is a high-severity missing authorization vulnerability in @actual-app/sync-server (npm), affecting versions < 26.4.0. It is fixed in 26.4.0.

Key facts
CVSS score
8.8
High
Attack vector
Network
Issuing authority
GitHub Advisory Database
Affected package
@actual-app/sync-server
Fixed in
26.4.0
Disclosed
2026

Summary

Summary Any authenticated user (including BASIC role) can escalate to ADMIN on servers migrated from password authentication to OpenID Connect. Three weaknesses combine: POST /account/change-password has no authorization check, allowing any session to overwrite the password hash; the inactive password auth row is never removed on migration; and the login endpoint accepts a client-supplied loginMethod that bypasses the server's active auth configuration. Together these allow an attacker to set a known password and authenticate as the anonymous admin account created during the multiuser migration. Details packages/sync-server/src/app-account.js:120-132, the /account/change-password route validates only that a session exists. No admin role check is performed packages/sync-server/src/accounts/password.js:113-125, changePassword() updates the hash with no current-password confirmation: packages/sync-server/src/accounts/password.js:56-62, loginWithPassword() always authenticates as the user with username = '', which is created by the multiuser migration with role = 'ADMIN': packages/sync-server/src/account-db.js:56-63, a client can force the password login method regardless of server configuration by sending loginMethod in the request body: When a server is migrated from password → OpenID via enableOpenID(), the password row is set to active = 0 but never deleted, leaving it available for exploitation. PoC Prerequisites: Server originally bootstrapped with password auth, then switched to OpenID. Default allowedLoginMethods configuration (includes password). Attacker has any valid OpenID session token (any role). The returned token belongs to the username = '' admin account (created by 1719409568000-multiuser.js). Verify admin access: Impact Privilege escalation, any authenticated user can gain full ADMIN access on affected deployments. An ADMIN can manage all users, access all budget files regardless of ownership, modify file access controls, and change server configuration. Affected deployments: multi-user servers running OpenID Connect that were previously configured with password authentication. Servers bootstrapped exclusively with OpenID from initial setup are not affected (no password row exists in the auth table). Recommendations Restrict POST /account/change-password to password-authenticated sessions only (packages/sync-server/src/app-account.js) The endpoint should reject requests from sessions that were authenticated via OpenID. The active auth method can be checked against the session's auth_method field or by querying the auth table for the currently active method. If the server is running in OpenID mode, this endpoint should return 403 Forbidden. Require current-password confirmation before accepting a new password (packages/sync-server/src/accounts/password.js) changePassword() should accept the current password as a parameter and verify it against the stored hash before applying the update. This prevents any session (even a legitimate password session) from silently overwriting the credential without proving possession of the existing one. Enforce active status and remove client control over login method selection (packages/sync-server/src/account-db.js, getLoginMethod()) Two issues exist in getLoginMethod(): (a) a client can supply loginMethod in the request body to select any method listed in allowedLoginMethods, regardless of whether it is currently active on the server; (b) the function does not check the active column, so an administratively disabled method (e.g. password after migrating to OpenID) remains accessible. The fix is to determine the permitted method server-side from WHERE active = 1 in the auth table and ignore any client-supplied loginMethod override entirely. Servers intentionally running both methods simultaneously can be supported by allowing multiple active = 1 rows rather than relying on client input. Immediate mitigation for existing deployments (OpenID-only servers) Administrators who have fully migrated to OpenID and do not need password auth can remove the orphaned row: The three weaknesses form a single, sequential exploit chain, none produces privilege escalation on its own: Missing authorization on POST /change-password, allows overwriting a password hash, but only matters if there is an orphaned row to target. Orphaned password row persisting after migration, provides the target row, but is harmless without the ability to authenticate using it. Client-controlled loginMethod: "password", allows forcing password-based auth, but is useless without a known hash established by step 1. All three must be chained in sequence to achieve the impact. No single weakness independently results in privilege escalation, which under CVE CNA rule 4.1.2 means they should not each be treated as standalone vulnerabilities. The single root cause is the missing authorization check on /change-password; the other two are preconditions that make it exploitable. A single CVE reflecting that root cause is the appropriate representation, splitting them would falsely imply each carries independent risk.

Impact

What is missing authorization?

The application does not perform an authorization check before performing a sensitive operation. Typical impact: unauthorized access to restricted functionality or data.

Severity and exposure

CVE-2026-33318 has a CVSS score of 8.8 (High). The vector is network-reachable, low privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment.

A fixed version is available (26.4.0). Upgrading removes the vulnerable code path.

Affected versions

npm

  • @actual-app/sync-server (< 26.4.0)

Security releases

  • @actual-app/sync-server → 26.4.0 (npm)
Kodem intelligence

Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.

Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter instead of chasing every advisory.

Kodem's runtime-powered SCA identifies whether CVE-2026-33318 is reachable in your applications. Explore open-source security for your team.

See if CVE-2026-33318 is reachable in your applications. Get a demo

Already deployed Kodem? See CVE-2026-33318 in your environment

Remediation advice

Upgrade @actual-app/sync-server to 26.4.0 or later to resolve this vulnerability.

Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.

Frequently asked questions about CVE-2026-33318

What is CVE-2026-33318?

CVE-2026-33318 is a high-severity missing authorization vulnerability in @actual-app/sync-server (npm), affecting versions < 26.4.0. It is fixed in 26.4.0. The application does not perform an authorization check before performing a sensitive operation.

How severe is CVE-2026-33318?

CVE-2026-33318 has a CVSS score of 8.8 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.

Which versions of @actual-app/sync-server are affected by CVE-2026-33318?

@actual-app/sync-server (npm) versions < 26.4.0 is affected.

Is there a fix for CVE-2026-33318?

Yes. CVE-2026-33318 is fixed in 26.4.0. Upgrade to this version or later.

Is CVE-2026-33318 exploitable, and should I be worried?

Whether CVE-2026-33318 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo

What actually determines whether CVE-2026-33318 is exploitable, and how bad it is?

Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.

How do I fix CVE-2026-33318?

Upgrade @actual-app/sync-server to 26.4.0 or later.

Stop the waste.
Protect your environment with Kodem.