Actual Sync Server proxy flaw exposes private GitHub repos
A prefix-matching bug in the self-hosted budgeting server's CORS proxy lets any logged-in user borrow the server's GitHub token, according to a GitHub advisory.
By The Agentic Times ·

A security advisory published on 7 October 2026 says Actual Sync Server, the self-hosted backend for the Actual Budget personal finance app, contains a flaw that lets any authenticated user read private GitHub repositories using the server's own access token. The issue is tracked as CVE-2026-57449 and affects the npm package @actual-app/sync-server.
The problem sits in the server's CORS proxy, an endpoint at /cors-proxy that is meant to let the app fetch files only from repositories named in the official Actual plugin allowlist. The advisory says the proxy fetches that allowlist from the project's plugin store and then checks whether a requested URL matches one of the listed repositories.
For requests to api.github.com, that check uses a plain startsWith() test on the path /repos/{owner}/{repo} with no boundary character after the repository name, according to the advisory. The result is that an allowlisted public repository called acme/plugin also authorises API calls to acme/plugin-private, acme/plugin-secrets, acme/plugin-internal, or any other repository under the same owner whose name begins with the same string.
That would be a smaller problem on its own. The advisory says the proxy then attaches the server's configured GitHub personal access token, set through the ACTUAL_GITHUB_TOKEN environment variable, to any request bound for api.github.com, raw.githubusercontent.com, or GitHub release URLs. Because the allowlist decision has already been made incorrectly, the token is sent along with requests the proxy should have refused, and GitHub's response is handed back to the user who asked for it.
The advisory sets out a reproduction path. A low-privileged user with any valid Actual session token calls the proxy with a URL such as https://api.github.com/repos/acme/plugin-private/contents/.env. The server returns GitHub's JSON response, including the base64-encoded contents of the file, which the attacker can decode locally. A Python proof-of-concept is attached to the advisory; it does not require the attacker to hold the GitHub token, because the Actual server itself acts as the oracle.
Several conditions have to line up. The CORS proxy is disabled by default and must be switched on through ACTUAL_CORS_PROXY_ENABLED. A GitHub token must be configured and must have read access to the target private repository. The plugin allowlist must contain a public repository whose owner and name form a prefix of the private repository's name. And the attacker needs a working Actual session.
Where those conditions are met, the advisory says the exposure covers private source code, repository metadata, release data, deployment files and secrets that have been committed by mistake. It adds that the damage scales with how broadly the server's token is scoped. The advisory names the specific source files and line numbers involved, in app-cors-proxy.js, app.ts and load-config.js.
The root cause here is a configuration and validation mistake rather than a memory-safety bug: a string comparison that does not insist on a path separator. Operators running a self-hosted Actual sync server with the CORS proxy enabled should check whether a GitHub token is configured and how widely that token can read.
Sources
- github.comhttps://github.com/advisories/GHSA-m62c-5q34-f3cf
