package util import ( "fmt" "os" "path/filepath" "strings" ) // isPathContained attempts to verify whether `path` is the same as or // contained within `directory`. It is overly cautious, returning false even if // `path` IS contained within `directory`, but the two paths use different // casing, and we happen to be on a case-insensitive filesystem. // This is ultimately to attempt to stop an LLM from going outside of where I // tell it to. Additional layers of security should be considered.. run in a // VM/container. func IsPathContained(directory string, path string) (bool, error) { // Clean and resolve symlinks for both paths path, err := filepath.Abs(path) if err != nil { return false, err } // check if path exists _, err = os.Stat(path) if err != nil { if !os.IsNotExist(err) { return false, fmt.Errorf("Could not stat path: %v", err) } } else { path, err = filepath.EvalSymlinks(path) if err != nil { return false, err } } directory, err = filepath.Abs(directory) if err != nil { return false, err } directory, err = filepath.EvalSymlinks(directory) if err != nil { return false, err } // Case insensitive checks if !strings.EqualFold(path, directory) && !strings.HasPrefix(strings.ToLower(path), strings.ToLower(directory)+string(os.PathSeparator)) { return false, nil } return true, nil } func IsPathWithinCWD(path string) (bool, string) { cwd, err := os.Getwd() if err != nil { return false, "Failed to determine current working directory" } if ok, err := IsPathContained(cwd, path); !ok { if err != nil { return false, fmt.Sprintf("Could not determine whether path '%s' is within the current working directory: %s", path, err.Error()) } return false, fmt.Sprintf("Path '%s' is not within the current working directory", path) } return true, "" } // AddLineNumbers takes a string of content and returns a new string with line // numbers prefixed func AddLineNumbers(content string) string { lines := strings.Split(strings.TrimSuffix(content, "\n"), "\n") result := strings.Builder{} for i, line := range lines { result.WriteString(fmt.Sprintf("%d\t%s\n", i+1, line)) } return result.String() }