mirror of
https://github.com/Gu1llaum-3/sshm.git
synced 2026-01-27 03:04:21 +01:00
fix: problems with quotes from Host names and support SSH tokens (issue #32)
This commit is contained in:
@@ -283,8 +283,12 @@ func parseSSHConfigFileWithProcessedFiles(configPath string, processedFiles map[
|
||||
hostNames := strings.Fields(value)
|
||||
|
||||
// Skip hosts with wildcards (*, ?) as they are typically patterns, not actual hosts
|
||||
// Also remove surrounding quotes from host names
|
||||
var validHostNames []string
|
||||
for _, hostName := range hostNames {
|
||||
// Remove surrounding double quotes if present
|
||||
hostName = strings.Trim(hostName, `"`)
|
||||
|
||||
if !strings.ContainsAny(hostName, "*?") {
|
||||
validHostNames = append(validHostNames, hostName)
|
||||
}
|
||||
@@ -896,6 +900,9 @@ func quickHostSearchInFile(hostName string, configPath string, processedFiles ma
|
||||
|
||||
// Check if our target host is in this Host declaration
|
||||
for _, candidateHostName := range hostNames {
|
||||
// Remove surrounding double quotes if present
|
||||
candidateHostName = strings.Trim(candidateHostName, `"`)
|
||||
|
||||
// Skip hosts with wildcards (*, ?) as they are typically patterns
|
||||
if !strings.ContainsAny(candidateHostName, "*?") && candidateHostName == hostName {
|
||||
return true, nil // Found the host!
|
||||
|
||||
@@ -1694,3 +1694,89 @@ Host production-server
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSSHConfigWithQuotedHostNames(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
configFile := filepath.Join(tempDir, "config")
|
||||
configContent := `# Test hosts with quoted names (issue #32)
|
||||
Host "my-host-name-01"
|
||||
HostName my-host-name-01.cwd.pub.domain.net
|
||||
Port 2222
|
||||
User my_user
|
||||
|
||||
Host "qa-test-vm"
|
||||
HostName qa-test-vm.example.com
|
||||
User guillaume
|
||||
Port 22
|
||||
|
||||
Host normal-host
|
||||
HostName normal.example.com
|
||||
User testuser
|
||||
|
||||
Host "quoted1" "quoted2"
|
||||
HostName multi.example.com
|
||||
User multiuser
|
||||
`
|
||||
|
||||
err := os.WriteFile(configFile, []byte(configContent), 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create config: %v", err)
|
||||
}
|
||||
|
||||
hosts, err := ParseSSHConfigFile(configFile)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseSSHConfigFile() error = %v", err)
|
||||
}
|
||||
|
||||
// Should get 5 hosts: my-host-name-01, qa-test-vm, normal-host, quoted1, quoted2
|
||||
// All without quotes
|
||||
expectedHosts := map[string]struct{}{
|
||||
"my-host-name-01": {},
|
||||
"qa-test-vm": {},
|
||||
"normal-host": {},
|
||||
"quoted1": {},
|
||||
"quoted2": {},
|
||||
}
|
||||
|
||||
if len(hosts) != len(expectedHosts) {
|
||||
t.Errorf("Expected %d hosts, got %d", len(expectedHosts), len(hosts))
|
||||
for _, host := range hosts {
|
||||
t.Logf("Found host: %q", host.Name)
|
||||
}
|
||||
}
|
||||
|
||||
hostMap := make(map[string]SSHHost)
|
||||
for _, host := range hosts {
|
||||
// Verify no quotes in host names
|
||||
if strings.Contains(host.Name, `"`) {
|
||||
t.Errorf("Host name %q still contains quotes", host.Name)
|
||||
}
|
||||
hostMap[host.Name] = host
|
||||
}
|
||||
|
||||
for expectedHostName := range expectedHosts {
|
||||
if _, found := hostMap[expectedHostName]; !found {
|
||||
t.Errorf("Expected host %q not found", expectedHostName)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify specific host details
|
||||
if host, found := hostMap["my-host-name-01"]; found {
|
||||
if host.Hostname != "my-host-name-01.cwd.pub.domain.net" {
|
||||
t.Errorf("Host my-host-name-01 has wrong hostname: %q", host.Hostname)
|
||||
}
|
||||
if host.Port != "2222" {
|
||||
t.Errorf("Host my-host-name-01 has wrong port: %q", host.Port)
|
||||
}
|
||||
if host.User != "my_user" {
|
||||
t.Errorf("Host my-host-name-01 has wrong user: %q", host.User)
|
||||
}
|
||||
}
|
||||
|
||||
if host, found := hostMap["qa-test-vm"]; found {
|
||||
if host.Hostname != "qa-test-vm.example.com" {
|
||||
t.Errorf("Host qa-test-vm has wrong hostname: %q", host.Hostname)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
)
|
||||
|
||||
// ValidateHostname checks if a hostname is valid
|
||||
// Accepts regular hostnames, IP addresses, and SSH tokens like %h, %p, %r, %u, %n, %C, %d, %i, %k, %L, %l, %T
|
||||
func ValidateHostname(hostname string) bool {
|
||||
if len(hostname) == 0 || len(hostname) > 253 {
|
||||
return false
|
||||
@@ -22,7 +23,18 @@ func ValidateHostname(hostname string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
hostnameRegex := regexp.MustCompile(`^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$`)
|
||||
// Check if hostname contains SSH tokens (e.g., %h, %p, %r, %u, %n, etc.)
|
||||
// SSH tokens are documented in ssh_config(5) man page
|
||||
sshTokenRegex := regexp.MustCompile(`%[hprunCdiklLT]`)
|
||||
if sshTokenRegex.MatchString(hostname) {
|
||||
// If it contains SSH tokens, it's a valid SSH config construct
|
||||
return true
|
||||
}
|
||||
|
||||
// RFC 1123: each label must start with alphanumeric, end with alphanumeric,
|
||||
// and contain only alphanumeric and hyphens. Labels are 1-63 chars.
|
||||
// A hostname is one or more labels separated by dots.
|
||||
hostnameRegex := regexp.MustCompile(`^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]|[a-zA-Z0-9]{0,62})?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]|[a-zA-Z0-9]{0,62})?)*$`)
|
||||
return hostnameRegex.MatchString(hostname)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,19 @@ func TestValidateHostname(t *testing.T) {
|
||||
{"hostname ending with dot", "example.com.", false},
|
||||
{"hostname with hyphen", "my-server.com", true},
|
||||
{"hostname starting with number", "1example.com", true},
|
||||
{"multiple hyphens and subdomains", "my-host-name-01.cwd.pub.domain.net", true},
|
||||
{"multiple hyphens", "my-host-name-01", true},
|
||||
{"complex hostname with hyphens", "server-01-prod.data-center.example.com", true},
|
||||
{"hostname with consecutive hyphens", "my--server.com", true},
|
||||
{"single char labels", "a.b.c.d.com", true},
|
||||
// SSH tokens support (issue #32 comment)
|
||||
{"SSH token %h", "%h.server.com", true},
|
||||
{"SSH token %p", "server.com:%p", true},
|
||||
{"SSH token %r", "%r@server.com", true},
|
||||
{"SSH token %u", "%u.example.com", true},
|
||||
{"SSH token %n", "%n.domain.net", true},
|
||||
{"SSH token %C", "host-%C.com", true},
|
||||
{"multiple SSH tokens", "%h.%u.server.com", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
Reference in New Issue
Block a user