Files
Elydora-Go-SDK/cmd/elydora/plugins/runtime_paths.go
T

144 lines
4.1 KiB
Go

package plugins
import (
"fmt"
"os"
"path/filepath"
"regexp"
"runtime"
"strings"
)
var windowsDeviceName = regexp.MustCompile(
`(?i)^(?:con|prn|aux|nul|com[1-9¹²³]|lpt[1-9¹²³])(?:\.|$)`,
)
// AgentRuntimeRoot returns the local root for all Elydora agent runtimes.
func AgentRuntimeRoot() (string, error) {
home, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("resolve home directory: %w", err)
}
root, err := filepath.Abs(filepath.Join(home, ".elydora"))
if err != nil {
return "", fmt.Errorf("resolve Elydora runtime root: %w", err)
}
return root, nil
}
// ResolveAgentRuntimeDirectory confines an agent ID to one portable path segment.
func ResolveAgentRuntimeDirectory(agentID string) (string, error) {
if invalidAgentDirectoryName(agentID) {
return "", fmt.Errorf(
"invalid agent ID for local storage: %q must be a single non-empty path segment with a portable file name",
agentID,
)
}
root, err := AgentRuntimeRoot()
if err != nil {
return "", err
}
candidate := filepath.Join(root, agentID)
relative, err := filepath.Rel(root, candidate)
if err != nil {
return "", fmt.Errorf("resolve agent runtime path for %q: %w", agentID, err)
}
if relative == "." || relative == ".." || filepath.IsAbs(relative) ||
strings.HasPrefix(relative, ".."+string(os.PathSeparator)) ||
strings.ContainsRune(relative, os.PathSeparator) {
return "", fmt.Errorf("agent ID escapes the local storage directory: %q", agentID)
}
if _, err := RequirePhysicalDirectory(root); err != nil {
return "", err
}
if _, err := RequirePhysicalDirectory(candidate); err != nil {
return "", err
}
return candidate, nil
}
func invalidAgentDirectoryName(agentID string) bool {
if agentID == "" || agentID == "." || agentID == ".." ||
strings.ContainsAny(agentID, `<>:"/\|?*`) ||
strings.HasPrefix(agentID, " ") || strings.HasSuffix(agentID, ".") ||
strings.HasSuffix(agentID, " ") ||
windowsDeviceName.MatchString(agentID) {
return true
}
for _, character := range agentID {
if character <= 31 {
return true
}
}
return false
}
// EnsurePrivateDirectory creates an owner-only physical directory.
func EnsurePrivateDirectory(path string) error {
if err := os.MkdirAll(path, 0700); err != nil {
return fmt.Errorf("create private directory %s: %w", path, err)
}
info, err := os.Lstat(path)
if err != nil {
return fmt.Errorf("inspect private directory %s: %w", path, err)
}
if info.Mode()&os.ModeSymlink != 0 || !info.IsDir() {
return fmt.Errorf("private directory path is not a physical directory: %s", path)
}
if runtime.GOOS != "windows" {
if err := os.Chmod(path, 0700); err != nil {
return fmt.Errorf("restrict private directory %s: %w", path, err)
}
}
return nil
}
// PrepareAgentRuntimeDirectory validates and creates the private runtime path.
func PrepareAgentRuntimeDirectory(agentID string) (string, error) {
root, err := AgentRuntimeRoot()
if err != nil {
return "", err
}
agentDirectory, err := ResolveAgentRuntimeDirectory(agentID)
if err != nil {
return "", err
}
if err := EnsurePrivateDirectory(root); err != nil {
return "", err
}
if err := EnsurePrivateDirectory(agentDirectory); err != nil {
return "", err
}
return agentDirectory, nil
}
// RequirePhysicalDirectory reports whether a path is an existing physical directory.
func RequirePhysicalDirectory(path string) (bool, error) {
info, err := os.Lstat(path)
if os.IsNotExist(err) {
return false, nil
}
if err != nil {
return false, fmt.Errorf("inspect agent runtime directory %s: %w", path, err)
}
if info.Mode()&os.ModeSymlink != 0 || !info.IsDir() {
return false, fmt.Errorf("agent runtime path is not a physical directory: %s", path)
}
return true, nil
}
// RequirePhysicalFile reports whether a path is an existing physical regular file.
func RequirePhysicalFile(path string) (bool, error) {
info, err := os.Lstat(path)
if os.IsNotExist(err) {
return false, nil
}
if err != nil {
return false, fmt.Errorf("inspect agent runtime file %s: %w", path, err)
}
if info.Mode()&os.ModeSymlink != 0 || !info.Mode().IsRegular() {
return false, fmt.Errorf("agent runtime config is not a physical file: %s", path)
}
return true, nil
}