mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 23:15:26 +02:00
feat(server): Improve sshd_config; Add unknown option detection
This commit is contained in:
parent
3ac3ebbe50
commit
0c827b04cd
@ -54,6 +54,7 @@ func Analyze(
|
||||
}
|
||||
}
|
||||
|
||||
analyzeValuesAreValid(ctx)
|
||||
analyzeMatchBlocks(ctx)
|
||||
analyzeTokens(ctx)
|
||||
|
||||
|
@ -11,12 +11,12 @@ import (
|
||||
func analyzeQuotesAreValid(
|
||||
ctx *analyzerContext,
|
||||
) {
|
||||
for _, option := range ctx.document.Config.GetAllOptions() {
|
||||
checkIsUsingDoubleQuotes(ctx, option.Key.Value, option.Key.LocationRange)
|
||||
checkIsUsingDoubleQuotes(ctx, option.OptionValue.Value, option.OptionValue.LocationRange)
|
||||
for _, info := range ctx.document.Config.GetAllOptions() {
|
||||
checkIsUsingDoubleQuotes(ctx, info.Option.Key.Value, info.Option.Key.LocationRange)
|
||||
checkIsUsingDoubleQuotes(ctx, info.Option.OptionValue.Value, info.Option.OptionValue.LocationRange)
|
||||
|
||||
checkQuotesAreClosed(ctx, option.Key.Value, option.Key.LocationRange)
|
||||
checkQuotesAreClosed(ctx, option.OptionValue.Value, option.OptionValue.LocationRange)
|
||||
checkQuotesAreClosed(ctx, info.Option.Key.Value, info.Option.Key.LocationRange)
|
||||
checkQuotesAreClosed(ctx, info.Option.OptionValue.Value, info.Option.OptionValue.LocationRange)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,13 +13,13 @@ import (
|
||||
func analyzeTokens(
|
||||
ctx *analyzerContext,
|
||||
) {
|
||||
for _, option := range ctx.document.Config.GetAllOptions() {
|
||||
if option.Key == nil || option.OptionValue == nil {
|
||||
for _, info := range ctx.document.Config.GetAllOptions() {
|
||||
if info.Option.Key == nil || info.Option.OptionValue == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
key := option.Key.Key
|
||||
text := option.OptionValue.Value.Value
|
||||
key := info.Option.Key.Key
|
||||
text := info.Option.OptionValue.Value.Value
|
||||
var tokens []string
|
||||
|
||||
if foundTokens, found := fields.OptionsTokensMap[key]; found {
|
||||
@ -39,7 +39,7 @@ func analyzeTokens(
|
||||
}
|
||||
|
||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||
Range: option.OptionValue.ToLSPRange(),
|
||||
Range: info.Option.OptionValue.ToLSPRange(),
|
||||
Message: fmt.Sprintf("Token '%s' is not allowed for option '%s'", token, optionName),
|
||||
Severity: &common.SeverityError,
|
||||
})
|
||||
|
39
server/handlers/sshd_config/analyzer/values.go
Normal file
39
server/handlers/sshd_config/analyzer/values.go
Normal file
@ -0,0 +1,39 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"config-lsp/handlers/sshd_config/diagnostics"
|
||||
"config-lsp/handlers/sshd_config/fields"
|
||||
)
|
||||
|
||||
func analyzeValuesAreValid(
|
||||
ctx *analyzerContext,
|
||||
) {
|
||||
// Check if there are unknown options
|
||||
for _, info := range ctx.document.Config.GetAllOptions() {
|
||||
normalizedName := fields.CreateNormalizedName(info.Option.Key.Value.Value)
|
||||
|
||||
var isUnknown bool = true
|
||||
|
||||
// Check if the option is unknown
|
||||
if info.MatchBlock == nil {
|
||||
// All options are allowed
|
||||
if _, found := fields.Options[normalizedName]; found {
|
||||
isUnknown = false
|
||||
}
|
||||
} else {
|
||||
// Only `MatchAllowedOptions` are allowed
|
||||
if _, found := fields.MatchAllowedOptions[normalizedName]; found {
|
||||
isUnknown = false
|
||||
}
|
||||
}
|
||||
|
||||
if isUnknown {
|
||||
ctx.diagnostics = append(ctx.diagnostics, diagnostics.GenerateUnknownOption(
|
||||
info.Option.Key.ToLSPRange(),
|
||||
info.Option.Key.Value.Value,
|
||||
))
|
||||
|
||||
ctx.document.Indexes.UnknownOptions[info.Option.Start.Line] = info
|
||||
}
|
||||
}
|
||||
}
|
34
server/handlers/sshd_config/analyzer/values_test.go
Normal file
34
server/handlers/sshd_config/analyzer/values_test.go
Normal file
@ -0,0 +1,34 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
testutils_test "config-lsp/handlers/sshd_config/test_utils"
|
||||
"testing"
|
||||
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
func TestUnknownOptionExample(
|
||||
t *testing.T,
|
||||
) {
|
||||
d := testutils_test.DocumentFromInput(t, `
|
||||
ThisOptionDoesNotExist okay
|
||||
`)
|
||||
ctx := &analyzerContext{
|
||||
document: d,
|
||||
diagnostics: make([]protocol.Diagnostic, 0),
|
||||
}
|
||||
|
||||
analyzeValuesAreValid(ctx)
|
||||
|
||||
if !(len(ctx.diagnostics) == 1) {
|
||||
t.Errorf("Expected 1 error, got %v", len(ctx.diagnostics))
|
||||
}
|
||||
|
||||
if !(len(ctx.document.Indexes.UnknownOptions) == 1) {
|
||||
t.Errorf("Expected 1 unknown option, got %v", len(ctx.document.Indexes.UnknownOptions))
|
||||
}
|
||||
|
||||
if !(ctx.document.Indexes.UnknownOptions[0].Option.Key.Value.Value == "ThisOptionDoesNotExist") {
|
||||
t.Errorf("Expected 'ThisOptionDoesNotExist', got %v", ctx.document.Indexes.UnknownOptions[0].Option.Key.Value.Value)
|
||||
}
|
||||
}
|
8
server/handlers/sshd_config/ast/sshd_config_ast_utils.go
Normal file
8
server/handlers/sshd_config/ast/sshd_config_ast_utils.go
Normal file
@ -0,0 +1,8 @@
|
||||
// Contains structs that are used as utilities, but are
|
||||
// not used for the AST itself
|
||||
package ast
|
||||
|
||||
type SSHDOptionInfo struct {
|
||||
MatchBlock *SSHDMatchBlock
|
||||
Option *SSHDOption
|
||||
}
|
@ -64,26 +64,32 @@ func (c SSHDConfig) FindOption(line uint32) (*SSHDOption, *SSHDMatchBlock) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c SSHDConfig) GetAllOptions() []*SSHDOption {
|
||||
options := make(
|
||||
[]*SSHDOption,
|
||||
func (c SSHDConfig) GetAllOptions() []SSHDOptionInfo {
|
||||
infos := make(
|
||||
[]SSHDOptionInfo,
|
||||
0,
|
||||
// Approximation, this does not need to be exact
|
||||
c.Options.Size()+10,
|
||||
)
|
||||
|
||||
var currentMatchBlock *SSHDMatchBlock = nil
|
||||
|
||||
for _, rawEntry := range c.Options.Values() {
|
||||
switch entry := rawEntry.(type) {
|
||||
case *SSHDOption:
|
||||
options = append(options, entry)
|
||||
infos = append(infos, SSHDOptionInfo{
|
||||
Option: entry,
|
||||
MatchBlock: currentMatchBlock,
|
||||
})
|
||||
case *SSHDMatchBlock:
|
||||
options = append(options, entry.MatchOption)
|
||||
currentMatchBlock = entry
|
||||
|
||||
for _, rawOption := range entry.Options.Values() {
|
||||
options = append(options, rawOption.(*SSHDOption))
|
||||
}
|
||||
infos = append(infos, SSHDOptionInfo{
|
||||
Option: entry.MatchOption,
|
||||
MatchBlock: currentMatchBlock,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
return infos
|
||||
}
|
||||
|
19
server/handlers/sshd_config/diagnostics/diagnostics.go
Normal file
19
server/handlers/sshd_config/diagnostics/diagnostics.go
Normal file
@ -0,0 +1,19 @@
|
||||
package diagnostics
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
"fmt"
|
||||
|
||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||
)
|
||||
|
||||
func GenerateUnknownOption(
|
||||
diagnosticRange protocol.Range,
|
||||
optionName string,
|
||||
) protocol.Diagnostic {
|
||||
return protocol.Diagnostic{
|
||||
Range: diagnosticRange,
|
||||
Message: fmt.Sprintf("Unknown option: %s", optionName),
|
||||
Severity: &common.SeverityError,
|
||||
}
|
||||
}
|
@ -37,4 +37,6 @@ type SSHDIndexes struct {
|
||||
AllOptionsPerName map[fields.NormalizedOptionName](map[*ast.SSHDMatchBlock]([]*ast.SSHDOption))
|
||||
|
||||
Includes map[uint32]*SSHDIndexIncludeLine
|
||||
|
||||
UnknownOptions map[uint32]ast.SSHDOptionInfo
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ func CreateIndexes(config ast.SSHDConfig) (*SSHDIndexes, []common.LSPError) {
|
||||
indexes := &SSHDIndexes{
|
||||
AllOptionsPerName: make(map[fields.NormalizedOptionName](map[*ast.SSHDMatchBlock]([]*ast.SSHDOption))),
|
||||
Includes: make(map[uint32]*SSHDIndexIncludeLine),
|
||||
UnknownOptions: make(map[uint32]ast.SSHDOptionInfo),
|
||||
}
|
||||
|
||||
it := config.Options.Iterator()
|
||||
|
Loading…
x
Reference in New Issue
Block a user