VYPR
High severityNVD Advisory· Published Feb 10, 2026· Updated Feb 10, 2026

SiYuan has a File Read Interface Case Bypass Vulnerability

CVE-2026-25992

Description

SiYuan is a personal knowledge management system. Prior to 3.5.5, the /api/file/getFile endpoint uses case-sensitive string equality checks to block access to sensitive files. On case-insensitive file systems such as Windows, attackers can bypass restrictions using mixed-case paths and read protected configuration files. This vulnerability is fixed in 3.5.5.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/siyuan-note/siyuan/kernelGo
<= 0.0.0-20260126094835-d5d10dd41b0c

Affected products

1

Patches

1
1f02650b3892

:art: Improve the security of the kernel API `/api/file/getFile` on the publish service https://github.com/siyuan-note/siyuan/issues/16603

https://github.com/siyuan-note/siyuanDanielJan 18, 2026via ghsa
1 file changed · +36 13
  • kernel/api/file.go+36 13 modified
    @@ -167,6 +167,13 @@ func getFile(c *gin.Context) {
     		c.JSON(http.StatusAccepted, ret)
     		return
     	}
    +	if !filelock.IsExist(fileAbsPath) {
    +		ret.Code = http.StatusNotFound
    +		ret.Msg = "file does not exist"
    +		c.JSON(http.StatusAccepted, ret)
    +		return
    +	}
    +
     	info, err := os.Stat(fileAbsPath)
     	if os.IsNotExist(err) {
     		ret.Code = http.StatusNotFound
    @@ -190,19 +197,8 @@ func getFile(c *gin.Context) {
     	}
     
     	// REF: https://github.com/siyuan-note/siyuan/issues/11364
    -	if role := model.GetGinContextRole(c); !model.IsValidRole(role, []model.Role{
    -		model.RoleAdministrator,
    -	}) {
    -		if relPath, err := filepath.Rel(util.ConfDir, fileAbsPath); err != nil {
    -			logging.LogErrorf("Get a relative path from [%s] to [%s] failed: %s", util.ConfDir, fileAbsPath, err)
    -			ret.Code = http.StatusInternalServerError
    -			ret.Msg = err.Error()
    -			c.JSON(http.StatusAccepted, ret)
    -			return
    -		} else if relPath == "conf.json" {
    -			ret.Code = http.StatusForbidden
    -			ret.Msg = http.StatusText(http.StatusForbidden)
    -			c.JSON(http.StatusAccepted, ret)
    +	if !model.IsAdminRoleContext(c) {
    +		if refuseToAccess(c, fileAbsPath, ret) {
     			return
     		}
     	}
    @@ -228,6 +224,33 @@ func getFile(c *gin.Context) {
     	c.Data(http.StatusOK, contentType, data)
     }
     
    +func refuseToAccess(c *gin.Context, fileAbsPath string, ret *gulu.Result) bool {
    +	// 禁止访问配置文件 conf/conf.json
    +	if filepath.Join(util.ConfDir, "conf.json") == fileAbsPath {
    +		ret.Code = http.StatusForbidden
    +		ret.Msg = http.StatusText(http.StatusForbidden)
    +		c.JSON(http.StatusAccepted, ret)
    +		return true
    +	}
    +
    +	// 禁止访问 data/snippets/conf.json
    +	if filepath.Join(util.DataDir, "snippets", "conf.json") == fileAbsPath {
    +		ret.Code = http.StatusForbidden
    +		ret.Msg = http.StatusText(http.StatusForbidden)
    +		c.JSON(http.StatusAccepted, ret)
    +		return true
    +	}
    +
    +	// 禁止访问 data/templates 目录
    +	if util.IsSubPath(filepath.Join(util.DataDir, "templates"), fileAbsPath) {
    +		ret.Code = http.StatusForbidden
    +		ret.Msg = http.StatusText(http.StatusForbidden)
    +		c.JSON(http.StatusAccepted, ret)
    +		return true
    +	}
    +	return false
    +}
    +
     func readDir(c *gin.Context) {
     	ret := gulu.Ret.NewResult()
     	defer c.JSON(http.StatusOK, ret)
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.