CVE-2026-39310
Description
Trilium Notes is a cross-platform, hierarchical note taking application focused on building large personal knowledge bases. In versions 0.102.1 and prior, the Clipper API in Trilium Desktop (v0.101.3) allows full authentication bypass when running in an Electron environment. When Trilium detects an Electron environment, it explicitly disables authentication middleware for the Clipper API, exposing endpoints such as /api/clipper/notes to the network with no password, API token, or CSRF protection. An attacker on a shared network (for example, a corporate LAN or public Wi-Fi) can scan for open high-range ports using a tool like nmap, since Trilium often binds to ports such as 37840. Once a candidate port is found, an unauthenticated request to the Clipper handshake endpoint, which also bypasses authentication, confirms a Trilium instance by returning the application name and protocol version. This facilitates unauthorized data access, phishing, and local system compromise. The issue has been fixed in version 0.102.2.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Trilium Notes Desktop v0.101.3–v0.102.1 disables authentication for the Clipper API in Electron mode, allowing unauthenticated remote attackers on the same network to access and inject notes.
Vulnerability
In Trilium Notes Desktop versions 0.101.3 through 0.102.1, the Clipper API routes (e.g., /api/clipper/notes) are exposed without authentication when the application detects an Electron environment. The check in apps/server/src/routes/routes.ts explicitly sets the middleware to an empty array for Electron builds, bypassing the normal ETAPI token validation [1]. Affected versions include all releases prior to 0.102.2.
Exploitation
An attacker on the same network (e.g., a corporate LAN or public Wi-Fi) can scan for open high-range ports (such as 37840) using tools like nmap. Once an open port is found, an unauthenticated GET /api/clipper/handshake request confirms the instance by returning {"appName":"trilium",...} [1]. The attacker can then craft a POST /api/clipper/notes request to inject arbitrary note content (e.g., a phishing message) directly into the victim's database—no credentials, API token, or CSRF token required.
Impact
A successful attack allows the attacker to write malicious notes into the victim's Trilium knowledge base, which can contain phishing links or other harmful content. This can lead to unauthorized data access (since the attacker can also read notes if they enumerate endpoints), social engineering attacks, and potential local system compromise if the injected content is rendered unsafely (e.g., via SVG or Mermaid diagrams, which were also patched in 0.102.2 [2]).
Mitigation
The vulnerability is fixed in Trilium Notes Desktop version 0.102.2 [2]. All users running 0.102.1 or earlier should upgrade immediately. No workaround is available for the Clipper API authentication bypass. The vendor also enabled Electron fuses and improved application integrity checks in the same release to harden the desktop application against further abuse [2].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2(expand)+ 1 more
- (no CPE)
- (no CPE)range: <=0.102.1
Patches
213b1e0afbbd9fix(desktop): make failing due to wrong version of fuses
2 files changed · +12 −23
apps/desktop/package.json+1 −1 modified@@ -40,7 +40,7 @@ "@electron-forge/maker-zip": "7.11.1", "@electron-forge/plugin-auto-unpack-natives": "7.11.1", "@electron-forge/plugin-fuses": "7.11.1", - "@electron/fuses": "1.0.0", + "@electron/fuses": "1.8.0", "@triliumnext/commons": "workspace:*", "@triliumnext/server": "workspace:*", "@types/electron-squirrel-startup": "1.0.2",
pnpm-lock.yaml+11 −22 modified@@ -447,10 +447,10 @@ importers: version: 7.11.1 '@electron-forge/plugin-fuses': specifier: 7.11.1 - version: 7.11.1(@electron/fuses@1.0.0) + version: 7.11.1(@electron/fuses@1.8.0) '@electron/fuses': - specifier: 1.0.0 - version: 1.0.0 + specifier: 1.8.0 + version: 1.8.0 '@triliumnext/commons': specifier: workspace:* version: link:../../packages/commons @@ -2394,8 +2394,9 @@ packages: engines: {node: '>=10.12.0'} hasBin: true - '@electron/fuses@1.0.0': - resolution: {integrity: sha512-VjWIlZHEB7a93tXl+6tX2YzN+s1/mS0RM8WX4GZlMOqAzlmRfTMP6pp0MM0LtkzWZB+KQOv+zJt5Dlgdik+DUQ==} + '@electron/fuses@1.8.0': + resolution: {integrity: sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==} + hasBin: true '@electron/get@2.0.3': resolution: {integrity: sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==} @@ -16658,8 +16659,6 @@ snapshots: '@ckeditor/ckeditor5-widget': 47.4.0 ckeditor5: 47.4.0 es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-icons@47.4.0': {} @@ -16702,8 +16701,6 @@ snapshots: '@ckeditor/ckeditor5-ui': 47.4.0 '@ckeditor/ckeditor5-utils': 47.4.0 ckeditor5: 47.4.0 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-inspector@5.0.0': {} @@ -16713,8 +16710,6 @@ snapshots: '@ckeditor/ckeditor5-ui': 47.4.0 '@ckeditor/ckeditor5-utils': 47.4.0 ckeditor5: 47.4.0 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-line-height@47.4.0': dependencies: @@ -16739,8 +16734,6 @@ snapshots: '@ckeditor/ckeditor5-widget': 47.4.0 ckeditor5: 47.4.0 es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-list-multi-level@47.4.0': dependencies: @@ -16764,8 +16757,6 @@ snapshots: '@ckeditor/ckeditor5-ui': 47.4.0 '@ckeditor/ckeditor5-utils': 47.4.0 ckeditor5: 47.4.0 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-markdown-gfm@47.4.0': dependencies: @@ -16803,8 +16794,6 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.4.0 '@ckeditor/ckeditor5-widget': 47.4.0 ckeditor5: 47.4.0 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-mention@47.4.0(patch_hash=5981fb59ba35829e4dff1d39cf771000f8a8fdfa7a34b51d8af9549541f2d62d)': dependencies: @@ -17618,11 +17607,11 @@ snapshots: - bluebird - supports-color - '@electron-forge/plugin-fuses@7.11.1(@electron/fuses@1.0.0)': + '@electron-forge/plugin-fuses@7.11.1(@electron/fuses@1.8.0)': dependencies: '@electron-forge/plugin-base': 7.11.1 '@electron-forge/shared-types': 7.11.1 - '@electron/fuses': 1.0.0 + '@electron/fuses': 1.8.0 transitivePeerDependencies: - bluebird - supports-color @@ -17709,9 +17698,11 @@ snapshots: glob: 7.2.3 minimatch: 3.1.2 - '@electron/fuses@1.0.0': + '@electron/fuses@1.8.0': dependencies: + chalk: 4.1.2 fs-extra: 9.1.0 + minimist: 1.2.8 '@electron/get@2.0.3': dependencies: @@ -23164,8 +23155,6 @@ snapshots: ckeditor5-collaboration@47.4.0: dependencies: '@ckeditor/ckeditor5-collaboration-core': 47.4.0 - transitivePeerDependencies: - - supports-color ckeditor5-premium-features@47.4.0(bufferutil@4.0.9)(ckeditor5@47.4.0)(utf-8-validate@6.0.5): dependencies:
176de87b6b45feat(desktop): add Electron fuses
3 files changed · +63 −33
apps/desktop/electron-forge/forge.config.ts+12 −0 modified@@ -1,4 +1,5 @@ import type { ForgeConfig } from "@electron-forge/shared-types"; +import { FuseV1Options, FuseVersion } from "@electron/fuses"; import { LOCALES } from "@triliumnext/commons"; import { existsSync } from "fs"; import fs from "fs-extra"; @@ -166,6 +167,17 @@ const config: ForgeConfig = { { name: "@electron-forge/plugin-auto-unpack-natives", config: {} + }, + { + name: "@electron-forge/plugin-fuses", + config: { + version: FuseVersion.V1, + [FuseV1Options.RunAsNode]: false, + [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false, + [FuseV1Options.EnableNodeCliInspectArguments]: false, + [FuseV1Options.EnableCookieEncryption]: true, + [FuseV1Options.OnlyLoadAppFromAsar]: true + } } ], hooks: {
apps/desktop/package.json+9 −7 modified@@ -27,15 +27,10 @@ "electron-debug": "4.1.0", "electron-dl": "4.0.0", "electron-squirrel-startup": "1.0.1", - "jquery.fancytree": "2.38.5", - "jquery-hotkeys": "0.2.2" + "jquery-hotkeys": "0.2.2", + "jquery.fancytree": "2.38.5" }, "devDependencies": { - "@types/electron-squirrel-startup": "1.0.2", - "@triliumnext/commons": "workspace:*", - "@triliumnext/server": "workspace:*", - "copy-webpack-plugin": "13.0.1", - "electron": "40.6.1", "@electron-forge/cli": "7.11.1", "@electron-forge/maker-deb": "7.11.1", "@electron-forge/maker-dmg": "7.11.1", @@ -44,6 +39,13 @@ "@electron-forge/maker-squirrel": "7.11.1", "@electron-forge/maker-zip": "7.11.1", "@electron-forge/plugin-auto-unpack-natives": "7.11.1", + "@electron-forge/plugin-fuses": "7.11.1", + "@electron/fuses": "2.1.1", + "@triliumnext/commons": "workspace:*", + "@triliumnext/server": "workspace:*", + "@types/electron-squirrel-startup": "1.0.2", + "copy-webpack-plugin": "13.0.1", + "electron": "40.6.1", "prebuild-install": "7.1.3" } } \ No newline at end of file
pnpm-lock.yaml+42 −26 modified@@ -445,6 +445,12 @@ importers: '@electron-forge/plugin-auto-unpack-natives': specifier: 7.11.1 version: 7.11.1 + '@electron-forge/plugin-fuses': + specifier: 7.11.1 + version: 7.11.1(@electron/fuses@2.1.1) + '@electron/fuses': + specifier: 2.1.1 + version: 2.1.1 '@triliumnext/commons': specifier: workspace:* version: link:../../packages/commons @@ -2345,6 +2351,12 @@ packages: resolution: {integrity: sha512-lKpSOV1GA3FoYiD9k05i6v4KaQVmojnRgCr7d6VL1bFp13QOtXSaAWhFI9mtSY7rGElOacX6Zt7P7rPoB8T9eQ==} engines: {node: '>= 16.4.0'} + '@electron-forge/plugin-fuses@7.11.1': + resolution: {integrity: sha512-Td517mHf+RjQAayFDM2kKb7NaGdRXrZfPbc7KOHlGbXthp5YTkFu2cCZGWokiqt1y1wsFaAodULhqBIg7vbbbw==} + engines: {node: '>= 16.4.0'} + peerDependencies: + '@electron/fuses': ^1.0.0 + '@electron-forge/publisher-base@7.11.1': resolution: {integrity: sha512-rXE9oMFGMtdQrixnumWYH5TTGsp99iPHZb3jI74YWq518ctCh6DlIgWlhf6ok2X0+lhWovcIb45KJucUFAQ13w==} engines: {node: '>= 16.4.0'} @@ -2382,6 +2394,11 @@ packages: engines: {node: '>=10.12.0'} hasBin: true + '@electron/fuses@2.1.1': + resolution: {integrity: sha512-38ho27/mtUV/LpsZ1LCDJUomKBBSUZDk/qBH4FNNtoN5fmnkmWDcIp5pm1Kv3InqhRjKZKs7Jzx+wWZNMArHrA==} + engines: {node: '>=22.12.0'} + hasBin: true + '@electron/get@2.0.3': resolution: {integrity: sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==} engines: {node: '>=12'} @@ -16058,6 +16075,8 @@ snapshots: '@ckeditor/ckeditor5-core': 47.4.0 '@ckeditor/ckeditor5-upload': 47.4.0 ckeditor5: 47.4.0 + transitivePeerDependencies: + - supports-color '@ckeditor/ckeditor5-ai@47.4.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)': dependencies: @@ -16198,12 +16217,16 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.4.0 '@ckeditor/ckeditor5-widget': 47.4.0 es-toolkit: 1.39.5 + transitivePeerDependencies: + - supports-color '@ckeditor/ckeditor5-cloud-services@47.4.0': dependencies: '@ckeditor/ckeditor5-core': 47.4.0 '@ckeditor/ckeditor5-utils': 47.4.0 ckeditor5: 47.4.0 + transitivePeerDependencies: + - supports-color '@ckeditor/ckeditor5-code-block@47.4.0(patch_hash=2361d8caad7d6b5bddacc3a3b4aa37dbfba260b1c1b22a450413a79c1bb1ce95)': dependencies: @@ -16396,6 +16419,8 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.4.0 ckeditor5: 47.4.0 es-toolkit: 1.39.5 + transitivePeerDependencies: + - supports-color '@ckeditor/ckeditor5-editor-classic@47.4.0': dependencies: @@ -16405,6 +16430,8 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.4.0 ckeditor5: 47.4.0 es-toolkit: 1.39.5 + transitivePeerDependencies: + - supports-color '@ckeditor/ckeditor5-editor-decoupled@47.4.0': dependencies: @@ -16414,6 +16441,8 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.4.0 ckeditor5: 47.4.0 es-toolkit: 1.39.5 + transitivePeerDependencies: + - supports-color '@ckeditor/ckeditor5-editor-inline@47.4.0': dependencies: @@ -16447,8 +16476,6 @@ snapshots: '@ckeditor/ckeditor5-table': 47.4.0 '@ckeditor/ckeditor5-utils': 47.4.0 ckeditor5: 47.4.0 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-emoji@47.4.0': dependencies: @@ -16505,8 +16532,6 @@ snapshots: '@ckeditor/ckeditor5-ui': 47.4.0 '@ckeditor/ckeditor5-utils': 47.4.0 ckeditor5: 47.4.0 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-export-word@47.4.0': dependencies: @@ -16531,6 +16556,8 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.4.0 ckeditor5: 47.4.0 es-toolkit: 1.39.5 + transitivePeerDependencies: + - supports-color '@ckeditor/ckeditor5-font@47.4.0': dependencies: @@ -16666,8 +16693,6 @@ snapshots: '@ckeditor/ckeditor5-ui': 47.4.0 '@ckeditor/ckeditor5-utils': 47.4.0 ckeditor5: 47.4.0 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-indent@47.4.0': dependencies: @@ -16791,8 +16816,6 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.4.0 ckeditor5: 47.4.0 es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-merge-fields@47.4.0': dependencies: @@ -16805,8 +16828,6 @@ snapshots: '@ckeditor/ckeditor5-widget': 47.4.0 ckeditor5: 47.4.0 es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-minimap@47.4.0': dependencies: @@ -16815,8 +16836,6 @@ snapshots: '@ckeditor/ckeditor5-ui': 47.4.0 '@ckeditor/ckeditor5-utils': 47.4.0 ckeditor5: 47.4.0 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-operations-compressor@47.4.0': dependencies: @@ -16871,8 +16890,6 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.4.0 '@ckeditor/ckeditor5-widget': 47.4.0 ckeditor5: 47.4.0 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-pagination@47.4.0': dependencies: @@ -16992,8 +17009,6 @@ snapshots: '@ckeditor/ckeditor5-ui': 47.4.0 '@ckeditor/ckeditor5-utils': 47.4.0 ckeditor5: 47.4.0 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-source-editing-enhanced@47.4.0': dependencies: @@ -17041,8 +17056,6 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.4.0 ckeditor5: 47.4.0 es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-table@47.4.0': dependencies: @@ -17055,8 +17068,6 @@ snapshots: '@ckeditor/ckeditor5-widget': 47.4.0 ckeditor5: 47.4.0 es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-template@47.4.0': dependencies: @@ -17131,8 +17142,6 @@ snapshots: '@ckeditor/ckeditor5-icons': 47.4.0 '@ckeditor/ckeditor5-ui': 47.4.0 '@ckeditor/ckeditor5-utils': 47.4.0 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-upload@47.4.0': dependencies: @@ -17169,8 +17178,6 @@ snapshots: '@ckeditor/ckeditor5-engine': 47.4.0 '@ckeditor/ckeditor5-utils': 47.4.0 es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-widget@47.4.0': dependencies: @@ -17190,8 +17197,6 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.4.0 ckeditor5: 47.4.0 es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@codemirror/autocomplete@6.18.6': dependencies: @@ -17615,6 +17620,15 @@ snapshots: - bluebird - supports-color + '@electron-forge/plugin-fuses@7.11.1(@electron/fuses@2.1.1)': + dependencies: + '@electron-forge/plugin-base': 7.11.1 + '@electron-forge/shared-types': 7.11.1 + '@electron/fuses': 2.1.1 + transitivePeerDependencies: + - bluebird + - supports-color + '@electron-forge/publisher-base@7.11.1': dependencies: '@electron-forge/shared-types': 7.11.1 @@ -17697,6 +17711,8 @@ snapshots: glob: 7.2.3 minimatch: 3.1.2 + '@electron/fuses@2.1.1': {} + '@electron/get@2.0.3': dependencies: debug: 4.4.3(supports-color@8.1.1)
Vulnerability mechanics
Root cause
"The Clipper API in Trilium Desktop disables all authentication middleware when running in an Electron environment, exposing endpoints to the network without any credential checks."
Attack vector
An attacker on the same network (e.g., corporate LAN or public Wi-Fi) scans for open high-range ports where Trilium Desktop typically binds (e.g., port 37840). Once a candidate port is found, the attacker sends an unauthenticated request to the Clipper handshake endpoint, which returns the application name and protocol version without requiring any password, API token, or CSRF token. The attacker can then call endpoints such as /api/clipper/notes to read, create, or modify notes, enabling unauthorized data access, phishing, and potential local system compromise [CWE-306].
Affected code
The advisory states that when Trilium detects an Electron environment, it explicitly disables authentication middleware for the Clipper API, exposing endpoints such as /api/clipper/notes. The patch modifies `apps/desktop/electron-forge/forge.config.ts` and `apps/desktop/package.json` to add Electron Fuses configuration; it does not show changes to the Clipper API authentication logic itself.
What the fix does
The patch adds the `@electron-forge/plugin-fuses` and `@electron/fuses` dependencies and configures Electron Fuses in the Forge config [patch_id=877571]. Specifically, it enables `OnlyLoadAppFromAsar`, disables `RunAsNode`, disables `EnableNodeOptionsEnvironmentVariable`, disables `EnableNodeCliInspectArguments`, and enables `EnableCookieEncryption`. These fuses harden the Electron runtime by preventing the app from being run as a plain Node.js process, blocking environment-variable-based injection, disabling CLI inspect arguments, and ensuring cookies are encrypted. While the patch does not directly modify the authentication-bypass logic in the Clipper API, it mitigates the impact by making local system compromise via the Electron environment significantly harder.
Preconditions
- configTrilium Desktop must be running in an Electron environment (the default for the desktop app).
- networkThe attacker must be on the same network as the victim (e.g., shared LAN, public Wi-Fi).
- authNo authentication credentials (password, API token, CSRF token) are required.
Generated on May 20, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2News mentions
0No linked articles in our index yet.