Ð޸´ȫÏîĿÉó²éÎÊÌⲢ·¢²¼ v1.1.9

¸ÄÓñ¾»ú¶ÀÁ¢ CA£¬»ָ´ CLI TLS УÑ飬±£»¤ƾ¾ÝÓëÅäÖò¢·¢±£´档
Ð޸´ Cursor Êý¾ݡ¢»Ự·¾¶¡¢´úÀíÉúÃüÖÜÆڡ¢ģÐÍÇëÇóÓë Skills Îļþ²Ù×÷¡£
Ϊ¶ÀÁ¢ Tab ·þÎñÔö¼ӿͻ§¶˼øȨ£¬Éý¼¶ÒÀÀµÓ빤¾ßÁ´£¬ÍêÉƷ¢²¼ԪÐÅϢͬ²½¡£

Ñé֤£ºÁ½¸ö Go ģ¿éµIJâÊÔ/vet¡¢¹ؼüģ¿é race¡¢ǰ¶˲âÊÔ/Éú²ú¹¹½¨/Éó¼ơ¢
Go ¿ɴï©¶´ɨÃèÓë macOS/Linux ¿ÉÐÐģ¿齻²æ±àÒë¾ùͨ¹ý¡£
Windows ¸ôÀëÆô¶¯ͨ¹ý£»δÍê³ɵĵã»÷ðÑ̺ÍԭÉúƽ̨Ñé֤¼ûÉó²鱨¸档
This commit is contained in:
FuDie0915
2026-11-10 09:59:34 +08:00
parent db73cf5a39
commit 188225fae6
98 changed files with 3324 additions and 930 deletions

113
internal/skills/safety.go Normal file
View File

@@ -0,0 +1,113 @@
package skills
import (
"bytes"
"crypto/sha256"
"fmt"
"io/fs"
"os"
"path"
"path/filepath"
"strings"
"sync"
"cursor/internal/appdata"
"cursor/internal/securefile"
)
var operationMu sync.Mutex
func checkManagedPaths(project, name string) error {
paths := []string{filepath.Join(project, ".cursor"), skillsDir(project), skillTargetDir(project, name), projectCommandsDir(project)}
for _, file := range commandFileNames(name) {
paths = append(paths, filepath.Join(projectCommandsDir(project), file))
}
for _, entry := range paths {
info, err := os.Lstat(entry)
if os.IsNotExist(err) {
continue
}
if err != nil {
return err
}
if info.Mode()&os.ModeSymlink != 0 {
return fmt.Errorf("安全保护:拒绝通过符号链接修改 %s", entry)
}
}
target := skillTargetDir(project, name)
if _, err := os.Stat(target); os.IsNotExist(err) {
return nil
}
return filepath.WalkDir(target, func(p string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.Type()&os.ModeSymlink != 0 {
return fmt.Errorf("安全保护:skill 内含符号链接 %s", p)
}
return nil
})
}
func backupBeforeReplace(target string, next []byte) error {
previous, err := os.ReadFile(target)
if os.IsNotExist(err) {
return nil
}
if err != nil {
return err
}
if bytes.Equal(previous, next) {
return nil
}
id := fmt.Sprintf("%x", sha256.Sum256([]byte(target)))
dir := filepath.Join(appdata.DataRootPath(), "skill-backups", id[:16])
if err = securefile.PrivateDir(dir); err != nil {
return err
}
file, err := os.CreateTemp(dir, filepath.Base(target)+"-*.bak")
if err != nil {
return err
}
name := file.Name()
if err = file.Close(); err != nil {
return err
}
return securefile.Write(name, previous)
}
func ensureNoCustomSkillFiles(project, name string) error {
allowed := map[string]bool{}
err := fs.WalkDir(builtinFS, path.Join(builtinRoot, name), func(p string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() {
rel := strings.TrimPrefix(p, path.Join(builtinRoot, name)+"/")
allowed[rel] = true
}
return nil
})
if err != nil {
return err
}
for _, file := range retiredSkillFiles[name] {
allowed[file] = true
}
target := skillTargetDir(project, name)
if _, err = os.Stat(target); os.IsNotExist(err) {
return nil
}
return filepath.WalkDir(target, func(p string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() {
rel, _ := filepath.Rel(target, p)
if !allowed[filepath.ToSlash(rel)] {
return fmt.Errorf("skill 含自建文件 %s,已保留;请先备份后手动移除", strings.TrimPrefix(p, target))
}
}
return nil
})
}