CVE-2026-69086
highpriority · track · 0EPSS 0.005 · 45 %ileEUUSEPSS · scored 2026-10-27
SiYuan versions before v3.7.3 fail to validate the avID parameter on all code branches in attribute-view read endpoints, allowing attackers to construct traversal paths that escape the storage directory. Authenticated users with RoleReader permissions or anonymous clients when publish authentication is disabled can read JSON files outside the attribute-view directory to disclose cross-scope database content.
cvss
Technical severity, 0–10. One row per scoring source.
base score 0–10 · higher = more severe · hover a vector to decode⚠ sources disagree · medium–high · 5.7–8.3
ssvc
A stakeholder decision — act, attend, or track — from exploitation and impact.
weaknesses
The CWE weakness classes this flaw belongs to.
affected products (2)
Products and version ranges known to be affected.
- Go github.com/siyuan-note/siyuan/kernel< 0.0.0-20260720151813-0f5a0e7c67b0osvUSpkg:golang/github.com/siyuan-note/siyuan/kernel
advisories (2)
Vendor and ecosystem advisories describing this.
- GHSA-7hm9-v7vf-7g4wSiYuan: Path Traversal via unvalidated avID in RenderAttributeView/AV read endpoints : reader-reachable cross-scope attribute-view disclosure2026-09-03ghsa · osvUS
**CVE:** This vulnerability corresponds to [CVE-2026-69086](https://nvd.nist.gov/vuln/detail/CVE-2026-69086). ### Summary Four attribute-view read endpoints build a filesystem path from a caller-controlled `id`/`avID` and read it without confining the result to the attribute-view storage directory (`DataDir/storage/av/`). On the load (file-exists) code path there is no boundary check, so an `avID` containing `../` segments escapes `storage/av/` and causes the kernel to read a `.json` file elsewhere in the workspace. The endpoints require only `CheckAuth`, which the publish service's `RoleReader` token satisfies; when `Publish.Auth.Enable` is `false` the publish proxy uses the anonymous account, making the surface reachable with no credentials. ### Details Affected endpoints (all gated by `CheckAuth` only, no `CheckAdminRole`): - `POST /api/av/renderAttributeView` → `arg["id"]` - `POST /api/av/getAttributeViewKeysByID` → `arg["avID"]` - `POST /api/av/getAttributeViewKeys` → `arg["id"]` - `POST /api/av/getCurrentAttrViewImages` → `arg["id"]` In `model.RenderAttributeView` (`model/attribute_view_render.go`), the only identifier guard `ast.IsNodeIDPattern(avID)` sits **inside** the `if !filelock.IsExist(existPath)` (create) branch: ```go existPath = GetAttributeViewDataPath(avID) // path built from avID, no check if !filelock.IsExist(existPath) { // NOT-EXIST / CREATE branch if !createIfNotExist { return // NotFound } if !ast.IsNodeIDPattern(avID) { // <-- ONLY id guard, create branch only return ErrInvalidID } // ... create ... } attrView, err = av.ParseAttributeView(avID) // LOAD runs unconditionally ``` When the traversal `avID` resolves to a file that already exists, the `!filelock.IsExist(...)` condition is `false`, the entire block (including the line with `ast.IsNodeIDPattern`) is skipped, and control falls straight through to `av.ParseAttributeView(avID)`. That function rebuilds the path via `filepath.Join(DataDir, "storage", "av", avID+".json")` and calls `filelock.ReadFile` with no `filepath.Rel` / `IsSubPath` / `..` rejection: ```go // av.ParseAttributeView -> attributeViewDataPathByBox / GetAttributeViewDataPath avJSONPath = filepath.Join(DataDir, "storage", "av", avID+".json") // no boundary check // -> parseAttributeViewByPathInBox(avJSONPath, boxID) data, _ = filelock.ReadFile(avJSONPath) // SINK ``` `filepath.Join` cleans the path but does **not** reject `..` segments, so it provides no containment. The three `getAttributeView*` endpoints call `ParseAttributeView` with no create branch at all, so they never even reach the `ast.IsNodeIDPattern` check same defect, same auth tier. The root cause is that identifier validation is placed on a single code branch rather than confining the load to the AV base directory, so the load path reads a caller-controlled location. ### PoC **Precondition:** publish mode enabled (default port `6808`); reachable by a `RoleReader` publish token, or anonymously when `Publish.Auth.Enable` is `false`. A request to `/api/av/renderAttributeView` with an `id` composed of `../` path segments that resolves to an existing `.json` file outside `DataDir/storage/av/` causes that file to be read and parsed instead of being rejected, because the identifier validation is only reached on the not-exist/create branch. I have withheld the exact encoded `id` value from this draft to avoid publishing a live traversal against internet-exposed publish instances. I'm happy to provide the precise value and a screenshot privately in this thread on request. ### Impact An authenticated publish `RoleReader` or an anonymous client when publish auth is disabled can cause the kernel to read `.json` files outside the attribute-view directory. Because the loaded file is unmarshalled into the attribute-view structure, the reliable primitives are: 1. Disclosure of attribute-view (database) content from other scopes/notebooks the reader is not authorized to see. 2. A `.json`-path existence oracle for arbitrary workspace locations. Files not conforming to the AV schema are read but reflect little content, and the `.json` suffix is force-appended, so this is **not** a general arbitrary-file read. No admin role, CSRF token, or write permission is required. ### Suggested fix Validate `avID` with `ast.IsNodeIDPattern` before path construction on **all** branches (move it ahead of `FindAttributeViewPath` / `GetAttributeViewDataPath`), or preferably, so every caller inherits it confine at the sink: in `attributeViewDataPathByBox` / `GetAttributeViewDataPath`, compute the joined path and reject it unless `filepath.Rel(avBaseDir, cleaned)` stays within `avBaseDir` (no leading `..`). Sink-side confinement also covers the three `getAttributeView*` endpoints that never reach the create-branch guard.
- GO-2026-6373SiYuan: Path Traversal via unvalidated avID in RenderAttributeView/AV read endpoints : reader-reachable cross-scope attribute-view disclosure in github.com/siyuan-note/siyuan/kernel2026-09-10vendor · osvUS
SiYuan: Path Traversal via unvalidated avID in RenderAttributeView/AV read endpoints : reader-reachable cross-scope attribute-view disclosure in github.com/siyuan-note/siyuan/kernel
also known as (5)
Other names and identifiers for this entity.
sources
Which sources assert this record, and when.
references (6)
External citations — patches, advisories, analysis.
- https://github.com/siyuan-note/siyuan/security/advisories/GHSA-7hm9-v7vf-7g4wcnnvd
- https://www.vulncheck.com/advisories/siyuan-before-path-traversal-via-unvalidated-avidcnnvd
- https://github.com/siyuan-note/siyuan/security/advisories/GHSA-7hm9-v7vf-7g4wenisa_euvd
- https://www.vulncheck.com/advisories/siyuan-before-path-traversal-via-unvalidated-avidenisa_euvd
- https://github.com/siyuan-note/siyuan/security/advisories/GHSA-7hm9-v7vf-7g4wnvd
- https://www.vulncheck.com/advisories/siyuan-before-path-traversal-via-unvalidated-avidnvd
Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.
Free scan