Oct 5, 2026·6 min read·6 visits
Weak relative path checks in SiYuan allowed standard users to access sensitive configuration files and publishing tokens by requesting them via unprotected history and git diff endpoints.
A weak path access control vulnerability in SiYuan note-taking application allowed authenticated users to bypass restricted file paths by requesting historical backups and git diffs of sensitive configurations, including plain-text authentication tokens.
SiYuan is a personal knowledge management system utilizing local-first note-taking structures. To protect user data and operational integrity, the application restricts access to highly sensitive system files. These include publishAccess.json, which contains plain-text synchronization and publishing credentials, custom formatting templates, and specific user snippet configurations.
The application implements a defensive path filter, IsForbiddenAbsPath, which blocks direct read operations on these system files. This absolute path validation mechanism effectively secures standard file retrieval endpoints like /api/file/getFile. However, the system's attack surface includes auxiliary endpoints designed to handle version management and synchronization history.
Two critical endpoints, /history/*path and /repo/diff/*path, allow users to access previous states of their notes and view differential updates. Prior to version 3.8.1, these endpoints were not subject to the same strict path-filtering rules applied to live filesystem operations. This omission created a direct logical bypass where restricted files could be queried from the historical backup and git directories.
The root cause of this vulnerability lies in the inconsistent application of path authorization logic across different subsystem handlers. While live file transactions are subjected to complete sanitization and verified against absolute blocklists, the historical backup system uses a distinct storage directory (HistoryDir). This directory contains complete snapshots of the workspace, retaining identical subdirectory layouts and file naming conventions.
Because the security validation utility IsForbiddenAbsPath was designed to evaluate absolute paths relative to the live data root (DataDir), queries made to the history controller bypassed these checks. The controller processed requests to retrieve snapshots directly by appending the user-supplied string to the history directory prefix. This allowed the path resolution to occur outside the scope of the established security interceptor.
Similarly, the git-based repository differential system, exposed via /repo/diff/*path, allowed users to request file differences directly. Since the diff processor only queried the repository history rather than active physical paths, the absolute path check was not triggered. Consequently, any credentials or configuration parameters modified over time could be reconstructed from the repository metadata history.
In the vulnerable version of kernel/server/serve.go, the route handler for the history endpoint accepted the path parameter directly from the Gin context without sanitization. The application performed a simple join operation using filepath.Join to determine the target file on the disk, completely bypassing the path validation logic designed for active files.
Below is the vulnerable handling logic inside /history/*path compared against the patched implementation:
// Pre-v3.8.1 Vulnerable Code Path
ginServer.GET("/history/*path", model.CheckAuth, model.CheckAdminRole, func(context *gin.Context) {
p := filepath.Join(util.HistoryDir, context.Param("path"))
// Direct file read and transmission occurs here without path validation checks
})In the patched version (v3.8.1), the developer added manual directory traversal checks and introduced a new helper function IsForbiddenDataRelPath to systematically inspect files residing in the snapshot directory structures:
// Patched Code Path in v3.8.1
ginServer.GET("/history/*path", model.CheckAuth, model.CheckAdminRole, func(context *gin.Context) {
requestPath := context.Param("path")
// Reject directory traversal attempts
if strings.Contains(requestPath, "..") {
context.Status(http.StatusUnauthorized)
return
}
p := filepath.Join(util.HistoryDir, requestPath)
// Normalize path by stripping the snapshot folder prefix
rel := strings.TrimPrefix(requestPath, "/")
if idx := strings.Index(rel, "/"); 0 <= idx {
rel = rel[idx+1:]
} else {
rel = ""
}
// Evaluate path using the relative path blacklist
if util.IsForbiddenDataRelPath(rel) {
context.Status(http.StatusForbidden)
return
}
// Safe to resolve file
})The introduction of IsForbiddenDataRelPath in kernel/util/path_guard.go addresses the path separation issue by standardizing relative pathways to forward slashes, forcing lowercase evaluation on case-insensitive filesystems, and evaluating against restricted prefixes. This centralized approach guarantees uniform protection across both the active filesystem and snapshot configurations.
Exploitation of this vulnerability requires an authenticated session or direct network access to the local API on a system configured to accept external requests. The target files of interest include .siyuan/publishAccess.json which stores plain-text access tokens and templates containing confidential records.
The attack sequence is structured as follows:
An attacker initiates the attack by identifying a valid historical snapshot prefix, typically formatted chronologically based on synchronization intervals. Once the snapshot directory identifier is determined, the attacker issues a GET request targeting the history route. By querying /history/<timestamp>-sync/.siyuan/publishAccess.json, the path check is avoided, allowing the application to serialize and return the raw configuration file. Alternatively, requesting /repo/diff/.siyuan/publishAccess.json yields the modifications history of the same credentials, extracting key assets from git logs without authorization checks.
An analysis of the patch implementation reveals a critical OS-specific bypass mechanism affecting Windows environments. The relative path isolation logic within /history/*path relies on string split operations that assume forward-slash separators are consistently utilized.
Specifically, the application performs the prefix trimming operation using the following instruction block:
rel := strings.TrimPrefix(requestPath, "/")
if idx := strings.Index(rel, "/"); 0 <= idx {
rel = rel[idx+1:]
} else {
rel = ""
}On Windows operating systems, filesystem requests can be formulated using backslash separators (\). If an attacker constructs a query targeting a historical snapshot utilizing backslashes (e.g., 2026-08-15-120000-sync\.siyuan\publishAccess.json), strings.Index(rel, "/") yields a value of -1. This shifts program execution to the else block, assigning an empty string to rel. Because an empty string does not match any of the forbidden rules in IsForbiddenDataRelPath, the validation check succeeds, and the request is permitted. The downstream function then combines the paths using filepath.Join, which automatically normalizes backslashes and accesses the restricted backup file on the host filesystem.
To remediate this structural gap, the incoming requestPath must be normalized utilizing filepath.ToSlash prior to executing any string matching or prefix slicing routines.
The primary mitigation action for this vulnerability is upgrading the application to version v3.8.1 or higher. This release integrates relative path filtering and directory traversal prevention into the affected API controllers.
For environments unable to perform an immediate upgrade, several operational workarounds can be deployed:
Network Binding Restraints: Restrict the listening socket of the SiYuan web interface exclusively to the local loopback interface (127.0.0.1). This isolates the interface from remote access vectors.
Web Application Firewall (WAF) Implementation: Deploy an inspection layer or reverse proxy (such as Nginx) in front of the server. Configure rule-sets to analyze incoming requests and block matches containing /history/ or /repo/diff/ combined with sensitive configuration directory structures or backslash sequences.
| Product | Affected Versions | Fixed Version |
|---|---|---|
SiYuan siyuan-note | < 3.8.1 | v3.8.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-862 (Missing Authorization) / CWE-22 (Path Traversal) |
| Attack Vector | Network (Authenticated Standard User or Local API Access) |
| CVSS v3.1 | 7.5 (Medium-High) |
| Exploit Status | Proof-of-Concept (PoC) Analyzed |
| Impact | Information Disclosure (Retrieval of plain-text publishing tokens and templates) |
| Remediation | Upgrade to v3.8.1 or higher |
CVE-2026-103918 is a medium-severity vulnerability within the @orpc/zod smart coercion plugin in oRPC. Prior to version 1.14.10, the package fails to sanitize untrusted input keys when performing pre-validation type coercion, allowing prototype injection on the returned request object and Denial of Service.
An incomplete path blocklist in the file retrieval engine of the SiYuan knowledge management platform allows authenticated users to read TLS and CA private key materials directly from the configuration directory.
CVE-2026-88779 is a critical vulnerability in Citrix NetScaler ADC and Citrix NetScaler Gateway affecting systems configured as a SAML Service Provider (SP) or SAML Identity Provider (IdP). An unauthenticated remote attacker can exploit this vulnerability to trigger a buffer overflow in the authentication daemon, resulting in persistent denial of service and appliance crash loops.
A critical cross-tenant SQL injection vulnerability exists in the TSQL query compiler of Trigger.dev, allowing authenticated users to bypass tenant isolation boundaries and read arbitrary ClickHouse analytics logs and execution payloads belonging to other organizations.
A logical authorization bypass vulnerability exists in Trigger.dev versions prior to 4.5.6. This flaw allows an authenticated client with a low-trust environment API key, such as development or staging, to cancel active worker deployments in a higher-trust environment like production within the same project. The vulnerability occurs because write operations on deployments were scoped solely by project identifier instead of environment identifier.
An in-depth technical analysis of multiple critical security flaws identified in the Vibe-Trading ecosystem (vibe-trading-ai). These issues range from unauthenticated remote command injection via agent tool executions to arbitrary Python execution through dynamic module loading and unsafe Jinja2 template autoescaping, allowing full system compromise.