publishAccess.json is an opt-out list. The publish gate returns accessible for anything not explicitly listed in it. Encrypted notebooks are never written into that file, because only the administrator-gated setPublishAccess writes it and no part of the encryption subsystem does. Consequently every encrypted notebook is publish-accessible as far as the gate is concerned. While an encrypted notebook is unlocked, an anonymous reader in publish mode can list it, enumerate its …
/api/notebook/getNotebookInfo is registered with CheckAuth only and applies no authorization check of any kind. Given a notebook identifier, it returns that notebook's author-written name, document count, total size and creation and modification timestamps. It does so for every notebook the server has loaded, including notebooks that lsNotebooks deliberately withholds from readers because they are closed or not publish-visible.
/api/av/getAttributeViewFieldViews is registered with CheckAuth only and applies no publish-access filtering. Given a database avID, it returns the complete view structure of that database: every view's name, icon, layout type and the per-field Hidden flag, regardless of whether the caller is authorized to see that database or those views. The route registered immediately before it, getAttributeViewKeys, does gate reader roles.
/api/system/getConf returns Conf.CookieKey, the key used to sign the server's session cookies in its response body. The endpoint is registered with CheckAuth only, so the field reaches the publish RoleReader token and the anonymous account when Publish.Auth.Enable is false. The configuration-export endpoint in the same file strips this exact field before returning config, so the project already treats it as secret. The reader-facing masking path does not.
/api/tag/getTag filters its results for reader roles through FilterTagsByPublishIgnore, which checks only the visible publish tier. Documents that are published but password-protected pass that check, so a reader who has never entered a document's publish password receives every tag label used inside it, together with occurrence counts. The project has already treated this exact tier mismatch as a vulnerability on a sibling path: commit 82e9ded42 ("Enforce publish access for graph …
Several static-file routes in the server mux (kernel/server/serve.go) are registered with CheckAuth only and serve directories directly, without the publish-access checks, sensitive-path blocklist, or refuseToAccess rules that the REST API applies to the same data. They are therefore reachable by the publish RoleReader token and by the anonymous account when Publish.Auth.Enable is false. Most notably, /templates/ serves data/templates a directory the REST file API explicitly refuses to serve to non-administrators.
renderAttributeView correctly applies the reader publish-access filter, but the filter's row-accessibility decision is keyed solely to the row's first cell, and it never inspects the remaining cells' values. Relation and Rollup cells carry mirrored content from a different database, so a row belonging to a published database can hand an anonymous reader the contents of a related database whose host document is hidden, publish-forbidden, or password-protected. Separately, when the first …
/api/system/getConf is registered with CheckAuth only and is reachable by the publish RoleReader token, and anonymously when Publish.Auth.Enable is false. Its non-administrator masking chain is a blocklist that enumerates fields individually. Three fields that the configuration-export endpoint in the same file deliberately clears are absent from that blocklist and are returned to readers: | Field | JSON | What it is | Cleared by exportConf at | |—|—|—|—| | Conf.CookieKey …
Five filetree endpoints resolve arbitrary document IDs and paths with no publish-access check of any kind. All are CheckAuth-only, so they are reachable by the publish RoleReader token and by the anonymous account when Publish.Auth.Enable is false. An anonymous reader can map the complete private document tree every notebook, folder, and document title, and which notebook holds each document for documents marked hidden, password-protected, or publish-forbidden, and can resolve titles …
POST /api/notebook/getEncryptedNotebookStatus returns the identifier, name, and current lock state of every encrypted notebook, with no publish-access filtering. The route is registered CheckAuth only, no CheckReadonly, no CheckAdminRole so it is reachable by the publish RoleReader token and by the anonymous account when Publish.Auth.Enable is false. Encrypted notebooks are private by design; their names frequently reveal the sensitive topic that motivated encrypting them.
/api/block/getBlockDOMWithEmbed and /api/block/getBlockDOMsWithEmbed gate only the requested block against publish access. The blocks pulled in by that block's embed (transclusion) query are inlined into the returned DOM with no publish-access check at all, so a reader who requests a legitimately-published block containing an embed query receives the content of every block that query matched including blocks in hidden, forbidden and password-protected documents. The dedicated embed endpoint getEmbedBlock filters these same …
The /api/search/fullTextSearchAssetContent endpoint exposes two SQL flaws on the asset-content database, both reachable by the publish RoleReader token and by the anonymous account when Publish.Auth.Enable is false: method 2 passes a client-supplied SQL statement to the read-write asset-content DB with no single-statement or read-only guard, and without the admin restriction its sibling fullTextSearchBlock applies to the same SQL method. method 3 builds a REGEXP clause by concatenating the client expression …
The /api/search/searchEmbedBlock endpoint passes a client-supplied SQL statement verbatim to the database with no validation. The endpoint is gated by CheckAuth only reachable by the publish RoleReader token, and by the anonymous account when Publish.Auth.Enable is false. The statement runs on the main read-write siyuan.db handle through a driver that executes stacked statements, with no single-statement or read-only guard. An unauthenticated request can therefore execute arbitrary SQL reading and writing …
SiYuan cleans user supplied SVG with util.SanitizeSVG before it serves the file inline as image/svg+xml. This cleaner is the guard behind the Editor.AllowSVGScript setting, which is off by default, so a <script> inside an SVG is meant to be removed. The cleaner reads the input as HTML, but the browser reads the served file as XML (SVG). Because the two parsers treat some tags differently, a <script> can be hidden …
The backlink/mention search query (kernel/model/backlink.go) concatenates stored block metadata (title, name, alias, anchor text) and the client-supplied keyword into a SQL MATCH/search statement, escaping only the double-quote character (") and not the single quote ('). A single quote in either the client keyword or in stored document metadata breaks out of the string literal. The query runs on the main read-write siyuan.db handle through a statement-stacking-capable driver. This yields two …
Attribute-view (AV) template columns are live-evaluated on every render and expose the queryBlocks template function, which runs raw SQL on the read-write database handle (SelectBlocksRawStmt, using ?→argument string substitution rather than parameter binding). AV mutations are admin-gated, so this is not directly reader-injectable but it is a second-order vector: an attacker distributes a SiYuan document or AV package whose template column contains .action{queryBlocks "<arbitrary SQL>"} when a victim imports the …
WebSocket sessions established through the publish surface (port 6808, RoleReader anonymous when Publish.Auth.Enable is false) are added to the same broadcast session pool as authenticated sessions. The kernel's broadcast functions push content events transactions carrying block DOM, document save/create, move/rename to every session in the pool with no role or publish-access filtering. As a result, an anonymous reader who holds a WebSocket connection open passively receives a real-time feed of …
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 …
SiYuan's /export/ file handler was hardened against export disclosure (issue #12213) by adding an IsSubPath(exportBaseDir, fullPath) check and an IsSensitivePath() check in commit bb481e1. These guards were added only to the main branch of the handler. The handler begins with a short-circuit branch: if strings.HasPrefix(c.Request.URL.Path, "/export/temp/") { c.File(filepath.Join(util.TempDir, c.Request.URL.Path)) return } This branch joins the broader util.TempDir with the raw, percent-decoded request path and serves it with neither IsSubPath nor …
FilterViewByPublishAccess, the filter renderAttributeView applies for Reader sessions drops rows using only the hidden/forbidden check and never checks the publish password. Its three sibling filters all check both tiers. As a result, a publish RoleReader (or the anonymous account when Publish.Auth.Enable is false) who has not entered a document's password still receives every database/attribute-view row bound to that password-protected document, the primary cell (title/ID) and all column values.
The /api/asset/getFileAnnotation endpoint returns the content of .sya PDF-annotation files with no publish-access check. It is gated by CheckAuth only, so it is reachable by the publish RoleReader token and by the anonymous account when Publish.Auth.Enable is false. Its sibling, the /assets/* asset route does enforce publish access, including the publish password. An anonymous reader who knows an asset path can therefore read the private PDF annotations (highlights, notes) attached …
POST /api/attr/getBlockAttrs and POST /api/attr/batchGetBlockAttrs return a block's full attribute set (IAL) with no publish-access check. Both are CheckAuth-only, so they are reachable by the publish RoleReader token and by the anonymous account when Publish.Auth.Enable is false. An anonymous reader supplying a block ID receives the block's name, alias, memo, bookmark, tags, and every custom-* attribute including for blocks in publish-forbidden and password-protected documents. The batch variant accepts an ID …
POST /api/av/getAttributeViewKeysByID returns a database's full column schema with no publish-access filtering, while its sibling getAttributeViewKeys applies the filter for reader sessions. Two further endpoints, getBlockDefIDsByRefText and getBlockRelevantIDs return workspace-wide block IDs with no publish scoping. All three are CheckAuth-only, so they are reachable by the publish RoleReader token and by the anonymous account when Publish.Auth.Enable is false.
Three block endpoints return document content snippets and metadata without any publish-access check, while their sibling getBlockInfo which returns comparable data does enforce one. All three are CheckAuth-only, so they are reachable by the publish RoleReader token and by the anonymous account when Publish.Auth.Enable is false. An anonymous reader supplying a block ID receives content and metadata belonging to publish-forbidden and password-protected documents.
The /api/ref/refreshBacklink endpoint is gated by CheckAuth only. Unlike its mutating siblings, it carries no CheckAdminRole, no CheckReadonly, and no inline reader-role guard so it falls through all three authorization mechanisms the codebase uses to protect write operations. A publish RoleReader or the anonymous account when Publish.Auth.Enable is false can invoke it, forcing the server to flush its pending write-transaction queue, scan all references globally, load and parse referencing trees …
The kernel's CheckAuth grants RoleAdministrator to any request whose RemoteAddr is loopback (127.0.0.1), for a specific set of endpoints, and these localhost bypasses sit outside the accessAuthCode gate so they apply even when an access auth code is configured. This is demonstrated live (Part A below). Separately, the fixed-port reverse proxy (fixedport.go) forwards requests to the kernel over loopback and injects no authentication token, and no SetTrustedProxies is configured, so …
getGraph and getLocalGraph filter reader sessions against the visibility tier only and never check the publish password. Password-protected documents are Visible = true, so their graph nodes survive the filter and graph nodes are block-level and carry the block's actual content. An anonymous reader who has never supplied a document's password can therefore retrieve that document's per-block content and its reference/backlink topology. Both endpoints are CheckAuth-only, so they are reachable …
Three "heading transaction" endpoints /api/block/getHeadingDeleteTransaction, /api/block/getHeadingLevelTransaction, and /api/block/getHeadingInsertTransaction return the rendered block DOM of a heading and its subtree in the computed transaction payload, with no publish-access check. They are gated by CheckAuth only, so they are reachable by the publish RoleReader token, and by the anonymous account when Publish.Auth.Enable is false. Despite their write-implying names, these endpoints perform no mutation on this path, they compute a transaction object and …
Two CheckAuth-only endpoints disclose the complete offline attack material for the encrypted-notebook master password, plus the wrapped per-notebook key needed to use it. Both are reachable by the publish RoleReader token and by the anonymous account when Publish.Auth.Enable is false. An unauthenticated remote client can retrieve the Argon2id salt and cost parameters, a verifier that confirms a correct password offline, and the encrypted per-notebook data key reducing the security of …
The /api/block/getBlockInfo endpoint returns document root metadata including the document title (rootTitle) for a block in a publish-forbidden document, with no publish-access check. Its sibling /api/block/getDocInfo applies the publish-access filter, getBlockInfo does not. Both are gated by CheckAuth only, so getBlockInfo is reachable by the publish RoleReader token and by the anonymous account when Publish.Auth.Enable is false.
The backlink API splits into list endpoints (which documents reference a block) and content endpoints (the rendered text of those referencing blocks). The list endpoints apply a publish-access filter, the content endpoints do not. As a result, /api/ref/getBacklinkDoc and /api/ref/getBackmentionDoc return the rendered DOM of blocks belonging to a publish-forbidden document to an anonymous reader, with no access check. Both content endpoints are gated by CheckAuth only, reachable by the …
SiYuan's publish mode defines a "protected" access level: a document that is publicly listed but requires a password to read (per the product's own UI help text, protected = "Publicly visible, requires password to access"). The password is enforced on the primary content path (getDoc, via FilterContentByPublishAccess). Several other content-returning endpoints getHeadingChildrenDOM, getHeadingDeleteTransaction/getHeadingLevelTransaction/ getHeadingInsertTransaction, and getBacklinkDoc/getBackmentionDoc return rendered block DOM with no password check at all. Combined with reader-reachable endpoints …
POST /api/asset/resolveAssetPath returns the resolved absolute filesystem path of an asset, unmodified. The route is CheckAuth-only, so it is reachable by the publish RoleReader token and by the anonymous account when Publish.Auth.Enable is false. An anonymous reader who knows any asset's relative path trivially harvested from an <img src="assets/…"> in any published document receives the server's absolute workspace path, disclosing the operating-system username and the installation layout.
Siyuan's block search endpoint concatenates attacker-controlled paths[] values into SQL predicates used by non-SQL search modes. Through Siyuan's publish service, an unauthenticated visitor is forwarded to the kernel with a reader-role token and can reach POST /api/search/fullTextSearchBlock. An attacker can inject a UNION SELECT through paths[] and return rows from hidden documents while projecting an allowed visible box and path. The post-query publish access filter trusts the projected box and …
Reporter: Cavan Loughran, Celvex Group Inc. Summary The /snippets/*filepath route handler serveSnippets in kernel/server/serve.go performs a bare filepath.Join(util.SnippetsPath, filePath) on the single-decoded c.Request.URL.Path and serves the result with c.File(), with NO IsSubPath containment and NO IsSensitivePath denylist - unlike the sibling /export/ (serveExport) and /appearance/ (serveAppearance) handlers, which both carry IsSubPath, and unlike /assets/ (serveAssets), whose traversal was fixed in GHSA-p4m3-mgmm-c664. Because util.SnippetsPath = WorkspaceDir/data/snippets, an authenticated request to GET …
The /api/icon/getDynamicIcon endpoint is explicitly excluded from authentication in SiYuan's kernel router (router.go, "不需要鉴权" – no auth needed). When called with type=8 and a valid block id parameter, this endpoint invokes RenderDynamicIconContentTemplate, which executes a Go template that includes the querySQL and queryBlocks functions. These functions run arbitrary SELECT statements against the SiYuan SQLite database. An unauthenticated network-adjacent attacker who knows a valid block ID can exfiltrate all user note …
SiYuan Note's kernel HTTP server unconditionally trusts all chrome-extension:// origins, granting RoleAdministrator access to every installed browser extension without any authentication. Combined with the default empty AccessAuthCode on desktop installs, any Chrome/Chromium extension – including a compromised legitimate extension via supply chain attack – can make fully authenticated admin API calls to the SiYuan kernel at 127.0.0.1:6806, enabling data exfiltration, stored XSS injection, and configuration tampering.
SiYuan v3.6.5 and earlier versions contain a stored cross-site scripting (XSS) vulnerability in the Attribute View (database) asset cell renderer that escalates to remote code execution (RCE) in the Electron desktop client. This is a neighbor-bug of CVE-2026-44588: the fix for -44588 used escapeAriaLabel() (double-escapes <), but the AV asset renderers were left using the weaker escapeAttr() (escapes only quotes) or no escaping at all.
A CSS snippet body containing </style> breaks out of its surrounding <style> tag when renderSnippet() interpolates it via insertAdjacentHTML. A payload like </style><img src=x onerror="…"> runs arbitrary JavaScript in the renderer. On Electron desktop builds the renderer runs with nodeIntegration:true, so require('child_process') is reachable from the injected handler and the XSS chains to host RCE. Snippets sync via the workspace repository, so an attacker with write access to any synced …
The attribute-view (database) cell renderer genAVValueHTML interpolates cell content raw in four of its branches: text, url, phone, and mAsset. A cell value like </textarea><img src=x onerror="…"> or "><img src=x onerror="…"> breaks out of its surrounding tag and runs arbitrary JavaScript in the renderer when the victim opens the block-attribute panel. On Electron desktop the renderer runs with nodeIntegration:true, so the XSS chains to host RCE via require('child_process'). AV files …
renderPackageREADME in kernel/bazaar/readme.go renders a Bazaar package README from Markdown to HTML with the lute engine and SetSanitize(true). The lute sanitizer is an event-handler blocklist: allowAttr rejects only attribute names present in a fixed eventAttrs map copied from the w3schools legacy handler list. That map omits modern event handlers. onpointerover, onpointerdown, onauxclick, onbeforetoggle, onfocusin, onanimationstart, and ontransitionend are not in the list, so the sanitizer passes them through verbatim on …
The patch for CVE-2026-41894 ("Path Traversal via Double URL Encoding") sanitized the /export/ route but the identical root cause remains in the /assets/*path route. In publish mode (anonymous read-only HTTP endpoint, default port 6808), an unauthenticated remote attacker can read arbitrary files inside WorkspaceDir — including conf/conf.json (which contains the AccessAuthCode SHA256 hash, API token, and sync keys), temp/siyuan.db, temp/blocktree.db, and siyuan.log — by double-URL-encoding .. segments. Verified against siyuan …
POST /api/tag/getTag is registered with model.CheckAuth only, omitting both model.CheckAdminRole and model.CheckReadonly, despite the handler performing a configuration write that is normally guarded by both. Any authenticated user — including publish-service RoleReader accounts and RoleEditor accounts on a read-only workspace — can call this endpoint with a sort argument to mutate model.Conf.Tag.Sort and trigger model.Conf.Save(), which atomically rewrites the entire workspace conf.json. Same root-cause class as the patched GHSA-4j3x-hhg2-fm2x (which …
SiYuan publish-mode Reader can mutate Conf and SQL index via 8 ungated APIs POST /api/graph/getGraph, POST /api/graph/getLocalGraph, POST /api/sync/setSyncInterval, POST /api/storage/updateRecentDocViewTime, POST /api/storage/updateRecentDocCloseTime, POST /api/storage/updateRecentDocOpenTime, POST /api/storage/batchUpdateRecentDocCloseTime, and POST /api/search/updateEmbedBlock are registered with model.CheckAuth only, omitting both model.CheckAdminRole and model.CheckReadonly. Each of them writes server-side state, including atomic rewrites of <workspace>/conf/conf.json via model.Conf.Save(). Any caller whose JWT passes CheckAuth, including a publish-service RoleReader (the role assigned to anonymous publish visitors) …
The advisory GHSA-c77m-r996-jr3q patched getBookmark so that, when invoked by a publish-mode RoleReader, results are filtered through FilterBlocksByPublishAccess to remove entries from password-protected / publish-ignored notebooks. Four sibling search handlers in the same file did not receive the equivalent treatment and continue to expose metadata across the publish-access boundary.
SiYuan's Bazaar (community marketplace) renders the name and version fields of a package's plugin.json (and the equivalent theme.json / template.json / widget.json / icon.json) into the Settings → Marketplace UI without HTML escaping. The kernel-side helper sanitizePackageDisplayStrings in kernel/bazaar/package.go HTML-escapes only Author, DisplayName, and Description — Name and Version flow through to the renderer raw. The frontend at app/src/config/bazaar.ts substitutes them into HTML template strings via ${item.preferredName} / ${data.name} / …
The tooltip mouseover handler in app/src/block/popover.ts reads aria-label via getAttribute and passes it through decodeURIComponent before assigning to messageElement.innerHTML in app/src/dialog/tooltip.ts:41. The encoder used at the producer side, escapeAriaLabel in app/src/util/escape.ts:19-25, only handles HTML special characters (", ', <, literal <) — it leaves %XX URL-escapes untouched. So a doc title containing %3Cimg src=x onerror=…%3E round-trips through escapeAriaLabel and the HTML attribute layer unmodified. Then decodeURIComponent on the consumer side …
The kernel stores Attribute View (AV / database) names without any HTML escape, then a render template uses raw strings.ReplaceAll(tpl, "${avName}", nodeAvName) to embed the name in HTML before pushing to all clients via WebSocket. Three independent client paths (render.ts:120 → outerHTML, Title.ts:401 → innerHTML, transaction.ts:559 → innerHTML) consume the value without escaping. Because the main BrowserWindow runs nodeIntegration:true, contextIsolation:false, webSecurity:false (app/electron/main.js:407-411), HTML injection in the renderer becomes Node.js code …
The fix for CVE-2026-30869 in SiYuan v3.5.10 only added a denylist check (IsSensitivePath) but did not address the root cause — a redundant url.PathUnescape() call in serveExport(). An authenticated attacker can use double URL encoding (%252e%252e) to traverse directories and read arbitrary workspace files including the full SQLite database (siyuan.db), kernel log, and all user documents.
The fix for CVE-2026-30869 in SiYuan v3.5.10 only added a denylist check (IsSensitivePath) but did not address the root cause — a redundant url.PathUnescape() call in serveExport(). An authenticated attacker can use double URL encoding (%252e%252e) to traverse directories and read arbitrary workspace files including the full SQLite database (siyuan.db), kernel log, and all user documents.
The incomplete fix for SiYuan's bazaar README rendering enables the Lute HTML sanitizer but fails to block <iframe> tags, allowing stored XSS via srcdoc attributes containing embedded scripts that execute in the Electron context.
The incomplete fix for SiYuan's bazaar README rendering enables the Lute HTML sanitizer but fails to block <iframe> tags, allowing stored XSS via srcdoc attributes containing embedded scripts that execute in the Electron context.
SiYuan's publish/read-only boundary can be broken through /api/av/removeUnusedAttributeView. A publish-service Reader context can call this endpoint because it is protected only by CheckAuth, and publish requests are forwarded upstream with a valid RoleReader JWT. The handler accepts attacker-controlled id input and passes it directly into a filesystem delete sink: no admin check no readonly check no node-ID validation no subpath enforcement no verification that the target AV is actually unused …
No affected version range available.
An authenticated publish-service reader can invoke /api/av/removeUnusedAttributeView and cause persistent deletion of arbitrary attribute view (AV) definition files from the workspace. The route is protected only by generic CheckAuth, which accepts publish RoleReader requests. The handler forwards a caller-controlled id directly into a model function that deletes data/storage/av/<id>.json without verifying either: that the caller is allowed to perform write/destructive actions; or that the target AV is actually unused. This is …
An authenticated publish-service reader can invoke /api/av/removeUnusedAttributeView and cause persistent deletion of arbitrary attribute view (AV) definition files from the workspace. The route is protected only by generic CheckAuth, which accepts publish RoleReader requests. The handler forwards a caller-controlled id directly into a model function that deletes data/storage/av/<id>.json without verifying either: that the caller is allowed to perform write/destructive actions; or that the target AV is actually unused. This is …
SiYuan configures Mermaid.js with securityLevel: "loose" and htmlLabels: true. In this mode, <img> tags with src attributes survive Mermaid's internal DOMPurify and land in SVG <foreignObject> blocks. The SVG is injected via innerHTML with no secondary sanitization. When a victim opens a note containing a malicious Mermaid diagram, the Electron client fetches the URL. On Windows, a protocol-relative URL (//attacker.com/image.png) resolves as a UNC path (\attacker.com\image.png). Windows attempts SMB authentication …
A malicious note synced to another user can trigger remote code execution in the SiYuan Electron desktop client. The root cause is that table caption content is stored without safe escaping and later unescaped into rendered HTML, creating a stored XSS sink. Because the desktop renderer runs with nodeIntegration enabled and contextIsolation disabled, attacker-controlled JavaScript executes with access to Node.js APIs. In practice, an attacker can import a crafted note …
The SanitizeSVG function introduced in v3.6.0 to fix XSS in the unauthenticated /api/icon/getDynamicIcon endpoint can be bypassed by using namespace-prefixed element names such as <x:script xmlns:x="http://www.w3.org/2000/svg">. The Go HTML5 parser records the element's tag as "x:script" rather than "script", so the tag check passes it through. The SVG is served with Content-Type: image/svg+xml and no Content Security Policy; when a browser opens the response directly, its XML parser resolves the …
A vulnerability allows crafted block attribute values to bypass server-side attribute escaping when an HTML entity is mixed with raw special characters. An attacker can embed a malicious IAL value inside a .sy document, package it as a .sy.zip, and have the victim import it through the normal Import -> SiYuan .sy.zip workflow. Once the note is opened, the malicious attribute breaks out of its original HTML context and injects …
The publish service exposes bookmarked blocks from password-protected documents to unauthenticated visitors. In publish/read-only mode, /api/bookmark/getBookmark filters bookmark results by calling FilterBlocksByPublishAccess(nil, …). Because the filter treats a nil context as authorized, it skips the publish password check and returns bookmarked blocks from documents configured as Protected. As a result, anyone who can access the publish service can retrieve content from protected documents without providing the required password, as long …
An attacker who can place a malicious URL in an Attribute View mAsse field can trigger stored XSS when a victim opens the Gallery or Kanban view with “Cover From -> Asset Field” enabled. The vulnerable code accepts arbitrary http(s) URLs without extensions as images, stores the attacker-controlled string in coverURL, and injects it directly into an <img src="…"> attribute without escaping. In the Electron desktop client, the injected JavaScript …
A malicious website can achieve Remote Code Execution (RCE) on any desktop running SiYuan by exploiting the permissive CORS policy (Access-Control-Allow-Origin: * + Access-Control-Allow-Private-Network: true) to inject a JavaScript snippet via the API. The injected snippet executes in Electron's Node.js context with full OS access the next time the user opens SiYuan's UI. No user interaction is required beyond visiting the malicious website while SiYuan is running.
Directory traversal vulnerability: The entire directory structure of a notebook could be obtained, and then a file reading vulnerability could be exploited to achieve arbitrary document reading. 资源文件夹 插件文件夹 conf文件夹
File reading: All encrypted or prohibited documents under the publishing service could be read.
The Siyuan kernel exposes an unauthenticated file-serving endpoint under */appearance/filepath. Due to improper path sanitization, attackers can perform directory traversal and read arbitrary files accessible to the server process. Authentication checks explicitly exclude this endpoint, allowing exploitation without valid credentials.
SiYuan's Bazaar (community marketplace) renders package README content without HTML sanitization. The backend renderREADME function uses lute.New() without calling SetSanitize(true), allowing raw HTML embedded in Markdown to pass through unmodified. The frontend then assigns the rendered HTML to innerHTML without any additional sanitization. A malicious package author can embed arbitrary JavaScript in their README that executes when a user clicks to view the package details. Because SiYuan's Electron configuration enables …
SiYuan's Bazaar (community marketplace) renders package metadata fields (displayName, description) using template literals without HTML escaping. A malicious package author can inject arbitrary HTML/JavaScript into these fields, which executes automatically when any user browses the Bazaar page. Because SiYuan's Electron configuration enables nodeIntegration: true with contextIsolation: false, this XSS escalates directly to full Remote Code Execution on the victim's operating system — with zero user interaction beyond opening the marketplace …
The SiYuan kernel WebSocket server accepts unauthenticated connections when a specific “auth keepalive” query parameter is present. After connection, incoming messages are parsed using unchecked type assertions on attacker-controlled JSON. A remote attacker can send malformed messages that trigger a runtime panic, potentially crashing the kernel process and causing denial of service.
The IsSensitivePath() function in kernel/util/path.go uses a denylist approach that was recently expanded (GHSA-h5vh-m7fg-w5h6, commit 9914fd1) but remains incomplete. Multiple security-relevant Linux directories are not blocked, including /opt (application data), /usr (local configs/binaries), /home (other users), /mnt and /media (mounted volumes). The globalCopyFiles and importStdMd endpoints rely on IsSensitivePath as their primary defense against reading files outside the workspace.
In SiYuan, /api/lute/html2BlockDOM on the desktop copies local files pointed to by file:// links in pasted HTML into the workspace assets directory without validating paths against a sensitive-path list. Together with GET /assets/*path, which only requires authentication, a publish-service visitor can cause the desktop kernel to copy any readable sensitive file and then read it via GET, leading to exfiltration of sensitive files.
In SiYuan, /api/lute/html2BlockDOM on the desktop copies local files pointed to by file:// links in pasted HTML into the workspace assets directory without validating paths against a sensitive-path list. Together with GET /assets/*path, which only requires authentication, a publish-service visitor can cause the desktop kernel to copy any readable sensitive file and then read it via GET, leading to exfiltration of sensitive files.
SiYuan Note v3.6.0 (and likely prior versions) contains an authorization bypass vulnerability in the /api/search/fullTextSearchBlock endpoint. When the method parameter is set to 2, the endpoint passes user-supplied input directly as a raw SQL statement to the underlying SQLite database without any authorization or read-only checks. This allows any authenticated user — including those with the Reader role — to execute arbitrary SQL statements (SELECT, DELETE, UPDATE, DROP TABLE, etc.) …
SiYuan's mobile file tree (MobileFiles.ts) renders notebook names via innerHTML without HTML escaping when processing renamenotebook WebSocket events. The desktop version (Files.ts) properly uses escapeHtml() for the same operation. An authenticated user who can rename notebooks can inject arbitrary HTML/JavaScript that executes on any mobile client viewing the file tree. Since Electron is configured with nodeIntegration: true and contextIsolation: false, the injected JavaScript has full Node.js access, escalating stored XSS …
SiYuan's Bazaar (community marketplace) renders plugin/theme/template metadata and README content without sanitization. A malicious package author can achieve RCE on any user who browses the Bazaar by: Package metadata XSS (zero-click): Package displayName and description fields are injected directly into HTML via template literals without escaping. Just loading the Bazaar page triggers execution. README XSS (one-click): The renderREADME function uses lute.New() without SetSanitize(true), so raw HTML in the README passes …
SiYuan's WebSocket endpoint (/ws) allows unauthenticated connections when specific URL parameters are provided (?app=siyuan&id=auth&type=auth). This bypass, intended for the login page to keep the kernel alive, allows any external client — including malicious websites via cross-origin WebSocket — to connect and receive all server push events in real-time. These events leak sensitive document metadata including document titles, notebook names, file paths, and all CRUD operations performed by authenticated users. Combined …
SiYuan's WebSocket endpoint (/ws) allows unauthenticated connections when specific URL parameters are provided (?app=siyuan&id=auth&type=auth). This bypass, intended for the login page to keep the kernel alive, allows any external client — including malicious websites via cross-origin WebSocket — to connect and receive all server push events in real-time. These events leak sensitive document metadata including document titles, notebook names, file paths, and all CRUD operations performed by authenticated users. Combined …
POST /api/import/importSY and POST /api/import/importZipMd write uploaded archives to a path derived from the multipart filename field without sanitization, allowing an admin to write files to arbitrary locations outside the temp directory - including system paths that enable RCE.
POST /api/file/globalCopyFiles reads source files using filepath.Abs() with no workspace boundary check, relying solely on util.IsSensitivePath() whose blocklist omits /proc/, /run/secrets/, and home directory dotfiles. An admin can copy /proc/1/environ or Docker secrets into the workspace and read them via the standard file API.
POST /api/template/renderSprig lacks model.CheckAdminRole, allowing any authenticated user to execute arbitrary SQL queries against the SiYuan workspace database and exfiltrate all note content, metadata, and custom attributes.
The /api/network/forwardProxy endpoint allows authenticated users to make arbitrary HTTP requests from the server. The endpoint accepts a user-controlled URL and makes HTTP requests to it, returning the full response body and headers. There is no URL validation to prevent requests to internal networks, localhost, or cloud metadata services.
SiYuan's SVG sanitizer (SanitizeSVG) checks href attributes for the javascript: prefix using strings.HasPrefix(). However, inserting ASCII tab (	), newline ( ), or carriage return ( ) characters inside the javascript: string bypasses this prefix check. Browsers strip these characters per the WHATWG URL specification before parsing the URL scheme, so the JavaScript still executes. This allows an attacker to inject executable JavaScript into the unauthenticated /api/icon/getDynamicIcon endpoint, creating a reflected XSS. This …
SiYuan's SVG sanitizer (SanitizeSVG) blocks dangerous elements (<script>, <iframe>, <foreignobject>) and removes on* event handlers and javascript: in href attributes. However, it does NOT block SVG animation elements (<animate>, <set>) which can dynamically set attributes to dangerous values at runtime, bypassing the static sanitization. This allows an attacker to inject executable JavaScript into the unauthenticated /api/icon/getDynamicIcon endpoint (type=8), creating a reflected XSS. This is a bypass of the fix for …
A privilege escalation vulnerability exists in the publish service of SiYuan Note that allows a low-privilege publish account (RoleReader) to modify notebook content via the /api/block/appendHeadingChildren API endpoint. The endpoint only requires model.CheckAuth, which accepts RoleReader sessions. Because the endpoint performs a persistent document mutation and does not enforce CheckAdminRole or CheckReadonly, a publish user with read-only privileges can append new blocks to existing documents. This allows remote authenticated publish …
A path traversal vulnerability in the /export endpoint allows an attacker to read arbitrary files from the server filesystem. By exploiting double‑encoded traversal sequences, an attacker can access sensitive files such as conf/conf.json, which contains secrets including the API token, cookie signing key, and workspace access authentication code. Leaking these secrets may enable administrative access to the SiYuan kernel API, and in certain deployment scenarios could potentially be chained into …
A path traversal vulnerability in the /export endpoint allows an attacker to read arbitrary files from the server filesystem. By exploiting double‑encoded traversal sequences, an attacker can access sensitive files such as conf/conf.json, which contains secrets including the API token, cookie signing key, and workspace access authentication code. Leaking these secrets may enable administrative access to the SiYuan kernel API, and in certain deployment scenarios could potentially be chained into …
An unauthenticated reflected XSS vulnerability exists in the dynamic icon API endpoint: GET /api/icon/getDynamicIcon When type=8, attacker-controlled content is embedded into SVG output without escaping. Because the endpoint is unauthenticated and returns image/svg+xml, a crafted URL can inject executable SVG/HTML event handlers (for example onerror) and run JavaScript in the SiYuan web origin. This can be chained to perform authenticated API actions and exfiltrate sensitive data when a logged-in user …
/api/query/sql allows users to run SQL directly, but it only checks basic auth, not admin rights, any logged-in user, even readers, can run any SQL query on the database.
The /api/file/copyFile endpoint does not validate the dest parameter, allowing authenticated users to write files to arbitrary locations on the filesystem. This can lead to Remote Code Execution (RCE) by writing to sensitive locations such as cron jobs, SSH authorized_keys, or shell configuration files. Affected Version: 3.5.3 (and likely all prior versions)
The /api/file/copyFile endpoint does not validate the dest parameter, allowing authenticated users to write files to arbitrary locations on the filesystem. This can lead to Remote Code Execution (RCE) by writing to sensitive locations such as cron jobs, SSH authorized_keys, or shell configuration files. Affected Version: 3.5.3 (and likely all prior versions)
Read sensitive information in configuration files (e.g., access codes, API Tokens, sync configurations, etc.). Remotely exploitable directly when the service is published without authentication.
Read sensitive information in configuration files (e.g., access codes, API Tokens, sync configurations, etc.). Remotely exploitable directly when the service is published without authentication.
The SiYuan Note application (v3.5.3) contains a logic vulnerability in the /api/file/globalCopyFiles endpoint. The function allows authenticated users to copy files from any location on the server's filesystem into the application's workspace without proper path validation
Markdown feature allows unrestricted server side html-rendering which allows arbitary file read (LFD) and fully SSRF access We in @0xL4ugh ( @abdoghazy2015, @xtromera, @A-z4ki, @ZeyadZonkorany and @KarimTantawey) During playing Null CTF 2025 that helps us solved a challenge with unintended way : ) Please note that we used the latest Version and deployed it via this dockerfile : Dockerfile: FROM b3log/siyuan ENV TZ=America/New_York \ PUID=1000 \ PGID=1000 \ SIYUAN_ACCESS_AUTH_CODE=SuperSecretPassword RUN …
Reflected XSS in /api/icon/getDynamicIcon due to unsanitized SVG input.
A Stored Cross-Site Scripting (XSS) vulnerability exists in SiYuan Note. The application does not sanitize uploaded SVG files. If a user uploads and views a malicious SVG file (e.g., imported from an untrusted source), arbitrary JavaScript code is executed in the context of their authenticated session.