diff --git a/handlers/sshd_config/analyzer/match.go b/handlers/sshd_config/analyzer/match.go index b508e86..ad316bb 100644 --- a/handlers/sshd_config/analyzer/match.go +++ b/handlers/sshd_config/analyzer/match.go @@ -2,7 +2,9 @@ package analyzer import ( "config-lsp/common" + docvalues "config-lsp/doc-values" sshdconfig "config-lsp/handlers/sshd_config" + "config-lsp/handlers/sshd_config/fields" "config-lsp/handlers/sshd_config/match-parser" "config-lsp/utils" "errors" @@ -23,6 +25,7 @@ func analyzeMatchBlocks( Range: option.LocationRange, Err: errors.New("A match expression is required"), }) + continue } @@ -32,12 +35,15 @@ func analyzeMatchBlocks( Range: entry.LocationRange, Err: errors.New(fmt.Sprintf("A value for %s is required", entry.Criteria.Type)), }) - } else { - errs = append(errs, analyzeMatchValuesContainsPositiveValue(entry.Values)...) - for _, value := range entry.Values.Values { - errs = append(errs, analyzeMatchValueNegation(value)...) - } + continue + } + + errs = append(errs, analyzeMatchValuesContainsPositiveValue(entry.Values)...) + + for _, value := range entry.Values.Values { + errs = append(errs, analyzeMatchValueNegation(value)...) + errs = append(errs, analyzeMatchValueIsValid(value, entry.Criteria.Type)...) } } @@ -109,3 +115,33 @@ func analyzeMatchValuesContainsPositiveValue( return nil } + +func analyzeMatchValueIsValid( + value *matchparser.MatchValue, + criteria matchparser.MatchCriteriaType, +) []common.LSPError { + errs := make([]common.LSPError, 0) + + if value.Value.Raw == "" { + return errs + } + + docOption := fields.MatchValueFieldMap[criteria] + invalidValues := docOption.CheckIsValid(value.Value.Raw) + + errs = append( + errs, + utils.Map( + invalidValues, + func(invalidValue *docvalues.InvalidValue) common.LSPError { + err := docvalues.LSPErrorFromInvalidValue(value.Start.Line, *invalidValue) + err.ShiftCharacter(value.Start.Character) + + return err + }, + )..., + ) + + return errs +} + diff --git a/handlers/sshd_config/analyzer/match_test.go b/handlers/sshd_config/analyzer/match_test.go index c0b3cfe..3ccb5c7 100644 --- a/handlers/sshd_config/analyzer/match_test.go +++ b/handlers/sshd_config/analyzer/match_test.go @@ -61,3 +61,70 @@ Match User !root,!admin t.Errorf("Expected 1 error, got %v", len(errors)) } } + +func TestEmptyMatchValues( + t *testing.T, +) { + input := utils.Dedent(` +PermitRootLogin yes +Match User +`) + c := ast.NewSSHDConfig() + + errors := c.Parse(input) + + if len(errors) > 0 { + t.Fatalf("Parse error: %v", errors) + } + + i, errors := indexes.CreateIndexes(*c) + + if len(errors) > 0 { + t.Fatalf("Index error: %v", errors) + } + + d := &sshdconfig.SSHDDocument{ + Config: c, + Indexes: i, + } + + errors = analyzeMatchBlocks(d) + + if !(len(errors) == 1) { + t.Errorf("Expected 1 error, got %v", len(errors)) + } +} + +func TestIncompleteMatchValues( + t *testing.T, +) { + input := utils.Dedent(` +PermitRootLogin yes +Match User +`) + c := ast.NewSSHDConfig() + + errors := c.Parse(input) + + if len(errors) > 0 { + t.Fatalf("Parse error: %v", errors) + } + + i, errors := indexes.CreateIndexes(*c) + + if len(errors) > 0 { + t.Fatalf("Index error: %v", errors) + } + + d := &sshdconfig.SSHDDocument{ + Config: c, + Indexes: i, + } + + errors = analyzeMatchBlocks(d) + + if !(len(errors) == 1) { + t.Errorf("Expected 1 error, got %v", len(errors)) + } +} + diff --git a/handlers/sshd_config/analyzer/options.go b/handlers/sshd_config/analyzer/options.go index 10ba045..5e40cef 100644 --- a/handlers/sshd_config/analyzer/options.go +++ b/handlers/sshd_config/analyzer/options.go @@ -109,24 +109,6 @@ func checkMatchBlock( ) []common.LSPError { errs := make([]common.LSPError, 0) - matchOption := matchBlock.MatchOption.OptionValue - if matchOption != nil { - invalidValues := fields.Options["Match"].CheckIsValid(matchOption.Value.Value) - - errs = append( - errs, - utils.Map( - invalidValues, - func(invalidValue *docvalues.InvalidValue) common.LSPError { - err := docvalues.LSPErrorFromInvalidValue(matchBlock.Start.Line, *invalidValue) - err.ShiftCharacter(matchOption.Start.Character) - - return err - }, - )..., - ) - } - it := matchBlock.Options.Iterator() for it.Next() { diff --git a/handlers/sshd_config/fields/fields.go b/handlers/sshd_config/fields/fields.go index 45e4ebb..dd81bd5 100644 --- a/handlers/sshd_config/fields/fields.go +++ b/handlers/sshd_config/fields/fields.go @@ -10,16 +10,6 @@ var ZERO = 0 var MAX_PORT = 65535 var MAX_FILE_MODE = 0777 -var AllowedDuplicateOptions = map[string]struct{}{ - "AllowGroups": {}, - "AllowUsers": {}, - "DenyGroups": {}, - "DenyUsers": {}, - "ListenAddress": {}, - "Match": {}, - "Port": {}, -} - var Options = map[string]docvalues.DocumentationValue{ "AcceptEnv": { Documentation: `Specifies what environment variables sent by the client will be copied into the session's environ(7). See SendEnv and SetEnv in ssh_config(5) for how to configure the client. The TERM environment variable is always accepted whenever the client requests a pseudo-terminal as it is required by the protocol. Variables are specified by name, which may contain the wildcard characters ‘*’ and ‘?’. Multiple environment variables may be separated by whitespace or spread across multiple AcceptEnv directives. Be warned that some environment variables could be used to bypass restricted user environments. For this reason, care should be taken in the use of this directive. The default is not to accept any environment variables.`, diff --git a/handlers/sshd_config/fields/match.go b/handlers/sshd_config/fields/match.go index 59da836..b61943e 100644 --- a/handlers/sshd_config/fields/match.go +++ b/handlers/sshd_config/fields/match.go @@ -1,6 +1,9 @@ package fields -import docvalues "config-lsp/doc-values" +import ( + docvalues "config-lsp/doc-values" + matchparser "config-lsp/handlers/sshd_config/match-parser" +) var MatchAllowedOptions = map[string]struct{}{ "AcceptEnv": {}, @@ -75,3 +78,13 @@ var MatchAddressField = docvalues.IPAddressValue{ AllowIPv6: true, AllowRange: true, } + +var MatchValueFieldMap = map[matchparser.MatchCriteriaType]docvalues.Value{ + matchparser.MatchCriteriaTypeUser: MatchUserField, + matchparser.MatchCriteriaTypeGroup: MatchGroupField, + matchparser.MatchCriteriaTypeHost: MatchHostField, + matchparser.MatchCriteriaTypeLocalAddress: MatchLocalAddressField, + matchparser.MatchCriteriaTypeLocalPort: MatchLocalPortField, + matchparser.MatchCriteriaTypeRDomain: MatchRDomainField, + matchparser.MatchCriteriaTypeAddress: MatchAddressField, +} diff --git a/handlers/sshd_config/fields/options.go b/handlers/sshd_config/fields/options.go new file mode 100644 index 0000000..f3a2ee8 --- /dev/null +++ b/handlers/sshd_config/fields/options.go @@ -0,0 +1,12 @@ +package fields + +var AllowedDuplicateOptions = map[string]struct{}{ + "AllowGroups": {}, + "AllowUsers": {}, + "DenyGroups": {}, + "DenyUsers": {}, + "ListenAddress": {}, + "Match": {}, + "Port": {}, +} +