From be0a86b45f9843ec3a6b7a70acebaf824fcf46ab Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Fri, 10 Apr 2026 00:08:07 +0800 Subject: [PATCH 1/2] :lock: https://github.com/siyuan-note/siyuan/security/advisories/GHSA-w95v-4h65-j455 Signed-off-by: Daniel <845765@qq.com> --- app/src/protyle/render/mermaidRender.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/protyle/render/mermaidRender.ts b/app/src/protyle/render/mermaidRender.ts index 526dad274..1ff7c81c2 100644 --- a/app/src/protyle/render/mermaidRender.ts +++ b/app/src/protyle/render/mermaidRender.ts @@ -98,7 +98,7 @@ const initMermaid = (mermaidElements: Element[]) => { try { renderElement.innerHTML = `${Constants.ZWSP}
`; const mermaidData = await window.mermaid.render(id, Lute.UnEscapeHTMLStr(item.getAttribute("data-content"))); - renderElement.lastElementChild.innerHTML = mermaidData.svg; + renderElement.lastElementChild.innerHTML = mermaidData.svg.replace(/(href|src|xlink:href)\s*=\s*["']\\\\/gi, (match, p1) => `${p1}="about:blank"`);; } catch (e) { const errorElement = document.querySelector("#" + id); renderElement.lastElementChild.innerHTML = `${errorElement.outerHTML}
${e.message.replace(/\n/, "
")}
`; From c1539878c879fac759ae0675d5038b1ac37447ee Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Fri, 10 Apr 2026 00:38:50 +0800 Subject: [PATCH 2/2] :recycle: Move IsSubPath to gulu Signed-off-by: Daniel <845765@qq.com> --- kernel/api/file.go | 2 +- kernel/api/import.go | 6 +++--- kernel/api/sync.go | 4 ++-- kernel/api/system.go | 2 +- kernel/api/workspace.go | 2 +- kernel/go.mod | 2 +- kernel/go.sum | 4 ++-- kernel/model/assets.go | 4 ++-- kernel/model/publish_access.go | 4 ++-- kernel/model/sync.go | 2 +- kernel/model/upload.go | 2 +- kernel/server/serve.go | 2 +- kernel/util/file.go | 28 ---------------------------- kernel/util/path.go | 4 ++-- 14 files changed, 20 insertions(+), 48 deletions(-) diff --git a/kernel/api/file.go b/kernel/api/file.go index bfd15fe0d..7fbd0f5e0 100644 --- a/kernel/api/file.go +++ b/kernel/api/file.go @@ -366,7 +366,7 @@ func refuseToAccess(c *gin.Context, fileAbsPath string, ret *gulu.Result) bool { // 禁止访问 data/templates 目录 templatesBase := normalizeAndResolve(filepath.Join(util.DataDir, "templates")) - if util.IsSubPath(templatesBase, fileNorm) { + if gulu.File.IsSubPath(templatesBase, fileNorm) { ret.Code = http.StatusForbidden ret.Msg = http.StatusText(http.StatusForbidden) c.JSON(http.StatusAccepted, ret) diff --git a/kernel/api/import.go b/kernel/api/import.go index e590531ac..289a9a3be 100644 --- a/kernel/api/import.go +++ b/kernel/api/import.go @@ -63,7 +63,7 @@ func importSY(c *gin.Context) { } writePath := filepath.Join(importDir, file.Filename) - if !util.IsSubPath(importDir, writePath) { + if !gulu.File.IsSubPath(importDir, writePath) { logging.LogErrorf("import path [%s] is not sub path of import dir [%s]", writePath, importDir) ret.Code = -1 ret.Msg = "import path is not sub path of import dir" @@ -232,7 +232,7 @@ func importStdMd(c *gin.Context) { localPath := arg["localPath"].(string) toPath := arg["toPath"].(string) - if util.IsSubPath(util.WorkingDir, localPath) { + if gulu.File.IsSubPath(util.WorkingDir, localPath) { msg := fmt.Sprintf("import from local path [%s] failed: local path is sub path of working dir", localPath) logging.LogErrorf(msg) ret.Code = -1 @@ -288,7 +288,7 @@ func importZipMd(c *gin.Context) { } writePath := filepath.Join(importDir, file.Filename) - if !util.IsSubPath(importDir, writePath) { + if !gulu.File.IsSubPath(importDir, writePath) { logging.LogErrorf("import path [%s] is not sub path of import dir [%s]", writePath, importDir) ret.Code = -1 ret.Msg = "import path is not sub path of import dir" diff --git a/kernel/api/sync.go b/kernel/api/sync.go index 5e7b5984f..d4281479c 100644 --- a/kernel/api/sync.go +++ b/kernel/api/sync.go @@ -81,7 +81,7 @@ func importSyncProviderWebDAV(c *gin.Context) { } writePath := filepath.Join(importDir, f.Filename) - if !util.IsSubPath(importDir, writePath) { + if !gulu.File.IsSubPath(importDir, writePath) { logging.LogErrorf("import path [%s] is not sub path of import dir [%s]", writePath, importDir) ret.Code = -1 ret.Msg = "import path is not sub path of import dir" @@ -274,7 +274,7 @@ func importSyncProviderS3(c *gin.Context) { } writePath := filepath.Join(importDir, f.Filename) - if !util.IsSubPath(importDir, writePath) { + if !gulu.File.IsSubPath(importDir, writePath) { logging.LogErrorf("import path [%s] is not sub path of import dir [%s]", writePath, importDir) ret.Code = -1 ret.Msg = "import path is not sub path of import dir" diff --git a/kernel/api/system.go b/kernel/api/system.go index 6b1e2590a..cb0a4b5d0 100644 --- a/kernel/api/system.go +++ b/kernel/api/system.go @@ -442,7 +442,7 @@ func importConf(c *gin.Context) { } writePath := filepath.Join(importDir, f.Filename) - if !util.IsSubPath(importDir, writePath) { + if !gulu.File.IsSubPath(importDir, writePath) { logging.LogErrorf("import path [%s] is not sub path of import dir [%s]", writePath, importDir) ret.Code = -1 ret.Msg = "import path is not sub path of import dir" diff --git a/kernel/api/workspace.go b/kernel/api/workspace.go index a8dca59cb..a2fc03636 100644 --- a/kernel/api/workspace.go +++ b/kernel/api/workspace.go @@ -289,7 +289,7 @@ func setWorkspaceDir(c *gin.Context) { // 改进判断工作空间路径实现 https://github.com/siyuan-note/siyuan/issues/7569 installDirLower := strings.ToLower(filepath.Dir(util.WorkingDir)) pathLower := strings.ToLower(path) - if strings.HasPrefix(pathLower, installDirLower) && (util.IsSubPath(installDirLower, pathLower) || filepath.Clean(installDirLower) == filepath.Clean(pathLower)) { + if strings.HasPrefix(pathLower, installDirLower) && (gulu.File.IsSubPath(installDirLower, pathLower) || filepath.Clean(installDirLower) == filepath.Clean(pathLower)) { ret.Code = -1 ret.Msg = model.Conf.Language(98) ret.Data = map[string]any{"closeTimeout": 5000} diff --git a/kernel/go.mod b/kernel/go.mod index 913ef9858..20f3e114b 100644 --- a/kernel/go.mod +++ b/kernel/go.mod @@ -7,7 +7,7 @@ require ( github.com/88250/clipboard v0.1.5 github.com/88250/epub v0.0.0-20230830085737-c19055cd1f48 github.com/88250/go-humanize v0.0.0-20240424102817-4f78fac47ea7 - github.com/88250/gulu v1.2.3-0.20260124101918-98654a7ca98a + github.com/88250/gulu v1.2.3-0.20260409163331-8c1dab1828ba github.com/88250/lute v1.7.7-0.20260408120251-b51434f68f79 github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1 github.com/ClarkThan/ahocorasick v0.0.0-20231011042242-30d1ef1347f4 diff --git a/kernel/go.sum b/kernel/go.sum index d03f827a0..9769f783a 100644 --- a/kernel/go.sum +++ b/kernel/go.sum @@ -12,8 +12,8 @@ github.com/88250/go-humanize v0.0.0-20240424102817-4f78fac47ea7 h1:MafIFwSS0x6A4 github.com/88250/go-humanize v0.0.0-20240424102817-4f78fac47ea7/go.mod h1:HrKCCTin3YNDSLBD02K0AOljjV6eNwc3/zyEI+xyV1I= github.com/88250/go-sqlite3 v1.14.13-0.20231214121541-e7f54c482950 h1:Pa5hMiBceTVVqrYaDlLio2QSKbXMUmAZPbzCwT5eNCw= github.com/88250/go-sqlite3 v1.14.13-0.20231214121541-e7f54c482950/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/88250/gulu v1.2.3-0.20260124101918-98654a7ca98a h1:s86WMolaqommXG1k2vXg9Gf1iXvgtHSdeD0eeJTQVR8= -github.com/88250/gulu v1.2.3-0.20260124101918-98654a7ca98a/go.mod h1:D+Db16m0N7r9MLZCMcj1a0ZsEGQAxDZkadOn79Gh0vI= +github.com/88250/gulu v1.2.3-0.20260409163331-8c1dab1828ba h1:F9nZLeLTSPUIyYrQqprqElKFef4xVI7fzjxkLXRb2Bo= +github.com/88250/gulu v1.2.3-0.20260409163331-8c1dab1828ba/go.mod h1:D+Db16m0N7r9MLZCMcj1a0ZsEGQAxDZkadOn79Gh0vI= github.com/88250/lute v1.7.7-0.20260408120251-b51434f68f79 h1:HHydQTaIPrXP9g9LcF1T35VoacyJEh5h3N/3oiA0srg= github.com/88250/lute v1.7.7-0.20260408120251-b51434f68f79/go.mod h1:WYyUw//5yVw9BJnoVjx7rI/3szsISxNZCYGOqTIrV0o= github.com/88250/pdfcpu v0.3.14-0.20250424122812-f10e8d9d8d46 h1:Bq1JsDfVbHKUxNL/B2JXd8cC/1h6aFjrlXpGycnh0Hk= diff --git a/kernel/model/assets.go b/kernel/model/assets.go index 0b5d6aeef..f61266a05 100644 --- a/kernel/model/assets.go +++ b/kernel/model/assets.go @@ -561,7 +561,7 @@ func getAssetAbsPath(relativePath string) (absPath string, err error) { // 在 data 文件夹下搜索,主要是 data/assets 文件夹 p := filepath.Join(util.DataDir, relativePath) if gulu.File.IsExist(p) { - if !util.IsSubPath(util.WorkspaceDir, p) { + if !gulu.File.IsSubPath(util.WorkspaceDir, p) { return "", fmt.Errorf("[%s] is not sub path of workspace", p) } return p, nil @@ -594,7 +594,7 @@ func getAssetAbsPath(relativePath string) (absPath string, err error) { }) if "" != absPath { - if !util.IsSubPath(util.WorkspaceDir, absPath) { + if !gulu.File.IsSubPath(util.WorkspaceDir, absPath) { return "", fmt.Errorf("[%s] is not sub path of workspace", absPath) } return absPath, nil diff --git a/kernel/model/publish_access.go b/kernel/model/publish_access.go index 82442c058..af5240738 100644 --- a/kernel/model/publish_access.go +++ b/kernel/model/publish_access.go @@ -245,11 +245,11 @@ func CheckPublishAuthCookie(c *gin.Context, ID string, password string) bool { func CheckAbsPathAccessableByPublishAccess(c *gin.Context, absPath string, publishAccess PublishAccess) bool { absPath = filepath.Clean(absPath) - if util.IsSubPath(util.HistoryDir, absPath) { + if gulu.File.IsSubPath(util.HistoryDir, absPath) { return false } - if util.IsSubPath(util.DataDir, absPath) { + if gulu.File.IsSubPath(util.DataDir, absPath) { relPath, err := filepath.Rel(util.DataDir, absPath) if err != nil { return true diff --git a/kernel/model/sync.go b/kernel/model/sync.go index 5ee0321fb..f610ce0f7 100644 --- a/kernel/model/sync.go +++ b/kernel/model/sync.go @@ -490,7 +490,7 @@ func SetSyncProviderLocal(local *conf.Local) (err error) { return } - if util.IsSubPath(absPath, util.WorkspaceDir) { + if gulu.File.IsSubPath(absPath, util.WorkspaceDir) { msg := fmt.Sprintf("endpoint [%s] is parent of workspace", local.Endpoint) logging.LogErrorf(msg) err = fmt.Errorf(Conf.Language(77), msg) diff --git a/kernel/model/upload.go b/kernel/model/upload.go index 662ac0cb5..3c64e8d3d 100644 --- a/kernel/model/upload.go +++ b/kernel/model/upload.go @@ -67,7 +67,7 @@ func InsertLocalAssets(id string, assetAbsPaths []string, isUpload bool) (succMa continue } - if util.IsSubPath(assetsDirPath, assetAbsPath) { + if gulu.File.IsSubPath(assetsDirPath, assetAbsPath) { // 已经位于 assets 目录下的资源文件不处理 // Dragging a file from the assets folder into the editor causes the kernel to exit https://github.com/siyuan-note/siyuan/issues/15355 succMap[baseName] = "assets/" + fName diff --git a/kernel/server/serve.go b/kernel/server/serve.go index 7a2a5b145..8ac8f7990 100644 --- a/kernel/server/serve.go +++ b/kernel/server/serve.go @@ -444,7 +444,7 @@ func serveAppearance(ginServer *gin.Engine) { } siyuan.GET("/appearance/*filepath", func(c *gin.Context) { filePath := filepath.Join(appearancePath, strings.TrimPrefix(c.Request.URL.Path, "/appearance/")) - if !util.IsSubPath(appearancePath, filePath) { + if !gulu.File.IsSubPath(appearancePath, filePath) { c.Status(http.StatusUnauthorized) return } diff --git a/kernel/util/file.go b/kernel/util/file.go index a94a12fe9..f8fdfc983 100644 --- a/kernel/util/file.go +++ b/kernel/util/file.go @@ -320,34 +320,6 @@ func FilterFileName(name string) string { return name } -func IsSubPath(absPath, toCheckPath string) bool { - if 1 > len(absPath) || 1 > len(toCheckPath) { - return false - } - if absPath == toCheckPath { // 相同路径时不认为是子路径 - return false - } - - if gulu.OS.IsWindows() { - if filepath.IsAbs(absPath) && filepath.IsAbs(toCheckPath) { - if strings.ToLower(absPath)[0] != strings.ToLower(toCheckPath)[0] { - // 不在一个盘 - return false - } - } - } - - up := ".." + string(os.PathSeparator) - rel, err := filepath.Rel(absPath, toCheckPath) - if err != nil { - return false - } - if !strings.HasPrefix(rel, up) && rel != ".." { - return true - } - return false -} - func IsCompressibleAssetImage(p string) bool { lowerName := strings.ToLower(p) return strings.HasPrefix(lowerName, "assets/") && diff --git a/kernel/util/path.go b/kernel/util/path.go index a2cd08a25..f3db6aed7 100644 --- a/kernel/util/path.go +++ b/kernel/util/path.go @@ -359,14 +359,14 @@ func GetAbsPathInWorkspace(relPath string) (string, error) { return absPath, nil } - if IsSubPath(WorkspaceDir, absPath) { + if gulu.File.IsSubPath(WorkspaceDir, absPath) { return absPath, nil } return "", os.ErrPermission } func IsAbsPathInWorkspace(absPath string) bool { - return IsSubPath(WorkspaceDir, absPath) + return gulu.File.IsSubPath(WorkspaceDir, absPath) } // IsWorkspaceDir 判断指定目录是否是工作空间目录。