**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...
Full CISO analysis pending enrichment.
What systems are affected?
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| Jupyter Notebook | go | < 0.0.0-20260720151813-0f5a0e7c67b0 | 0.0.0-20260720151813-0f5a0e7c67b0 |
Do you use Jupyter Notebook? You're affected.
How severe is it?
What is the attack surface?
What should I do?
Patch available
Update Jupyter Notebook to version 0.0.0-20260720151813-0f5a0e7c67b0
Which compliance frameworks are affected?
Compliance analysis pending. Sign in for full compliance mapping when available.
Frequently Asked Questions
What is CVE-2026-69086?
**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.
Is CVE-2026-69086 actively exploited?
No confirmed active exploitation of CVE-2026-69086 has been reported, but organizations should still patch proactively.
How to fix CVE-2026-69086?
Update to patched version: Jupyter Notebook 0.0.0-20260720151813-0f5a0e7c67b0.
What is the CVSS score for CVE-2026-69086?
CVE-2026-69086 has a CVSS v3.1 base score of 7.7 (HIGH). The EPSS exploitation probability is 0.35%.
What are the technical details?
Original Advisory
**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.
Weaknesses (CWE)
CWE-22 — Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'): The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
- [Implementation] Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a list of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does. When performing input validation, consider all potentially relevant properties, including length, type of input, the full range of acceptable values, missing or extra inputs, syntax, consistency across related fields, and conformance to business rules. As an example of business rule logic, "boat" may be syntactically valid because it only contains alphanumeric characters, but it is not valid if the input is only expected to contain colors such as "red" or "blue." Do not rely exclusively on looking for malicious or malformed inputs. This is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, denylis
- [Architecture and Design] For any security checks that are performed on the client side, ensure that these checks are duplicated on the server side, in order to avoid CWE-602. Attackers can bypass the client-side checks by modifying values after the checks have been performed, or by changing the client to remove the client-side checks entirely. Then, these modified values would be submitted to the server.
Source: MITRE CWE corpus.
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N References
Timeline
Related Vulnerabilities
CVE-2026-69084 10.0 SiYuan: SQL injection in search endpoint exposes notebooks
Same package: notebook CVE-2026-72811 10.0 SiYuan: SQL injection enables cross-notebook DB access
Same package: notebook CVE-2026-69083 10.0 SiYuan: unauthenticated SQLi in full-text search endpoint
Same package: notebook CVE-2026-44727 9.0 jupyter-server: stored XSS yields kernel RCE
Same package: notebook CVE-2026-52798 8.9 Gogs: Stored XSS via .ipynb Markdown re-render bypass
Same package: notebook