CVE-2026-72809: SiYuan Localhost-trust Admin Bypass On Auth-code-gated Vulnerability

HIGH
Advisory Identifier: GHSA-3mp7-4rh5-jrv9
CVSS 8.0
CWE-290
Published
Advisory Summary

CVE-2026-72809 is a HIGH (CVSS 8.0) localhost-trust admin bypass on auth-code-gated vulnerability (CWE-290: Authentication Bypass by Spoofing) affecting SiYuan libraries in the Go ecosystem. The issue involves localhost-trust admin bypass on auth-code-gated endpoints, with potential remote reachability via the fixed-port proxy. Remediation is available by upgrading affected packages to version 0.0.0-20260723031701-9c16e9851f0b.

CVE-2026-72809 at a Glance

CVE Identifier CVE-2026-72809
GHSA Identifier GHSA-3mp7-4rh5-jrv9
Severity HIGH
CVSS v3.1 Base Score 8.0
CWE Classification CWE-290: Authentication Bypass by Spoofing
Affected Product SiYuan
Package Ecosystem Go
Published Date
Last Updated

Security Impact

CVSS: 8.0

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:H).

Attack Vector Local (AV:L)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality High (C:H)
Integrity Low (I:L)
Availability High (A:H)

How to Fix CVE-2026-72809

To remediate this vulnerability, upgrade all affected SiYuan dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

github.com/siyuan-note/siyuan/kernel fixed: 0.0.0-20260723031701-9c16e9851f0b
Upgrade github.com/siyuan-note/siyuan/kernel to 0.0.0-20260723031701-9c16e9851f0b
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for github.com/siyuan-note/siyuan/kernel

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-72809, inspect your project dependencies for SiYuan:

inspect dependencies for github.com/siyuan-note/siyuan/kernel

Analyze the terminal output to locate any version of SiYuan in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.0.0-20260723031701-9c16e9851f0b), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-290: Authentication Bypass by Spoofing, where this attack-focused weakness is caused by incorrectly implemented authentication schemes that are subject to spoofing attacks. In SiYuan, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

**CVE:** This vulnerability corresponds to [CVE-2026-72809](https://nvd.nist.gov/vuln/detail/CVE-2026-72809). ### Summary The kernel's `CheckAuth` grants `RoleAdministrator` to any request whose `RemoteAddr` is loopback (`127.0.0.1`), for a specific set of endpoints, and these localhost bypasses sit **outside** the `accessAuthCode` gate so they apply even when an access auth code is configured. This is demonstrated live (Part A below). Separately, the fixed-port reverse proxy (`fixedport.go`) forwards requests to the kernel over loopback and injects no authentication token, and no `SetTrustedProxies` is configured, so gin does not derive the client address from forwarding headers. By code inspection, a request forwarded through this proxy would reach the kernel with `RemoteAddr = 127.0.0.1`. **If** the fixed-port proxy is bound to a network interface (via `NetworkServe`) and forwards remote requests to the kernel this way, the localhost bypass would grant a remote unauthenticated caller admin on those endpoints. This second step is established by reading the code but was **not reproduced end-to-end**, and I'm asking the maintainer to confirm the proxy's runtime forwarding behavior (Part B below). ### Details **Localhost-trust admin bypass (proven).** `CheckAuth` (`session.go:298-321`) contains localhost-only bypasses that key off `RemoteAddr` and grant `RoleAdministrator`. They sit outside the `accessAuthCode` gate i.e. they apply even when an access auth code is set and cover `/api/system/exit`, `getNetwork`, `getWorkspaceInfo`, `/assets/*`, and `/export/*`. **Fixed-port proxy behavior (code inspection).** `fixedport.go` is a plain reverse proxy that dials the kernel at `127.0.0.1` and injects no token unlike the publish proxy, which injects a `RoleReader` JWT. There is no `SetTrustedProxies` call, so gin does not rewrite `RemoteAddr` from `X-Forwarded-For`. On this reading, a request forwarded through the fixed-port proxy reaches the kernel with `RemoteAddr = 127.0.0.1`. **The composition (conditional).** If both hold at runtime, then: remote request → fixed-port proxy on a network interface → forwarded to kernel at `127.0.0.1` (no token) → kernel sees `RemoteAddr = 127.0.0.1` → localhost bypass grants admin for the endpoints above. I have proven the final link (localhost → admin) and read the code for the forwarding link, but have not confirmed at runtime that the fixed-port proxy is instantiated and forwards remote requests as loopback in a shipped configuration. **Distinct from the previously-dismissed localhost→admin observation.** That observation concerned the *publish* proxy path, where the injected `RoleReader` JWT causes `CheckAuth` to early-return before reaching the localhost bypasses. The fixed-port proxy injects no token, so a request through it would fall through to the `RemoteAddr`-keyed bypass instead. Different proxy, different code path. ### Proof of Concept **Part A: the localhost bypass grants admin without auth, outside the auth-code gate (demonstrated live).** On a local instance with an access auth code configured, the same no-token `getWorkspaceInfo` request returns different results depending on the source address the kernel sees: - From a non-loopback source (kernel sees a non-127.0.0.1 address): `HTTP 401`. - From `127.0.0.1` (kernel sees loopback): `{"code":0,"data":{"workspaceDir":"/siyuan/workspace",…}}` admin data, no auth, despite `accessAuthCode` being set. This confirms the localhost-trust bypass grants admin for these endpoints and is not gated by the access auth code. **Part B: remote → proxy → loopback (code inspection only; NOT reproduced).** By reading `fixedport.go`, the proxy dials `127.0.0.1`, injects no token, and no `SetTrustedProxies` is set. I was not able to reproduce this end-to-end: the `serve` CLI in the container image tested exposes only `--port` and `--accessAuthCode`, not a flag that instantiates the fixed-port proxy in the `NetworkServe`-on-a-non-default-port shape, so the remote→proxy→loopback chain was not exercised at runtime. I did not invoke the destructive `/api/system/exit` endpoint. **I'm asking the maintainer to confirm: under what conditions is the fixed-port proxy instantiated, does it bind a non-loopback interface under `NetworkServe`, and does it forward to the kernel preserving the client address or as loopback?** That determines whether Part A's bypass is remotely reachable. ### Impact **Confirmed (Part A):** on any deployment where a caller can cause the kernel to see a loopback `RemoteAddr`, the endpoints `/api/system/exit`, `getNetwork`, `getWorkspaceInfo`, `/assets/*`, and `/export/*` are reachable with admin rights without the access auth code i.e. the auth code does not protect these endpoints against a loopback-sourced caller. On its own this is a local/adjacent bypass of the access-code control for those endpoints. If the fixed-port proxy forwards remote requests to the kernel as loopback (to be confirmed by the maintainer), a *remote* unauthenticated attacker obtains those admin capabilities, remote kernel shutdown (DoS), network/workspace-path disclosure, and admin-level asset/export reads that bypass the publish-access filter. This would be a remote authentication bypass. It does not grant the full admin API only the localhost-trusted endpoints. ### Suggested fix Do not derive admin trust from `RemoteAddr` when a proxy forwards over loopback. Options: have the fixed-port proxy inject an explicit role/token (as the publish proxy does) so the kernel authorizes on the claim rather than the source address; or configure `SetTrustedProxies` and derive the real client address before applying any localhost bypass; or require the access auth code for these endpoints regardless of source address. The localhost bypass assumes loopback implies a local trusted caller, any loopback-dialing proxy breaks that assumption.

CVE-2026-72809 Affected SiYuan Versions

1 component(s)
Fixed Versions:
0.0.0-20260723031701-9c16e9851f0b
Product / Package Ecosystem Affected Versions Fixed Version
github.com/siyuan-note/siyuan/kernel
Go
< 0.0.0-20260723031701-9c16e9851f0b
0.0.0-20260723031701-9c16e9851f0b

References

Frequently Asked Questions

What is CVE-2026-72809?
CVE-2026-72809 is a security vulnerability in SiYuan involving Localhost-trust admin bypass on auth-code-gated endpoints, with potential remote reachability via the fixed-port proxy under CWE-290: Authentication Bypass by Spoofing.
Which SiYuan versions are affected?
Affected versions include releases prior to 0.0.0-20260723031701-9c16e9851f0b across affected packages: github.com/siyuan-note/siyuan/kernel.
What version fixes CVE-2026-72809?
Upgrading to 0.0.0-20260723031701-9c16e9851f0b (or the latest patched release for your respective branch) resolves the vulnerability.
Is github.com/siyuan-note/siyuan/kernel affected?
Yes, github.com/siyuan-note/siyuan/kernel is affected in releases prior to 0.0.0-20260723031701-9c16e9851f0b. Upgrade your dependency to 0.0.0-20260723031701-9c16e9851f0b or later.
What is the CVSS score of CVE-2026-72809?
CVE-2026-72809 has a CVSS v3.1 base score of 8.0 (HIGH) with vector string CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:H.
What CWE is associated with CVE-2026-72809?
CVE-2026-72809 is classified under CWE-290: Authentication Bypass by Spoofing (This attack-focused weakness is caused by incorrectly implemented authentication schemes that are subject to spoofing attacks.).
How can I check Go dependencies for CVE-2026-72809?
Run the dependency tree command: 'inspect dependencies for github.com/siyuan-note/siyuan/kernel'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-3mp7-4rh5-jrv9",
  "modified" : "2026-09-03T22:34:06Z",
  "published" : "2026-09-03T22:34:06Z",
  "aliases" : [ "CVE-2026-72809" ],
  "summary" : "SiYuan: Localhost-trust admin bypass on auth-code-gated endpoints, with potential remote reachability via the fixed-port proxy",
  "details" : "**CVE:** This vulnerability corresponds to [CVE-2026-72809](https://nvd.nist.gov/vuln/detail/CVE-2026-72809).\n\n### Summary\n\nThe kernel's `CheckAuth` grants `RoleAdministrator` to any request whose `RemoteAddr` is loopback (`127.0.0.1`), for a specific set of endpoints, and these localhost bypasses sit **outside** the `accessAuthCode` gate so they apply even when an access auth code is configured. This is demonstrated live (Part A below).\n\nSeparately, the fixed-port reverse proxy (`fixedport.go`) forwards requests to the kernel over loopback and injects no authentication token, and no `SetTrustedProxies` is configured, so gin does not derive the client address from forwarding headers. By code inspection, a request forwarded through this proxy would reach the kernel with `RemoteAddr = 127.0.0.1`. **If** the fixed-port proxy is bound to a network interface (via `NetworkServe`) and forwards remote requests to the kernel this way, the localhost bypass would grant a remote unauthenticated caller admin on those endpoints. This second step is established by reading the code but was **not reproduced end-to-end**, and I'm asking the maintainer to confirm the proxy's runtime forwarding behavior (Part B below).\n\n### Details\n\n**Localhost-trust admin bypass (proven).** `CheckAuth` (`session.go:298-321`) contains localhost-only bypasses that key off `RemoteAddr` and grant `RoleAdministrator`. They sit outside the `accessAuthCode` gate i.e. they apply even when an access auth code is set and cover `/api/system/exit`, `getNetwork`, `getWorkspaceInfo`, `/assets/*`, and `/export/*`.\n\n**Fixed-port proxy behavior (code inspection).** `fixedport.go` is a plain reverse proxy that dials the kernel at `127.0.0.1` and injects no token unlike the publish proxy, which injects a `RoleReader` JWT. There is no `SetTrustedProxies` call, so gin does not rewrite `RemoteAddr` from `X-Forwarded-For`. On this reading, a request forwarded through the fixed-port proxy reaches the kernel with `RemoteAddr = 127.0.0.1`.\n\n**The composition (conditional).** If both hold at runtime, then: remote request → fixed-port proxy on a network interface → forwarded to kernel at `127.0.0.1` (no token) → kernel sees `RemoteAddr = 127.0.0.1` → localhost bypass grants admin for the endpoints above. I have proven the final link (localhost → admin) and read the code for the forwarding link, but have not confirmed at runtime that the fixed-port proxy is instantiated and forwards remote requests as loopback in a shipped configuration.\n\n**Distinct from the previously-dismissed localhost→admin observation.** That observation concerned the *publish* proxy path, where the injected `RoleReader` JWT causes `CheckAuth` to early-return before reaching the localhost bypasses. The fixed-port proxy injects no token, so a request through it would fall through to the `RemoteAddr`-keyed bypass instead. Different proxy, different code path.\n\n### Proof of Concept\n\n**Part A: the localhost bypass grants admin without auth, outside the auth-code gate (demonstrated live).**\nOn a local instance with an access auth code configured, the same no-token `getWorkspaceInfo` request returns different results depending on the source address the kernel sees:\n- From a non-loopback source (kernel sees a non-127.0.0.1 address): `HTTP 401`.\n- From `127.0.0.1` (kernel sees loopback): `{\"code\":0,\"data\":{\"workspaceDir\":\"/siyuan/workspace\",…}}` admin data, no auth, despite `accessAuthCode` being set.\n\nThis confirms the localhost-trust bypass grants admin for these endpoints and is not gated by the access auth code.\n\n**Part B: remote → proxy → loopback (code inspection only; NOT reproduced).**\nBy reading `fixedport.go`, the proxy dials `127.0.0.1`, injects no token, and no `SetTrustedProxies` is set. I was not able to reproduce this end-to-end: the `serve` CLI in the container image tested exposes only `--port` and `--accessAuthCode`, not a flag that instantiates the fixed-port proxy in the `NetworkServe`-on-a-non-default-port shape, so the remote→proxy→loopback chain was not exercised at runtime. I did not invoke the destructive `/api/system/exit` endpoint. **I'm asking the maintainer to confirm: under what conditions is the fixed-port proxy instantiated, does it bind a non-loopback interface under `NetworkServe`, and does it forward to the kernel preserving the client address or as loopback?** That determines whether Part A's bypass is remotely reachable.\n\n### Impact\n\n**Confirmed (Part A):** on any deployment where a caller can cause the kernel to see a loopback `RemoteAddr`, the endpoints `/api/system/exit`, `getNetwork`, `getWorkspaceInfo`, `/assets/*`, and `/export/*` are reachable with admin rights without the access auth code i.e. the auth code does not protect these endpoints against a loopback-sourced caller. On its own this is a local/adjacent bypass of the access-code control for those endpoints. If the fixed-port proxy forwards remote requests to the kernel as loopback (to be confirmed by the maintainer), a *remote* unauthenticated attacker obtains those admin capabilities, remote kernel shutdown (DoS), network/workspace-path disclosure, and admin-level asset/export reads that bypass the publish-access filter. This would be a remote authentication bypass. It does not grant the full admin API only the localhost-trusted endpoints.\n\n### Suggested fix\n\nDo not derive admin trust from `RemoteAddr` when a proxy forwards over loopback. Options: have the fixed-port proxy inject an explicit role/token (as the publish proxy does) so the kernel authorizes on the claim rather than the source address; or configure `SetTrustedProxies` and derive the real client address before applying any localhost bypass; or require the access auth code for these endpoints regardless of source address. The localhost bypass assumes loopback implies a local trusted caller, any loopback-dialing proxy breaks that assumption.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:H"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/siyuan-note/siyuan/kernel"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "0.0.0-20260723031701-9c16e9851f0b"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/siyuan-note/siyuan/security/advisories/GHSA-3mp7-4rh5-jrv9"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-72809"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/siyuan-note/siyuan/commit/9c16e9851f0b5d7ed179e5c2fca15a7724666871"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/siyuan-note/siyuan"
  }, {
    "type" : "WEB",
    "url" : "https://www.vulncheck.com/advisories/siyuan-before-authentication-bypass-via-localhost-trust"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-290" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-09-03T22:34:06Z",
    "nvd_published_at" : null
  }
}