feat(sshd_config): Add indexes

This commit is contained in:
Myzel394 2024-09-12 23:15:23 +02:00
parent 47c511996b
commit 998c0740d1
No known key found for this signature in database
GPG Key ID: DEC4AAB876F73185
5 changed files with 215 additions and 2 deletions

View File

@ -3,6 +3,7 @@ package analyzer
import (
"config-lsp/common"
"config-lsp/handlers/sshd_config"
"config-lsp/handlers/sshd_config/indexes"
"config-lsp/utils"
protocol "github.com/tliron/glsp/protocol_3_16"
@ -22,5 +23,19 @@ func Analyze(
)
}
indexes, indexErrors := indexes.CreateIndexes(*d.Config)
_ = indexes
errors = append(errors, indexErrors...)
if len(errors) > 0 {
return utils.Map(
errors,
func(err common.LSPError) protocol.Diagnostic {
return err.ToDiagnostic()
},
)
}
return nil
}

View File

@ -158,7 +158,7 @@ Match 192.168.0.2
t.Errorf("Expected sixth entry to be 'MaxAuthTries 3', but got: %v", sixthEntry.Value)
}
firstOption, firstMatchBlock := p.FindOption(uint32(4))
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)

View File

@ -103,7 +103,12 @@ func (c SSHConfig) FindOption(line uint32) (*SSHOption, *SSHMatchBlock) {
rawEntry, found := c.Options.Get(line)
if found {
return rawEntry.(*SSHOption), nil
switch rawEntry.(type) {
case *SSHMatchBlock:
return rawEntry.(*SSHMatchBlock).MatchEntry, rawEntry.(*SSHMatchBlock)
case *SSHOption:
return rawEntry.(*SSHOption), nil
}
}
return nil, nil

View File

@ -0,0 +1,88 @@
package indexes
import (
"config-lsp/common"
"config-lsp/handlers/sshd_config/ast"
"errors"
"fmt"
)
var allowedDoubleOptions = map[string]struct{}{
"AllowGroups": {},
"AllowUsers": {},
"DenyGroups": {},
"DenyUsers": {},
"ListenAddress": {},
"Match": {},
"Port": {},
}
type SSHIndexEntry struct {
Option string
MatchBlock *ast.SSHMatchBlock
}
type SSHIndexes struct {
EntriesPerKey map[SSHIndexEntry][]*ast.SSHOption
}
func CreateIndexes(config ast.SSHConfig) (*SSHIndexes, []common.LSPError) {
errs := make([]common.LSPError, 0)
indexes := &SSHIndexes{
EntriesPerKey: make(map[SSHIndexEntry][]*ast.SSHOption),
}
it := config.Options.Iterator()
for it.Next() {
entry := it.Value().(ast.SSHEntry)
switch entry.(type) {
case *ast.SSHOption:
option := entry.(*ast.SSHOption)
errs = append(errs, addOption(indexes, option, nil)...)
case *ast.SSHMatchBlock:
matchBlock := entry.(*ast.SSHMatchBlock)
it := matchBlock.Options.Iterator()
for it.Next() {
option := it.Value().(*ast.SSHOption)
errs = append(errs, addOption(indexes, option, matchBlock)...)
}
}
}
return indexes, errs
}
func addOption(
i *SSHIndexes,
option *ast.SSHOption,
matchBlock *ast.SSHMatchBlock,
) []common.LSPError {
var errs []common.LSPError
indexEntry := SSHIndexEntry{
Option: option.Key.Value,
MatchBlock: matchBlock,
}
if existingEntry, found := i.EntriesPerKey[indexEntry]; found {
if _, found := allowedDoubleOptions[option.Key.Value]; found {
// Double value, but doubles are allowed
i.EntriesPerKey[indexEntry] = append(existingEntry, option)
} else {
errs = append(errs, common.LSPError{
Range: option.Key.LocationRange,
Err: errors.New(fmt.Sprintf("Option %s is already defined on line %d", option.Key.Value, existingEntry[0].Start.Line+1)),
})
}
} else {
i.EntriesPerKey[indexEntry] = []*ast.SSHOption{option}
}
return errs
}

View File

@ -0,0 +1,105 @@
package indexes
import (
"config-lsp/handlers/sshd_config/ast"
"config-lsp/utils"
"testing"
)
func TestSimpleExample(
t *testing.T,
) {
input := utils.Dedent(`
PermitRootLogin yes
RootLogin yes
PermitRootLogin no
`)
config := ast.NewSSHConfig()
errors := config.Parse(input)
if len(errors) > 0 {
t.Fatalf("Unexpected errors: %v", errors)
}
indexes, errors := CreateIndexes(*config)
if !(len(errors) == 1) {
t.Fatalf("Expected 1 error, but got %v", len(errors))
}
indexEntry := SSHIndexEntry{
Option: "PermitRootLogin",
MatchBlock: nil,
}
if !(indexes.EntriesPerKey[indexEntry][0].Value == "PermitRootLogin yes" && indexes.EntriesPerKey[indexEntry][0].Start.Line == 0) {
t.Errorf("Expected 'PermitRootLogin yes', but got %v", indexes.EntriesPerKey[indexEntry][0].Value)
}
indexEntry = SSHIndexEntry{
Option: "RootLogin",
MatchBlock: nil,
}
if !(indexes.EntriesPerKey[indexEntry][0].Value == "RootLogin yes" && indexes.EntriesPerKey[indexEntry][0].Start.Line == 1) {
t.Errorf("Expected 'RootLogin yes', but got %v", indexes.EntriesPerKey[indexEntry][0].Value)
}
}
func TestComplexExample(
t *testing.T,
) {
input := utils.Dedent(`
PermitRootLogin yes
Port 22
Port 2022
Port 2024
Match Address 192.168.0.1/24
PermitRootLogin no
RoomLogin yes
PermitRootLogin yes
`)
config := ast.NewSSHConfig()
errors := config.Parse(input)
if len(errors) > 0 {
t.Fatalf("Expected no errors, but got %v", len(errors))
}
indexes, errors := CreateIndexes(*config)
if !(len(errors) == 1) {
t.Fatalf("Expected no errors, but got %v", len(errors))
}
indexEntry := SSHIndexEntry{
Option: "PermitRootLogin",
MatchBlock: nil,
}
if !(indexes.EntriesPerKey[indexEntry][0].Value == "PermitRootLogin yes" && indexes.EntriesPerKey[indexEntry][0].Start.Line == 0) {
t.Errorf("Expected 'PermitRootLogin yes' on line 0, but got %v on line %v", indexes.EntriesPerKey[indexEntry][0].Value, indexes.EntriesPerKey[indexEntry][0].Start.Line)
}
firstMatchBlock := config.FindMatchBlock(uint32(6))
indexEntry = SSHIndexEntry{
Option: "PermitRootLogin",
MatchBlock: firstMatchBlock,
}
if !(indexes.EntriesPerKey[indexEntry][0].Value == "\tPermitRootLogin no" && indexes.EntriesPerKey[indexEntry][0].Start.Line == 6) {
t.Errorf("Expected 'PermitRootLogin no' on line 6, but got %v on line %v", indexes.EntriesPerKey[indexEntry][0].Value, indexes.EntriesPerKey[indexEntry][0].Start.Line)
}
// Double check
indexEntry = SSHIndexEntry{
Option: "Port",
MatchBlock: nil,
}
if !(indexes.EntriesPerKey[indexEntry][0].Value == "Port 22" &&
indexes.EntriesPerKey[indexEntry][0].Start.Line == 1 &&
len(indexes.EntriesPerKey[indexEntry]) == 3 &&
indexes.EntriesPerKey[indexEntry][1].Value == "Port 2022" &&
indexes.EntriesPerKey[indexEntry][1].Start.Line == 2 &&
indexes.EntriesPerKey[indexEntry][2].Value == "Port 2024" &&
indexes.EntriesPerKey[indexEntry][2].Start.Line == 3) {
t.Errorf("Expected 'Port 22' on line 1, but got %v on line %v", indexes.EntriesPerKey[indexEntry][0].Value, indexes.EntriesPerKey[indexEntry][0].Start.Line)
}
}