CVE-2026-46700 Overview
CVE-2026-46700 affects Actual Budget, a local-first personal finance tool. The GET /secret/:name endpoint in @actual-app/sync-server verifies only that the caller holds a valid session. It fails to confirm the caller has administrative privileges, unlike the sibling POST /secret/ handler that enforces an admin check in OpenID mode. Any authenticated non-admin BASIC user in OpenID multi-user deployments can query the secrets store. Exposed values include simplefin_accessKey, pluggyai_clientSecret, pluggyai_itemIds, and GoCardless secrets used for bank-sync integrations. The maintainers fixed the issue in version 26.6.0. The Common Weakness Enumeration classification is [CWE-285] Improper Authorization.
Critical Impact
Authenticated low-privilege users can read admin-managed bank-sync API credentials, enabling downstream access to connected financial data providers.
Affected Products
- @actual-app/sync-server versions prior to 26.6.0
- Actual Budget deployments configured for OpenID multi-user mode
- Self-hosted Actual instances with BASIC role users
Discovery Timeline
- 2026-07-07 - CVE-2026-46700 published to NVD
- 2026-07-08 - Last updated in NVD database
Technical Details for CVE-2026-46700
Vulnerability Analysis
The vulnerability exists in the secrets API of the Actual Budget sync-server. The GET /secret/:name route reuses validateSessionMiddleware but omits the admin authorization check applied to write operations. In OpenID multi-user mode, roles are enforced elsewhere, so the missing check on read creates an authorization gap. A BASIC user with a valid session can enumerate secret names and retrieve stored values. These secrets grant programmatic access to third-party bank-sync providers such as SimpleFIN, Pluggy.ai, and GoCardless.
Root Cause
The root cause is inconsistent authorization enforcement between sibling route handlers in packages/sync-server/src/app-secrets.js. The POST /secret/ handler gated writes behind isAdmin, while the corresponding GET /secret/:name handler did not. This asymmetric access control maps directly to [CWE-285] Improper Authorization.
Attack Vector
An attacker requires an authenticated BASIC-role account on an OpenID multi-user deployment. The attacker issues an authenticated HTTP GET request to /secret/:name supplying a known secret identifier such as simplefin_accessKey. The server returns the secret value without checking role membership. The attacker then reuses these credentials against upstream bank-sync APIs outside the Actual application boundary.
// Patch from packages/sync-server/src/app-secrets.js
import express from 'express';
-import { getAccountDb, isAdmin } from './account-db';
-import { secretsService } from './services/secrets-service';
+import { getActiveLoginMethod, isAdmin } from './account-db';
+import { SecretName, secretsService } from './services/secrets-service';
import {
requestLoggerMiddleware,
validateSessionMiddleware,
Source: GitHub commit 3494f78. The patch restricts secrets API access to admins when OpenID is the active login method.
Detection Methods for CVE-2026-46700
Indicators of Compromise
- Unexpected HTTP GET requests to /secret/simplefin_accessKey, /secret/pluggyai_clientSecret, /secret/pluggyai_itemIds, or /secret/gocardless_* from non-admin sessions.
- Repeated /secret/:name probing patterns from a single authenticated session enumerating multiple secret identifiers.
- Outbound API calls to SimpleFIN, Pluggy.ai, or GoCardless originating from IP addresses not associated with the Actual admin.
Detection Strategies
- Review sync-server access logs for GET /secret/ requests and correlate the requesting user's role in the OpenID identity provider.
- Deploy application-layer logging that captures the authenticated user identity alongside every secrets endpoint request.
- Alert on any read of secret names by user accounts that lack the admin role in OpenID mode.
Monitoring Recommendations
- Forward reverse proxy logs for the sync-server into a centralized log platform and index by user, route, and status code.
- Track anomalous request rates against the /secret/ route family and flag bursts consistent with enumeration.
- Monitor bank-sync provider audit logs for API activity originating outside expected sync windows or source addresses.
How to Mitigate CVE-2026-46700
Immediate Actions Required
- Upgrade @actual-app/sync-server to version 26.6.0 or later without delay.
- Rotate all bank-sync secrets including simplefin_accessKey, pluggyai_clientSecret, pluggyai_itemIds, and GoCardless credentials.
- Audit OpenID user membership and remove BASIC accounts that no longer require access.
Patch Information
Actual Budget released the fix in version 26.6.0. Review the GitHub Security Advisory GHSA-3f62-qv96-4p78, the GitHub Pull Request #7862, and the v26.6.0 release notes before deploying.
Workarounds
- Restrict the sync-server to admin-only users until the upgrade to 26.6.0 completes.
- Block access to the /secret/ route at a reverse proxy for all non-admin identities using the OpenID group claim.
- Temporarily disable OpenID multi-user mode and operate in single-user mode if patching must be deferred.
# Example nginx rule blocking non-admin access to the secrets endpoint
location ~ ^/secret/ {
if ($http_x_forwarded_role != "admin") {
return 403;
}
proxy_pass http://actual_sync_server;
}
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

