From 94407289db156db75e6c62c6aeae553fd9c7e147 Mon Sep 17 00:00:00 2001 From: Ona Date: Fri, 26 Sep 2025 00:16:10 +0000 Subject: [PATCH] Add support for quoted values in SSH config - Support both double quotes and single quotes in config values - Handle quoted Include directives (e.g., Include "gitpod/config") - Properly resolve relative paths in includes to ~/.ssh/ directory - Maintain compatibility with unquoted values - Add parseConfigLine function for proper quote handling This fixes compatibility with Gitpod and other tools that generate SSH configs with quoted include paths. Co-authored-by: Ona --- ssh_config.go | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/ssh_config.go b/ssh_config.go index c60ef50..e88bb99 100644 --- a/ssh_config.go +++ b/ssh_config.go @@ -77,14 +77,11 @@ func (sc *SSHConfig) loadConfigFromFileRecursive(path string, visited map[string continue } - parts := strings.Fields(line) - if len(parts) < 2 { - continue + key, value, err := parseConfigLine(line) + if err != nil { + continue // Skip malformed lines } - key := strings.ToLower(parts[0]) - value := strings.Join(parts[1:], " ") - switch key { case "include": // Handle include directive @@ -142,6 +139,13 @@ func (sc *SSHConfig) processInclude(pattern string, visited map[string]bool) err return fmt.Errorf("failed to get home directory: %w", err) } pattern = filepath.Join(homeDir, pattern[2:]) + } else if !filepath.IsAbs(pattern) { + // Relative paths are relative to ~/.ssh/ + homeDir, err := os.UserHomeDir() + if err != nil { + return fmt.Errorf("failed to get home directory: %w", err) + } + pattern = filepath.Join(homeDir, ".ssh", pattern) } // Handle glob patterns @@ -179,4 +183,29 @@ func (sc *SSHConfig) GetHostByName(name string) (*SSHHost, error) { } } return nil, fmt.Errorf("host '%s' not found", name) +} + +// parseConfigLine parses a SSH config line, handling quoted values +func parseConfigLine(line string) (key, value string, err error) { + // Find the first whitespace to separate key from value + parts := strings.SplitN(strings.TrimSpace(line), " ", 2) + if len(parts) < 2 { + return "", "", fmt.Errorf("invalid config line") + } + + key = strings.ToLower(strings.TrimSpace(parts[0])) + valueStr := strings.TrimSpace(parts[1]) + + // Handle quoted values + if len(valueStr) >= 2 && + ((valueStr[0] == '"' && valueStr[len(valueStr)-1] == '"') || + (valueStr[0] == '\'' && valueStr[len(valueStr)-1] == '\'')) { + // Remove quotes + value = valueStr[1 : len(valueStr)-1] + } else { + // Handle unquoted values (may contain multiple words) + value = valueStr + } + + return key, value, nil } \ No newline at end of file