CVE-2026-73605: SiYuan GetUniqueFilename Passes An Unvalidated Client-supplied Vulnerability

MODERATE
Advisory Identifier: GHSA-hf8h-97gm-4x2p
CVSS 5.8
CWE-862
Published
Advisory Summary

CVE-2026-73605 is a MODERATE (CVSS 5.8) getuniquefilename passes an unvalidated client-supplied vulnerability (CWE-862: Missing Authorization) affecting SiYuan libraries in the Go ecosystem. The issue involves getuniquefilename passes an unvalidated client-supplied path to the filesystem, giving anonymous readers an existence oracle over the entire host filesystem. Remediation is available by upgrading affected packages to version 0.0.0-20260812083335-251596fc0de2.

CVE-2026-73605 at a Glance

CVE Identifier CVE-2026-73605
GHSA Identifier GHSA-hf8h-97gm-4x2p
Severity MODERATE
CVSS v3.1 Base Score 5.8
CWE Classification CWE-862: Missing Authorization
Affected Product SiYuan
Package Ecosystem Go
Published Date
Last Updated

Security Impact

CVSS: 5.8

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

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction None (UI:N)
Scope Changed (S:C)
Confidentiality Low (C:L)
Integrity None (I:N)
Availability None (A:N)

How to Fix CVE-2026-73605

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-20260812083335-251596fc0de2
Upgrade github.com/siyuan-note/siyuan/kernel to 0.0.0-20260812083335-251596fc0de2
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-73605, 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-20260812083335-251596fc0de2), 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-862: Missing Authorization, where the product does not perform an authorization check when an actor attempts to access a resource or perform an action. 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

### Summary `/api/file/getUniqueFilename` takes a path from the request body and passes it to a filesystem existence check with no validation, confinement or authorization. The response distinguishes paths that exist from paths that do not, so an anonymous reader in publish mode can probe arbitrary locations on the host, one request per probe. Files and directories both work. Every neighbouring file route either requires an administrator or confines the path first. ### Details **Route.** `kernel/api/router.go:289` on master, `:292` on the development branch, `CheckAuth` only. **Handler,** `kernel/api/file.go:84`, in full: ```go util.ParseJsonArgs(arg, ret, util.BindJsonArg("path", &filePath, true, true)) ret.Data = map[string]any{"path": util.GetUniqueFilename(filePath)} ``` **The argument binder does no path handling.** `util.BindJsonArg` (`kernel/util/net.go:365`) takes two booleans, `required` (the key must be present) and `rejectEmpty` (the value must be non-empty). That is its entire contract. There is no `filepath.Clean`, no workspace join, no traversal check. An absolute path outside the workspace reaches `gulu.File.IsExist` unmodified. **The oracle.** `util/file.go:67`: ```go if !gulu.File.IsExist(filePath) { return filePath } // otherwise loop, returning the first non-existent dir/base (i).ext ``` Input echoed back means the path does not exist. A `(1)` suffix means it does. **The asymmetry.** | Route | Middleware | |---|---| | `putFile`, `copyFile`, `globalCopyFiles`, `workspaceCopyFiles`, `removeFile`, `renameFile` | `CheckAuth`, `CheckAdminRole`, `CheckReadonly` | | `readDir` (directory enumeration) | `CheckAuth`, `CheckAdminRole` | | `getFile` | `CheckAuth`, but `GetAbsPathInWorkspace` returns 403 on escape, plus `rejectEncryptedBoxPath` and a publish-access check | | `getUniqueFilename` | `CheckAuth`, nothing else | `getFile` binds the identical `BindJsonArg("path", &filePath, true, true)` and confines it as its first action. ### Proof of Concept Kernel 3.7.2, publish mode on port 6808, `Publish.Auth.Enable` false, anonymous client. Reader context confirmed by `getChildBlocks` returning HTTP 403 on the same session. ``` {"path":"/etc/passwd"} → {"code":0,"msg":"","data":{"path":"/etc/passwd (1)"}} {"path":"/etc/shadow"} → {"code":0,"msg":"","data":{"path":"/etc/shadow (1)"}} {"path":"/etc/definitely-not-here"} → {"code":0,"msg":"","data":{"path":"/etc/definitely-not-here"}} ``` Directories behave the same way: ``` {"path":"/root"} → {"path":"/root (1)"} {"path":"/rootnope"} → {"path":"/rootnope"} ``` And within the workspace, confirming whether a specific document file exists: ``` {"path":"<workspace>/data/<boxID>/<docID>.sy"} → "<...> (1).sy" {"path":"<workspace>/data/nope-no-such.sy"} → echoed unchanged ``` ### Impact An anonymous reader in publish mode, or any publish `RoleReader`, determines whether arbitrary paths exist on the host. Because directories respond the same way as files, the filesystem can be walked rather than only guessed at. This supports identifying the operating-system user's home directory, detecting installed software, backup and synchronisation directories, mapping the workspace layout, and confirming the existence of specific documents by probing their storage paths. It also composes with the absolute workspace path returned elsewhere, which supplies a concrete prefix rather than requiring guesswork, and it recovers the operating-system username that the browser-request path strip added under issue #17410 was introduced to conceal. Scoped precisely: nothing is read and nothing is written. This is an existence and layout disclosure, valuable primarily as a reconnaissance step. ### Suggested fix Confine the path as the adjacent `getFile` route does, by routing `filePath` through `util.GetAbsPathInWorkspace` and returning 403 on escape, or add `CheckAdminRole` and `CheckReadonly` to match `readDir`. The frontend uses this endpoint only for naming uploads and assets inside the workspace, so neither change affects legitimate use.

CVE-2026-73605 Affected SiYuan Versions

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

References

Frequently Asked Questions

What is CVE-2026-73605?
CVE-2026-73605 is a security vulnerability in SiYuan involving getUniqueFilename passes an unvalidated client-supplied path to the filesystem, giving anonymous readers an existence oracle over the entire host filesystem under CWE-862: Missing Authorization.
Which SiYuan versions are affected?
Affected versions include releases prior to 0.0.0-20260812083335-251596fc0de2 across affected packages: github.com/siyuan-note/siyuan/kernel.
What version fixes CVE-2026-73605?
Upgrading to 0.0.0-20260812083335-251596fc0de2 (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-20260812083335-251596fc0de2. Upgrade your dependency to 0.0.0-20260812083335-251596fc0de2 or later.
What is the CVSS score of CVE-2026-73605?
CVE-2026-73605 has a CVSS v3.1 base score of 5.8 (MODERATE) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N.
What CWE is associated with CVE-2026-73605?
CVE-2026-73605 is classified under CWE-862: Missing Authorization (The product does not perform an authorization check when an actor attempts to access a resource or perform an action.).
How can I check Go dependencies for CVE-2026-73605?
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-hf8h-97gm-4x2p",
  "modified" : "2026-10-01T15:46:01Z",
  "published" : "2026-10-01T15:46:01Z",
  "aliases" : [ "CVE-2026-73605" ],
  "summary" : "SiYuan: getUniqueFilename passes an unvalidated client-supplied path to the filesystem, giving anonymous readers an existence oracle over the entire host filesystem",
  "details" : "### Summary\n\n`/api/file/getUniqueFilename` takes a path from the request body and passes it to a filesystem existence check with no validation, confinement or authorization. The response distinguishes paths that exist from paths that do not, so an anonymous reader in publish mode can probe arbitrary locations on the host, one request per probe. Files and directories both work.\n\nEvery neighbouring file route either requires an administrator or confines the path first.\n\n### Details\n\n**Route.** `kernel/api/router.go:289` on master, `:292` on the development branch, `CheckAuth` only.\n\n**Handler,** `kernel/api/file.go:84`, in full:\n\n```go\nutil.ParseJsonArgs(arg, ret, util.BindJsonArg(\"path\", &filePath, true, true))\nret.Data = map[string]any{\"path\": util.GetUniqueFilename(filePath)}\n```\n\n**The argument binder does no path handling.** `util.BindJsonArg` (`kernel/util/net.go:365`) takes two booleans, `required` (the key must be present) and `rejectEmpty` (the value must be non-empty). That is its entire contract. There is no `filepath.Clean`, no workspace join, no traversal check. An absolute path outside the workspace reaches `gulu.File.IsExist` unmodified.\n\n**The oracle.** `util/file.go:67`:\n\n```go\nif !gulu.File.IsExist(filePath) {\n    return filePath\n}\n// otherwise loop, returning the first non-existent dir/base (i).ext\n```\n\nInput echoed back means the path does not exist. A `(1)` suffix means it does.\n\n**The asymmetry.**\n\n| Route | Middleware |\n|---|---|\n| `putFile`, `copyFile`, `globalCopyFiles`, `workspaceCopyFiles`, `removeFile`, `renameFile` | `CheckAuth`, `CheckAdminRole`, `CheckReadonly` |\n| `readDir` (directory enumeration) | `CheckAuth`, `CheckAdminRole` |\n| `getFile` | `CheckAuth`, but `GetAbsPathInWorkspace` returns 403 on escape, plus `rejectEncryptedBoxPath` and a publish-access check |\n| `getUniqueFilename` | `CheckAuth`, nothing else |\n\n`getFile` binds the identical `BindJsonArg(\"path\", &filePath, true, true)` and confines it as its first action.\n\n### Proof of Concept\n\nKernel 3.7.2, publish mode on port 6808, `Publish.Auth.Enable` false, anonymous client. Reader context confirmed by `getChildBlocks` returning HTTP 403 on the same session.\n\n```\n{\"path\":\"/etc/passwd\"}\n→ {\"code\":0,\"msg\":\"\",\"data\":{\"path\":\"/etc/passwd (1)\"}}\n\n{\"path\":\"/etc/shadow\"}\n→ {\"code\":0,\"msg\":\"\",\"data\":{\"path\":\"/etc/shadow (1)\"}}\n\n{\"path\":\"/etc/definitely-not-here\"}\n→ {\"code\":0,\"msg\":\"\",\"data\":{\"path\":\"/etc/definitely-not-here\"}}\n```\n\nDirectories behave the same way:\n\n```\n{\"path\":\"/root\"}      → {\"path\":\"/root (1)\"}\n{\"path\":\"/rootnope\"}  → {\"path\":\"/rootnope\"}\n```\n\nAnd within the workspace, confirming whether a specific document file exists:\n\n```\n{\"path\":\"<workspace>/data/<boxID>/<docID>.sy\"}  → \"<...> (1).sy\"\n{\"path\":\"<workspace>/data/nope-no-such.sy\"}     → echoed unchanged\n```\n\n### Impact\n\nAn anonymous reader in publish mode, or any publish `RoleReader`, determines whether arbitrary paths exist on the host. Because directories respond the same way as files, the filesystem can be walked rather than only guessed at. This supports identifying the operating-system user's home directory, detecting installed software, backup and synchronisation directories, mapping the workspace layout, and confirming the existence of specific documents by probing their storage paths.\n\nIt also composes with the absolute workspace path returned elsewhere, which supplies a concrete prefix rather than requiring guesswork, and it recovers the operating-system username that the browser-request path strip added under issue #17410 was introduced to conceal.\n\nScoped precisely: nothing is read and nothing is written. This is an existence and layout disclosure, valuable primarily as a reconnaissance step.\n\n### Suggested fix\n\nConfine the path as the adjacent `getFile` route does, by routing `filePath` through `util.GetAbsPathInWorkspace` and returning 403 on escape, or add `CheckAdminRole` and `CheckReadonly` to match `readDir`. The frontend uses this endpoint only for naming uploads and assets inside the workspace, so neither change affects legitimate use.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/siyuan-note/siyuan/kernel"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "0.0.0-20260812083335-251596fc0de2"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/siyuan-note/siyuan/security/advisories/GHSA-hf8h-97gm-4x2p"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-73605"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/siyuan-note/siyuan"
  }, {
    "type" : "WEB",
    "url" : "https://www.vulncheck.com/advisories/siyuan-before-path-traversal-via-getuniquefilename"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-862" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-10-01T15:46:01Z",
    "nvd_published_at" : null
  }
}