mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-09-05 22:16:59 +00:00
51 lines
1.8 KiB
Go
51 lines
1.8 KiB
Go
package skills
|
|
|
|
import (
|
|
"fmt"
|
|
"path"
|
|
"strings"
|
|
)
|
|
|
|
// ValidateSkillTargetPath validates a relative path inside a managed skill root.
|
|
// Set allowSkillMD when the caller is explicitly editing the primary SKILL.md.
|
|
func ValidateSkillTargetPath(rawPath string, allowSkillMD bool) (string, error) {
|
|
if rawPath == "" {
|
|
return "", fmt.Errorf("invalid file path %q: empty path", rawPath)
|
|
}
|
|
if strings.ContainsRune(rawPath, 0x00) {
|
|
return "", fmt.Errorf("invalid file path %q: null byte", rawPath)
|
|
}
|
|
if len(rawPath) >= 2 && rawPath[1] == ':' {
|
|
return "", fmt.Errorf("invalid file path %q: windows drive paths are not allowed", rawPath)
|
|
}
|
|
normalized := strings.ReplaceAll(rawPath, "\\", "/")
|
|
if strings.HasPrefix(normalized, "/") {
|
|
return "", fmt.Errorf("invalid file path %q: absolute paths are not allowed", rawPath)
|
|
}
|
|
for part := range strings.SplitSeq(normalized, "/") {
|
|
switch part {
|
|
case "..":
|
|
return "", fmt.Errorf("invalid file path %q: parent traversal is not allowed", rawPath)
|
|
case ".git":
|
|
return "", fmt.Errorf("invalid file path %q: system artifact paths are not allowed", rawPath)
|
|
}
|
|
if strings.HasPrefix(part, ".") {
|
|
return "", fmt.Errorf("invalid file path %q: hidden files are not allowed", rawPath)
|
|
}
|
|
}
|
|
cleanPath := path.Clean(normalized)
|
|
if cleanPath == "." || strings.HasPrefix(cleanPath, "../") || cleanPath == ".." || strings.HasPrefix(cleanPath, "/") {
|
|
return "", fmt.Errorf("invalid file path %q: path escapes skill root", rawPath)
|
|
}
|
|
if strings.EqualFold(cleanPath, "SKILL.md") {
|
|
if allowSkillMD {
|
|
return "SKILL.md", nil
|
|
}
|
|
return "", fmt.Errorf("invalid file path %q: SKILL.md must be provided via content or find/replace", rawPath)
|
|
}
|
|
if IsSystemArtifact(cleanPath) {
|
|
return "", fmt.Errorf("invalid file path %q: system artifact paths are not allowed", rawPath)
|
|
}
|
|
return cleanPath, nil
|
|
}
|