Skip to content

Commit f372d96

Browse files
committed
🐛 Preserve sensitive home path checks across workspace aliases #19178
1 parent 4f34945 commit f372d96

2 files changed

Lines changed: 107 additions & 20 deletions

File tree

kernel/util/path.go

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -550,26 +550,33 @@ func isSensitivePath(p string) bool {
550550
// 覆盖常见凭据 dotfile,防止通过 globalCopyFiles 等接受工作空间外绝对路径的接口把内核用户
551551
// 家目录下的凭据复制进工作空间后外泄:Git push token、HTTP/API 凭据、Postgres 密码、
552552
// K8s/Docker/容器仓库配置、GPG 私钥环、云厂商 CLI 凭据、包管理器 token 等。
553-
homePrefixes := []string{
554-
strings.ToLower(filepath.Join(HomeDir, ".ssh")),
555-
strings.ToLower(filepath.Join(HomeDir, ".config")),
556-
strings.ToLower(filepath.Join(HomeDir, ".bashrc")),
557-
strings.ToLower(filepath.Join(HomeDir, ".zshrc")),
558-
strings.ToLower(filepath.Join(HomeDir, ".profile")),
559-
strings.ToLower(filepath.Join(HomeDir, ".git-credentials")),
560-
strings.ToLower(filepath.Join(HomeDir, ".netrc")),
561-
strings.ToLower(filepath.Join(HomeDir, ".pgpass")),
562-
strings.ToLower(filepath.Join(HomeDir, ".kube")),
563-
strings.ToLower(filepath.Join(HomeDir, ".docker")),
564-
strings.ToLower(filepath.Join(HomeDir, ".gnupg")),
565-
strings.ToLower(filepath.Join(HomeDir, ".aws")),
566-
strings.ToLower(filepath.Join(HomeDir, ".azure")),
567-
strings.ToLower(filepath.Join(HomeDir, ".npmrc")),
568-
strings.ToLower(filepath.Join(HomeDir, ".pypirc")),
569-
}
570-
for _, hp := range homePrefixes {
571-
if strings.HasPrefix(toCheckPathLower, hp) {
572-
return true
553+
homeDirs := []string{HomeDir}
554+
homeCheckPaths := []string{toCheckPathLower}
555+
if HomeDir != "" && !gulu.File.IsSubPath(HomeDir, p) {
556+
// 工作空间真实路径获得系统目录豁免后,仍需匹配家目录真实路径下的敏感位置。
557+
if resolved, err := filepath.EvalSymlinks(HomeDir); err == nil && resolved != HomeDir {
558+
homeDirs = append(homeDirs, resolved)
559+
}
560+
// 家目录已是真实路径而目标仍使用工作空间别名时,按工作空间根目录映射目标。
561+
// 只映射根目录,保留对尚未创建的导出目标及工作空间内路径的检查。
562+
if inWorkspace && workspaceDir == WorkspaceDir {
563+
if resolved, err := filepath.EvalSymlinks(workspaceDir); err == nil && resolved != workspaceDir {
564+
if rel, err := filepath.Rel(workspaceDir, p); err == nil {
565+
homeCheckPaths = append(homeCheckPaths, strings.ToLower(filepath.Join(resolved, rel)))
566+
}
567+
}
568+
}
569+
}
570+
for _, homeDir := range homeDirs {
571+
for _, name := range []string{
572+
".ssh", ".config", ".bashrc", ".zshrc", ".profile", ".git-credentials", ".netrc", ".pgpass",
573+
".kube", ".docker", ".gnupg", ".aws", ".azure", ".npmrc", ".pypirc",
574+
} {
575+
for _, checkPath := range homeCheckPaths {
576+
if strings.HasPrefix(checkPath, strings.ToLower(filepath.Join(homeDir, name))) {
577+
return true
578+
}
579+
}
573580
}
574581
}
575582

kernel/util/path_alias_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// SiYuan - From thought to insight, with agents
2+
// Copyright (c) 2020-present, b3log.org
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
package util
18+
19+
import (
20+
"os"
21+
"path/filepath"
22+
"testing"
23+
)
24+
25+
// TestSensitivePathAliases 验证工作空间及家目录别名使用一致的敏感路径规则。
26+
func TestSensitivePathAliases(t *testing.T) {
27+
realHome, err := filepath.EvalSymlinks(t.TempDir())
28+
if err != nil {
29+
t.Fatal(err)
30+
}
31+
alias := filepath.Join(t.TempDir(), "home")
32+
origHome, origWorkspace := HomeDir, WorkspaceDir
33+
t.Cleanup(func() { HomeDir, WorkspaceDir = origHome, origWorkspace })
34+
if err := os.MkdirAll(filepath.Join(realHome, "home"), 0755); err != nil {
35+
t.Fatal(err)
36+
}
37+
for _, layout := range []string{"SiYuan", ".config/SiYuan", "siyuan"} {
38+
for _, rel := range []string{"data/emojis/robot.svg", "conf/conf.json", "temp/private.txt", "temp/export/doc.html"} {
39+
p := filepath.Join(realHome, filepath.FromSlash(layout), filepath.FromSlash(rel))
40+
if err := os.MkdirAll(filepath.Dir(p), 0755); err != nil {
41+
t.Fatal(err)
42+
}
43+
if err := os.WriteFile(p, []byte("fixture"), 0600); err != nil {
44+
t.Fatal(err)
45+
}
46+
}
47+
}
48+
if err := os.Symlink(realHome, alias); err != nil {
49+
t.Skipf("create directory symlink failed: %s", err)
50+
}
51+
for _, layout := range []string{"SiYuan", ".config/SiYuan", "siyuan"} {
52+
t.Run(layout, func(t *testing.T) {
53+
HomeDir = alias
54+
if layout == "siyuan" {
55+
HomeDir = filepath.Join(alias, "home")
56+
}
57+
WorkspaceDir = filepath.Join(alias, filepath.FromSlash(layout))
58+
resolved, err := filepath.EvalSymlinks(WorkspaceDir)
59+
if err != nil {
60+
t.Fatalf("resolve %q: %v", WorkspaceDir, err)
61+
}
62+
// 新导出目标尚不存在时也应保留相同的目录访问规则。
63+
for _, rel := range []string{"data/emojis/robot.svg", "conf/conf.json", "temp/private.txt", "temp/export/doc.html", "conf/new.json", "temp/export/new.html"} {
64+
want := layout == ".config/SiYuan" || rel == "conf/conf.json" || rel == "conf/new.json" || rel == "temp/private.txt"
65+
for _, home := range []string{alias, realHome} {
66+
HomeDir = home
67+
if layout == "siyuan" {
68+
HomeDir = filepath.Join(home, "home")
69+
}
70+
for _, root := range []string{WorkspaceDir, resolved} {
71+
p := filepath.Join(root, filepath.FromSlash(rel))
72+
if got := IsSensitivePath(p); got != want {
73+
t.Errorf("IsSensitivePath(%q) = %v, want %v (home %q)", p, got, want, home)
74+
}
75+
}
76+
}
77+
}
78+
})
79+
}
80+
}

0 commit comments

Comments
 (0)