CVE-2026-73606

GHSA-vg99-7gj7-2fr5 MEDIUM
Published October 1, 2026

### Summary `/api/block/getRefIDs` filters its results for reader roles through a helper that checks only the visibility tiers. The password tier is not checked, because the helper does not receive the request context and therefore cannot evaluate the publish auth cookie. A reader who has not...

Full CISO analysis pending enrichment.

What systems are affected?

Package Ecosystem Vulnerable Range Patched
Jupyter Notebook go < 0.0.0-20260812083335-251596fc0de2 0.0.0-20260812083335-251596fc0de2
13.4K OpenSSF 5.8 3.0K dependents Pushed 8d ago 85% patched ~92d to patch Full package profile →

Do you use Jupyter Notebook? You're affected.

How severe is it?

CVSS 3.1
5.8 / 10
EPSS
0.3%
chance of exploitation in 30 days
Higher than 23% of all CVEs
Exploitation Status
No known exploitation
Sophistication
N/A

What is the attack surface?

AV AC PR UI S C I A
AV Network
AC Low
PR None
UI None
S Changed
C Low
I None
A None

What should I do?

Patch available

Update Jupyter Notebook to version 0.0.0-20260812083335-251596fc0de2

Which compliance frameworks are affected?

Compliance analysis pending. Sign in for full compliance mapping when available.

Frequently Asked Questions

What is CVE-2026-73606?

### Summary `/api/block/getRefIDs` filters its results for reader roles through a helper that checks only the visibility tiers. The password tier is not checked, because the helper does not receive the request context and therefore cannot evaluate the publish auth cookie. A reader who has not entered a document's publish password learns that the document references a given block. A function ten lines away in the same file does perform the full check, on the same input type. ### Details **Route,** identical at `eef105683` (`kernel/api/router.go:235`) and dev `a7ae96ce` (`:245`): ```go ginServer.Handle("POST", "/api/block/getRefIDs", model.CheckAuth, getRefIDs) ``` No `CheckReadonly`, no `CheckAdminRole`. **The filter chain.** `getRefIDs` (`kernel/api/block.go:631`) checks `isEncryptedNotebookDeniedForPublish`, calls `model.GetBlockRefsInBox`, then for read-only roles: ```go publishIgnore := model.GetInvisiblePublishAccess(publishAccess) refDefs, originalRefBlockIDs = model.FilterRefDefsByPublishIgnore(publishIgnore, refDefs) ``` `FilterRefDefsByPublishIgnore` (`kernel/model/publish_access.go:1324`) collects the reference and definition identifiers, resolves their block trees, and delegates the decision to `FilterBlockTreesByPublishIgnore` (`:1314`), whose entire body is: ```go for id, bt := range bts { if CheckPathAccessableByPublishIgnore(bt.BoxID, bt.Path, publishIgnore) { ret[id] = bt } } ``` Inside that helper, `CheckPublishAuthCookie`, `GetPathPasswordByPublishAccess`, `checkBlockTreeAccessableByPublishAccess` and `password` all appear zero times. **The complete check exists in the same file.** `kernel/model/publish_access.go:431`: ```go func checkBlockTreeAccessableByPublishAccess(c *gin.Context, publishAccess PublishAccess, bt *treenode.BlockTree) bool { if bt == nil || IsEncryptedBoxDeniedByPublishAccess(bt.BoxID) { return false } publishIgnore := filterDisablePublishAccess(publishAccess) passwordID, password := GetPathPasswordByPublishAccess(bt.BoxID, bt.Path, publishAccess) return CheckPathAccessableByPublishIgnore(bt.BoxID, bt.Path, publishIgnore) && (password == "" || CheckPublishAuthCookie(c, passwordID, password)) } ``` Same package, same file, same `*treenode.BlockTree` input. One takes the context and evaluates the password; the other does not receive it and structurally cannot. **Route-level contrast.** The adjacent `getChildBlocks` and `getTailChildBlocks` both carry `model.CheckAdminRole`. **Note on a prior assessment.** `getRefIDs` has been described as correctly filtered because it calls a publish-access filter. It does. The filter it calls covers the visibility tiers only. ### Proof of Concept Kernel 3.7.2, publish mode on port 6808, `Publish.Auth.Enable` false, anonymous client. A public document referenced from a second document whose publish tier was changed between runs. `getRefIDs` was called anonymously each time. | Tier of the referring document | Anonymous result | |---|---| | Public | reference returned (baseline) | | Password-protected | **reference still returned** | | Hidden | `[]`, filtered | | Forbidden | `[]`, filtered | The hidden and forbidden rows confirm the filter runs and works. The password-protected row is the defect. ### Impact An anonymous reader in publish mode, or any publish `RoleReader`, learns that a password-protected document contains a reference to a given block, without entering that document's password, and receives the block identifiers involved. Scoped precisely: the response carries identifiers only. `type RefDefs { RefID string; DefIDs []string }`, returned alongside `originalRefBlockIDs`, a map of identifier to identifier. There is no reference text, title or content. The disclosure is the existence of a relationship, plus identifiers usable as input to other endpoints. Confidentiality only. ### Suggested fix Thread `*gin.Context` into `FilterRefDefsByPublishIgnore` and `FilterBlockTreesByPublishIgnore`, and use `checkBlockTreeAccessableByPublishAccess` in place of the bare `CheckPathAccessableByPublishIgnore` call, so the password tier and the encrypted-box check are both applied.

Is CVE-2026-73606 actively exploited?

No confirmed active exploitation of CVE-2026-73606 has been reported, but organizations should still patch proactively.

How to fix CVE-2026-73606?

Update to patched version: Jupyter Notebook 0.0.0-20260812083335-251596fc0de2.

What is the CVSS score for CVE-2026-73606?

CVE-2026-73606 has a CVSS v3.1 base score of 5.8 (MEDIUM). The EPSS exploitation probability is 0.33%.

What are the technical details?

Original Advisory

### Summary `/api/block/getRefIDs` filters its results for reader roles through a helper that checks only the visibility tiers. The password tier is not checked, because the helper does not receive the request context and therefore cannot evaluate the publish auth cookie. A reader who has not entered a document's publish password learns that the document references a given block. A function ten lines away in the same file does perform the full check, on the same input type. ### Details **Route,** identical at `eef105683` (`kernel/api/router.go:235`) and dev `a7ae96ce` (`:245`): ```go ginServer.Handle("POST", "/api/block/getRefIDs", model.CheckAuth, getRefIDs) ``` No `CheckReadonly`, no `CheckAdminRole`. **The filter chain.** `getRefIDs` (`kernel/api/block.go:631`) checks `isEncryptedNotebookDeniedForPublish`, calls `model.GetBlockRefsInBox`, then for read-only roles: ```go publishIgnore := model.GetInvisiblePublishAccess(publishAccess) refDefs, originalRefBlockIDs = model.FilterRefDefsByPublishIgnore(publishIgnore, refDefs) ``` `FilterRefDefsByPublishIgnore` (`kernel/model/publish_access.go:1324`) collects the reference and definition identifiers, resolves their block trees, and delegates the decision to `FilterBlockTreesByPublishIgnore` (`:1314`), whose entire body is: ```go for id, bt := range bts { if CheckPathAccessableByPublishIgnore(bt.BoxID, bt.Path, publishIgnore) { ret[id] = bt } } ``` Inside that helper, `CheckPublishAuthCookie`, `GetPathPasswordByPublishAccess`, `checkBlockTreeAccessableByPublishAccess` and `password` all appear zero times. **The complete check exists in the same file.** `kernel/model/publish_access.go:431`: ```go func checkBlockTreeAccessableByPublishAccess(c *gin.Context, publishAccess PublishAccess, bt *treenode.BlockTree) bool { if bt == nil || IsEncryptedBoxDeniedByPublishAccess(bt.BoxID) { return false } publishIgnore := filterDisablePublishAccess(publishAccess) passwordID, password := GetPathPasswordByPublishAccess(bt.BoxID, bt.Path, publishAccess) return CheckPathAccessableByPublishIgnore(bt.BoxID, bt.Path, publishIgnore) && (password == "" || CheckPublishAuthCookie(c, passwordID, password)) } ``` Same package, same file, same `*treenode.BlockTree` input. One takes the context and evaluates the password; the other does not receive it and structurally cannot. **Route-level contrast.** The adjacent `getChildBlocks` and `getTailChildBlocks` both carry `model.CheckAdminRole`. **Note on a prior assessment.** `getRefIDs` has been described as correctly filtered because it calls a publish-access filter. It does. The filter it calls covers the visibility tiers only. ### Proof of Concept Kernel 3.7.2, publish mode on port 6808, `Publish.Auth.Enable` false, anonymous client. A public document referenced from a second document whose publish tier was changed between runs. `getRefIDs` was called anonymously each time. | Tier of the referring document | Anonymous result | |---|---| | Public | reference returned (baseline) | | Password-protected | **reference still returned** | | Hidden | `[]`, filtered | | Forbidden | `[]`, filtered | The hidden and forbidden rows confirm the filter runs and works. The password-protected row is the defect. ### Impact An anonymous reader in publish mode, or any publish `RoleReader`, learns that a password-protected document contains a reference to a given block, without entering that document's password, and receives the block identifiers involved. Scoped precisely: the response carries identifiers only. `type RefDefs { RefID string; DefIDs []string }`, returned alongside `originalRefBlockIDs`, a map of identifier to identifier. There is no reference text, title or content. The disclosure is the existence of a relationship, plus identifiers usable as input to other endpoints. Confidentiality only. ### Suggested fix Thread `*gin.Context` into `FilterRefDefsByPublishIgnore` and `FilterBlockTreesByPublishIgnore`, and use `checkBlockTreeAccessableByPublishAccess` in place of the bare `CheckPathAccessableByPublishIgnore` call, so the password tier and the encrypted-box check are both applied.

Weaknesses (CWE)

CWE-863 — Incorrect Authorization: The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check.

  • [Architecture and Design] Divide the product into anonymous, normal, privileged, and administrative areas. Reduce the attack surface by carefully mapping roles with data and functionality. Use role-based access control (RBAC) [REF-229] to enforce the roles at the appropriate boundaries. Note that this approach may not protect against horizontal authorization, i.e., it will not protect a user from attacking others with the same role.
  • [Architecture and Design] Ensure that access control checks are performed related to the business logic. These checks may be different than the access control checks that are applied to more generic resources such as files, connections, processes, memory, and database records. For example, a database may restrict access for medical records to a specific database user, but each record might only be intended to be accessible to the patient and the patient's doctor [REF-7].

Source: MITRE CWE corpus.

CVSS Vector

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N

Timeline

Published
October 1, 2026
Last Modified
October 1, 2026
First Seen
October 1, 2026

Related Vulnerabilities