package config import ( "fmt" "path/filepath" "strings" ) var allowedExtensions = map[string]bool{ ".json": true, ".yaml": true, ".yml": true, } // ValidatePath checks that the given relative path is safe and points to a supported file type. // It prevents directory traversal and rejects unsupported extensions. func ValidatePath(relPath string) error { if relPath == "" { return fmt.Errorf("empty path") } cleaned := filepath.Clean(relPath) if filepath.IsAbs(cleaned) { return fmt.Errorf("absolute paths not allowed") } for _, part := range strings.Split(cleaned, string(filepath.Separator)) { if part == ".." { return fmt.Errorf("path traversal not allowed") } } ext := strings.ToLower(filepath.Ext(cleaned)) if !allowedExtensions[ext] { return fmt.Errorf("unsupported file extension %q; allowed: .json, .yaml, .yml", ext) } return nil } // FullPath joins the base config directory with the validated relative path. func FullPath(baseDir, relPath string) (string, error) { if err := ValidatePath(relPath); err != nil { return "", err } full := filepath.Join(baseDir, filepath.Clean(relPath)) return full, nil }