diff --git a/handlers/sshd_config/ast/parser_test.go b/handlers/sshd_config/ast/parser_test.go index c7c4876..1cea847 100644 --- a/handlers/sshd_config/ast/parser_test.go +++ b/handlers/sshd_config/ast/parser_test.go @@ -159,10 +159,14 @@ Match 192.168.0.2 } firstOption, firstMatchBlock := p.FindOption(uint32(3)) - if !(firstOption.Key.Value == "PasswordAuthentication" && firstOption.OptionValue.Value == "yes" && firstMatchBlock.MatchEntry.Value == "Match 192.168.0.1") { t.Errorf("Expected first option to be 'PasswordAuthentication yes' and first match block to be 'Match 192.168.0.1', but got: %v, %v", firstOption, firstMatchBlock) } + + emptyOption, matchBlock := p.FindOption(uint32(5)) + if !(emptyOption == nil && matchBlock.MatchEntry.Value == "Match 192.168.0.1") { + t.Errorf("Expected empty option and match block to be 'Match 192.168.0.1', but got: %v, %v", emptyOption, matchBlock) + } } func TestSimpleExampleWithComments( diff --git a/handlers/sshd_config/ast/sshd_config.go b/handlers/sshd_config/ast/sshd_config.go index 5b30c37..818aa9b 100644 --- a/handlers/sshd_config/ast/sshd_config.go +++ b/handlers/sshd_config/ast/sshd_config.go @@ -97,6 +97,8 @@ func (c SSHConfig) FindOption(line uint32) (*SSHOption, *SSHMatchBlock) { if found { return rawEntry.(*SSHOption), matchBlock + } else { + return nil, matchBlock } } diff --git a/handlers/sshd_config/fields/match.go b/handlers/sshd_config/fields/match.go new file mode 100644 index 0000000..332c699 --- /dev/null +++ b/handlers/sshd_config/fields/match.go @@ -0,0 +1,63 @@ +package fields + +var MatchAllowedOptions = map[string]struct{}{ + "AcceptEnv": {}, + "AllowAgentForwarding": {}, + "AllowGroups": {}, + "AllowStreamLocalForwarding": {}, + "AllowTcpForwarding": {}, + "AllowUsers": {}, + "AuthenticationMethods": {}, + "AuthorizedKeysCommand": {}, + "AuthorizedKeysCommandUser": {}, + "AuthorizedKeysFile": {}, + "AuthorizedPrincipalsCommand": {}, + "AuthorizedPrincipalsCommandUser": {}, + "AuthorizedPrincipalsFile": {}, + "Banner": {}, + "CASignatureAlgorithms": {}, + "ChannelTimeout": {}, + "ChrootDirectory": {}, + "ClientAliveCountMax": {}, + "ClientAliveInterval": {}, + "DenyGroups": {}, + "DenyUsers": {}, + "DisableForwarding": {}, + "ExposeAuthInfo": {}, + "ForceCommand": {}, + "GatewayPorts": {}, + "GSSAPIAuthentication": {}, + "HostbasedAcceptedAlgorithms": {}, + "HostbasedAuthentication": {}, + "HostbasedUsesNameFromPacketOnly": {}, + "IgnoreRhosts": {}, + "Include": {}, + "IPQoS": {}, + "KbdInteractiveAuthentication": {}, + "KerberosAuthentication": {}, + "LogLevel": {}, + "MaxAuthTries": {}, + "MaxSessions": {}, + "PasswordAuthentication": {}, + "PermitEmptyPasswords": {}, + "PermitListen": {}, + "PermitOpen": {}, + "PermitRootLogin": {}, + "PermitTTY": {}, + "PermitTunnel": {}, + "PermitUserRC": {}, + "PubkeyAcceptedAlgorithms": {}, + "PubkeyAuthentication": {}, + "PubkeyAuthOptions": {}, + "RekeyLimit": {}, + "RevokedKeys": {}, + "RDomain": {}, + "SetEnv": {}, + "StreamLocalBindMask": {}, + "StreamLocalBindUnlink": {}, + "TrustedUserCAKeys": {}, + "UnusedConnectionTimeout": {}, + "X11DisplayOffset": {}, + "X11Forwarding": {}, + "X11UseLocalhos": {}, +} diff --git a/handlers/sshd_config/handlers/completions.go b/handlers/sshd_config/handlers/completions.go index 8d7814a..cf8d1dd 100644 --- a/handlers/sshd_config/handlers/completions.go +++ b/handlers/sshd_config/handlers/completions.go @@ -17,8 +17,20 @@ func GetRootCompletions( ) ([]protocol.CompletionItem, error) { kind := protocol.CompletionItemKindField + availableOptions := make(map[string]docvalues.Value) + + if parentMatchBlock == nil { + availableOptions = fields.Options + } else { + for option := range fields.MatchAllowedOptions { + if opt, found := fields.Options[option]; found { + availableOptions[option] = opt + } + } + } + return utils.MapMapToSlice( - fields.Options, + availableOptions, func(name string, rawValue docvalues.Value) protocol.CompletionItem { doc := rawValue.(docvalues.DocumentationValue)