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)
|
analyzeMatchBlocks(ctx)
|
||||||
analyzeTokens(ctx)
|
analyzeTokens(ctx)
|
||||||
|
|
||||||
|
@ -11,12 +11,12 @@ import (
|
|||||||
func analyzeQuotesAreValid(
|
func analyzeQuotesAreValid(
|
||||||
ctx *analyzerContext,
|
ctx *analyzerContext,
|
||||||
) {
|
) {
|
||||||
for _, option := range ctx.document.Config.GetAllOptions() {
|
for _, info := range ctx.document.Config.GetAllOptions() {
|
||||||
checkIsUsingDoubleQuotes(ctx, option.Key.Value, option.Key.LocationRange)
|
checkIsUsingDoubleQuotes(ctx, info.Option.Key.Value, info.Option.Key.LocationRange)
|
||||||
checkIsUsingDoubleQuotes(ctx, option.OptionValue.Value, option.OptionValue.LocationRange)
|
checkIsUsingDoubleQuotes(ctx, info.Option.OptionValue.Value, info.Option.OptionValue.LocationRange)
|
||||||
|
|
||||||
checkQuotesAreClosed(ctx, option.Key.Value, option.Key.LocationRange)
|
checkQuotesAreClosed(ctx, info.Option.Key.Value, info.Option.Key.LocationRange)
|
||||||
checkQuotesAreClosed(ctx, option.OptionValue.Value, option.OptionValue.LocationRange)
|
checkQuotesAreClosed(ctx, info.Option.OptionValue.Value, info.Option.OptionValue.LocationRange)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,13 +13,13 @@ import (
|
|||||||
func analyzeTokens(
|
func analyzeTokens(
|
||||||
ctx *analyzerContext,
|
ctx *analyzerContext,
|
||||||
) {
|
) {
|
||||||
for _, option := range ctx.document.Config.GetAllOptions() {
|
for _, info := range ctx.document.Config.GetAllOptions() {
|
||||||
if option.Key == nil || option.OptionValue == nil {
|
if info.Option.Key == nil || info.Option.OptionValue == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
key := option.Key.Key
|
key := info.Option.Key.Key
|
||||||
text := option.OptionValue.Value.Value
|
text := info.Option.OptionValue.Value.Value
|
||||||
var tokens []string
|
var tokens []string
|
||||||
|
|
||||||
if foundTokens, found := fields.OptionsTokensMap[key]; found {
|
if foundTokens, found := fields.OptionsTokensMap[key]; found {
|
||||||
@ -39,7 +39,7 @@ func analyzeTokens(
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
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),
|
Message: fmt.Sprintf("Token '%s' is not allowed for option '%s'", token, optionName),
|
||||||
Severity: &common.SeverityError,
|
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
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c SSHDConfig) GetAllOptions() []*SSHDOption {
|
func (c SSHDConfig) GetAllOptions() []SSHDOptionInfo {
|
||||||
options := make(
|
infos := make(
|
||||||
[]*SSHDOption,
|
[]SSHDOptionInfo,
|
||||||
0,
|
0,
|
||||||
// Approximation, this does not need to be exact
|
// Approximation, this does not need to be exact
|
||||||
c.Options.Size()+10,
|
c.Options.Size()+10,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var currentMatchBlock *SSHDMatchBlock = nil
|
||||||
|
|
||||||
for _, rawEntry := range c.Options.Values() {
|
for _, rawEntry := range c.Options.Values() {
|
||||||
switch entry := rawEntry.(type) {
|
switch entry := rawEntry.(type) {
|
||||||
case *SSHDOption:
|
case *SSHDOption:
|
||||||
options = append(options, entry)
|
infos = append(infos, SSHDOptionInfo{
|
||||||
|
Option: entry,
|
||||||
|
MatchBlock: currentMatchBlock,
|
||||||
|
})
|
||||||
case *SSHDMatchBlock:
|
case *SSHDMatchBlock:
|
||||||
options = append(options, entry.MatchOption)
|
currentMatchBlock = entry
|
||||||
|
|
||||||
for _, rawOption := range entry.Options.Values() {
|
infos = append(infos, SSHDOptionInfo{
|
||||||
options = append(options, rawOption.(*SSHDOption))
|
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))
|
AllOptionsPerName map[fields.NormalizedOptionName](map[*ast.SSHDMatchBlock]([]*ast.SSHDOption))
|
||||||
|
|
||||||
Includes map[uint32]*SSHDIndexIncludeLine
|
Includes map[uint32]*SSHDIndexIncludeLine
|
||||||
|
|
||||||
|
UnknownOptions map[uint32]ast.SSHDOptionInfo
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ func CreateIndexes(config ast.SSHDConfig) (*SSHDIndexes, []common.LSPError) {
|
|||||||
indexes := &SSHDIndexes{
|
indexes := &SSHDIndexes{
|
||||||
AllOptionsPerName: make(map[fields.NormalizedOptionName](map[*ast.SSHDMatchBlock]([]*ast.SSHDOption))),
|
AllOptionsPerName: make(map[fields.NormalizedOptionName](map[*ast.SSHDMatchBlock]([]*ast.SSHDOption))),
|
||||||
Includes: make(map[uint32]*SSHDIndexIncludeLine),
|
Includes: make(map[uint32]*SSHDIndexIncludeLine),
|
||||||
|
UnknownOptions: make(map[uint32]ast.SSHDOptionInfo),
|
||||||
}
|
}
|
||||||
|
|
||||||
it := config.Options.Iterator()
|
it := config.Options.Iterator()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user