From 09fb9132791ab6cbbc6ac982b32fc280da1a42f4 Mon Sep 17 00:00:00 2001 From: Matt Joyce Date: Sat, 7 Dec 2024 19:04:39 +1100 Subject: [PATCH] Fix #1169: Add robust handling for paths and symlinks in GetAbsolutePath --- common/utils.go | 74 +++++++++++++++++++++++++++++++++++++ plugins/db/fsdb/patterns.go | 13 ++++++- plugins/db/fsdb/storage.go | 67 ++++++++++++++++++--------------- 3 files changed, 124 insertions(+), 30 deletions(-) create mode 100644 common/utils.go diff --git a/common/utils.go b/common/utils.go new file mode 100644 index 00000000..89c0e9c7 --- /dev/null +++ b/common/utils.go @@ -0,0 +1,74 @@ +package common + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "runtime" + "strings" +) + +// GetAbsolutePath resolves a given path to its absolute form, handling ~, ./, ../, UNC paths, and symlinks. +func GetAbsolutePath(path string) (string, error) { + if path == "" { + return "", errors.New("path is empty") + } + + // Handle UNC paths on Windows + if runtime.GOOS == "windows" && strings.HasPrefix(path, `\\`) { + return path, nil + } + + // Handle ~ for home directory expansion + if strings.HasPrefix(path, "~") { + home, err := os.UserHomeDir() + if err != nil { + return "", errors.New("could not resolve home directory") + } + path = filepath.Join(home, path[1:]) + } + + // Convert to absolute path + absPath, err := filepath.Abs(path) + if err != nil { + return "", errors.New("could not get absolute path") + } + + // Resolve symlinks, but allow non-existent paths + resolvedPath, err := filepath.EvalSymlinks(absPath) + if err == nil { + return resolvedPath, nil + } + if os.IsNotExist(err) { + // Return the absolute path for non-existent paths + return absPath, nil + } + + return "", fmt.Errorf("could not resolve symlinks: %w", err) +} + + +// Helper function to check if a symlink points to a directory +func IsSymlinkToDir(path string) bool { + fileInfo, err := os.Lstat(path) + if err != nil { + return false + } + + if fileInfo.Mode()&os.ModeSymlink != 0 { + resolvedPath, err := filepath.EvalSymlinks(path) + if err != nil { + return false + } + + fileInfo, err = os.Stat(resolvedPath) + if err != nil { + return false + } + + return fileInfo.IsDir() + } + + return false // Regular directories should not be treated as symlinks +} diff --git a/plugins/db/fsdb/patterns.go b/plugins/db/fsdb/patterns.go index 23afb10b..79aeaa35 100644 --- a/plugins/db/fsdb/patterns.go +++ b/plugins/db/fsdb/patterns.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strings" + "github.com/danielmiessler/fabric/common" "github.com/danielmiessler/fabric/plugins/template" ) @@ -33,8 +34,16 @@ func (o *PatternsEntity) GetApplyVariables( strings.HasPrefix(source, ".") if isFilePath { - pattern, err = o.getFromFile(source) + // Resolve the file path using GetAbsolutePath + absPath, err := common.GetAbsolutePath(source) + if err != nil { + return nil, fmt.Errorf("could not resolve file path: %v", err) + } + + // Use the resolved absolute path to get the pattern + pattern, err = o.getFromFile(absPath) } else { + // Otherwise, get the pattern from the database pattern, err = o.getFromDB(source) } @@ -42,10 +51,12 @@ func (o *PatternsEntity) GetApplyVariables( return } + // Apply variables to the pattern err = o.applyVariables(pattern, variables, input) return } + func (o *PatternsEntity) applyVariables( pattern *Pattern, variables map[string]string, input string) (err error) { diff --git a/plugins/db/fsdb/storage.go b/plugins/db/fsdb/storage.go index 44e3e0be..21c9e184 100644 --- a/plugins/db/fsdb/storage.go +++ b/plugins/db/fsdb/storage.go @@ -7,7 +7,7 @@ import ( "path/filepath" "strings" - "github.com/samber/lo" + "github.com/danielmiessler/fabric/common" ) type StorageEntity struct { @@ -26,39 +26,48 @@ func (o *StorageEntity) Configure() (err error) { // GetNames finds all patterns in the patterns directory and enters the id, name, and pattern into a slice of Entry structs. it returns these entries or an error func (o *StorageEntity) GetNames() (ret []string, err error) { - var entries []os.DirEntry - if entries, err = os.ReadDir(o.Dir); err != nil { - err = fmt.Errorf("could not read items from directory: %v", err) - return + // Resolve the directory path to an absolute path + absDir, err := common.GetAbsolutePath(o.Dir) + if err != nil { + return nil, fmt.Errorf("could not resolve directory path: %v", err) } - if o.ItemIsDir { - ret = lo.FilterMap(entries, func(item os.DirEntry, index int) (ret string, ok bool) { - if ok = item.IsDir(); ok { - ret = item.Name() - } - return - }) - } else { - if o.FileExtension == "" { - ret = lo.FilterMap(entries, func(item os.DirEntry, index int) (ret string, ok bool) { - if ok = !item.IsDir(); ok { - ret = item.Name() - } - return - }) - } else { - ret = lo.FilterMap(entries, func(item os.DirEntry, index int) (ret string, ok bool) { - if ok = !item.IsDir() && filepath.Ext(item.Name()) == o.FileExtension; ok { - ret = strings.TrimSuffix(item.Name(), o.FileExtension) - } - return - }) - } + // Read the directory entries + var entries []os.DirEntry + if entries, err = os.ReadDir(absDir); err != nil { + return nil, fmt.Errorf("could not read items from directory: %v", err) } - return + + for _, entry := range entries { + entryPath := filepath.Join(absDir, entry.Name()) + + // Get metadata for the entry, including symlink info + fileInfo, err := os.Lstat(entryPath) + if err != nil { + return nil, fmt.Errorf("could not stat entry %s: %v", entryPath, err) + } + + // Determine if the entry should be included + if o.ItemIsDir { + // Include directories or symlinks to directories + if fileInfo.IsDir() || (fileInfo.Mode()&os.ModeSymlink != 0 && common.IsSymlinkToDir(entryPath)) { + ret = append(ret, entry.Name()) + } + } else { + // Include files, optionally filtering by extension + if !fileInfo.IsDir() { + if o.FileExtension == "" || filepath.Ext(entry.Name()) == o.FileExtension { + ret = append(ret, strings.TrimSuffix(entry.Name(), o.FileExtension)) + } + } + } + } + + return ret, nil } + + func (o *StorageEntity) Delete(name string) (err error) { if err = os.Remove(o.BuildFilePathByName(name)); err != nil { err = fmt.Errorf("could not delete %s: %v", name, err)