Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(parser): ansible configuration support #6595

Merged
merged 24 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
dba49c8
feat(parser): ansible configuration support
cx-henriqueAlvelos Aug 2, 2023
021d936
Merge branch 'master' into kics-970
cx-henriqueAlvelos Aug 2, 2023
bf447c0
Added query + fix parser + docs
cx-henriqueAlvelos Aug 3, 2023
1b777b9
added support to list and float
cx-henriqueAlvelos Aug 3, 2023
5c0f3ff
added support for empty lists
cx-henriqueAlvelos Aug 3, 2023
c655e47
changed analyzer test
cx-henriqueAlvelos Aug 3, 2023
017d0f1
changed query
cx-henriqueAlvelos Aug 3, 2023
52a8c7f
fix metadata
cx-henriqueAlvelos Aug 3, 2023
a68c926
Merge branch 'master' into kics-970
cx-henriqueAlvelos Aug 3, 2023
c3061a2
fixed tests
cx-henriqueAlvelos Aug 3, 2023
1756d8a
moved files
cx-henriqueAlvelos Aug 4, 2023
da58a8d
TODO
cx-henriqueAlvelos Aug 4, 2023
f63f9f8
Merge branch 'master' of https://github.com/Checkmarx/kics into kics-970
cx-henriqueAlvelos Aug 5, 2023
99f0996
Merge branch 'master' into kics-970
cx-henriqueAlvelos Aug 5, 2023
018b8be
Merge branch 'master' into kics-970
cx-henriqueAlvelos Aug 8, 2023
902a569
Fixed tests
cx-henriqueAlvelos Aug 8, 2023
98ff2c3
Added comments and changed file tests
cx-henriqueAlvelos Aug 8, 2023
68432c2
Fix analyzer tests
cx-henriqueAlvelos Aug 8, 2023
11abea1
Merge branch 'master' into kics-970
cx-henriqueAlvelos Aug 9, 2023
08f3391
deleted query
cx-henriqueAlvelos Aug 9, 2023
606d5f7
delete file
cx-henriqueAlvelos Aug 9, 2023
e072efa
added inline comments
cx-henriqueAlvelos Aug 10, 2023
636f4fe
Merge branch 'master' into kics-970
cx-henriqueAlvelos Aug 11, 2023
faa85fa
Merge branch 'master' into kics-970
asofsilva Aug 11, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/platforms.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ KICS supports scanning Ansible files with `.yaml` extension.

KICS can decrypt Ansible Vault files on the fly. For that, you need to define the environment variable `ANSIBLE_VAULT_PASSWORD_FILE`.

## Ansible Config
KICS supports scanning Ansible Configuration files with `.cfg` or `.conf` extension.

## Ansible Inventory
KICS supports scanning Ansible Inventory files with `.ini`, `.json` or `.yaml` extension.

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/alexmullins/zip v0.0.0-20180717182244-4affb64b04d0
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12
github.com/aws/aws-sdk-go v1.44.295
github.com/bigkevmcd/go-configparser v0.0.0-20230427073640-c6b631f70126
github.com/cheggaaa/pb/v3 v3.1.2
github.com/emicklei/proto v1.11.2
github.com/getsentry/sentry-go v0.20.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bigkevmcd/go-configparser v0.0.0-20230427073640-c6b631f70126 h1:uru++pUKoS/yYU3Ohq9VItZdK/cT7FFJH/UUjOlxc+s=
github.com/bigkevmcd/go-configparser v0.0.0-20230427073640-c6b631f70126/go.mod h1:zqqfbfnDeSdRs1WihmMjSbhb2Ptw8Jbus831xoqiIec=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
Expand Down
5 changes: 3 additions & 2 deletions pkg/analyzer/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ var (
"tfvars": true,
".proto": true,
".sh": true,
".cfg": true,
".conf": true,
".ini": true,
}
supportedRegexes = map[string][]string{
Expand Down Expand Up @@ -378,8 +380,7 @@ func (a *analyzerInfo) worker(results, unwanted chan<- string, locCount chan<- i
results <- grpc
locCount <- linesCount
}
// Ansible Inventory Files
case ".ini":
case ".cfg", ".conf", ".ini":
if a.isAvailableType(ansible) {
results <- ansible
locCount <- linesCount
Expand Down
32 changes: 28 additions & 4 deletions pkg/analyzer/analyzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func TestAnalyzer_Analyze(t *testing.T) {
filepath.FromSlash("../../test/fixtures/analyzer_test/dead_symlink")},
typesFromFlag: []string{""},
excludeTypesFromFlag: []string{""},
wantLOC: 602,
wantLOC: 793,
wantErr: false,
gitIgnoreFileName: "",
excludeGitIgnore: false,
Expand Down Expand Up @@ -219,7 +219,7 @@ func TestAnalyzer_Analyze(t *testing.T) {
filepath.FromSlash("../../test/fixtures/analyzer_test/dead_symlink")},
typesFromFlag: []string{"ansible", "pulumi"},
excludeTypesFromFlag: []string{""},
wantLOC: 300,
wantLOC: 491,
wantErr: false,
gitIgnoreFileName: "",
excludeGitIgnore: false,
Expand Down Expand Up @@ -251,7 +251,7 @@ func TestAnalyzer_Analyze(t *testing.T) {
filepath.FromSlash("../../test/fixtures/analyzer_test/dead_symlink")},
typesFromFlag: []string{""},
excludeTypesFromFlag: []string{""},
wantLOC: 602,
wantLOC: 793,
wantErr: false,
gitIgnoreFileName: "",
excludeGitIgnore: false,
Expand All @@ -267,7 +267,7 @@ func TestAnalyzer_Analyze(t *testing.T) {
},
typesFromFlag: []string{""},
excludeTypesFromFlag: []string{""},
wantLOC: 602,
wantLOC: 793,
wantErr: false,
gitIgnoreFileName: "",
excludeGitIgnore: false,
Expand All @@ -284,6 +284,30 @@ func TestAnalyzer_Analyze(t *testing.T) {
gitIgnoreFileName: "",
excludeGitIgnore: false,
},
{
name: "analyze_test_ansible_cfg",
paths: []string{filepath.FromSlash("../../test/fixtures/analyzer_test/ansible.cfg")},
wantTypes: []string{"ansible"},
wantExclude: []string{},
typesFromFlag: []string{""},
excludeTypesFromFlag: []string{""},
wantLOC: 173,
wantErr: false,
gitIgnoreFileName: "",
excludeGitIgnore: false,
},
{
name: "analyze_test_ansible_conf",
paths: []string{filepath.FromSlash("../../test/fixtures/analyzer_test/ansible.conf")},
wantTypes: []string{"ansible"},
wantExclude: []string{},
typesFromFlag: []string{""},
excludeTypesFromFlag: []string{""},
wantLOC: 18,
wantErr: false,
gitIgnoreFileName: "",
excludeGitIgnore: false,
},
}

for _, tt := range tests {
Expand Down
1 change: 1 addition & 0 deletions pkg/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const (
KindCOMMON FileKind = "*"
KindHELM FileKind = "HELM"
KindBUILDAH FileKind = "SH"
KindCFG FileKind = "CFG"
KindINI FileKind = "INI"
)

Expand Down
118 changes: 118 additions & 0 deletions pkg/parser/ansible/ini/config/parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package ansibleconfig

import (
"regexp"
"strconv"
"strings"

"github.com/Checkmarx/kics/pkg/model"
"github.com/Checkmarx/kics/pkg/parser/ansible/ini/comments"
"github.com/bigkevmcd/go-configparser"
)

// Parser defines a parser type
type Parser struct {
}

func (p *Parser) Resolve(fileContent []byte, filename string) ([]byte, error) {
return fileContent, nil
}

// Parse parses .cfg/.conf file and returns it as a Document
func (p *Parser) Parse(filePath string, fileContent []byte) ([]model.Document, []int, error) {
model.NewIgnore.Reset()

reader := strings.NewReader(string(fileContent))
configparser.Delimiters("=")
inline := configparser.InlineCommentPrefixes([]string{";"})

config, err := configparser.ParseReaderWithOptions(reader, inline)
if err != nil {
return nil, nil, err
}

doc := make(map[string]interface{})
doc["groups"] = refactorConfig(config)

ignoreLines := comments.GetIgnoreLines(strings.Split(string(fileContent), "\n"))

return []model.Document{doc}, ignoreLines, nil
}

// refactorConfig removes all extra information and tries to convert
func refactorConfig(config *configparser.ConfigParser) (doc *model.Document) {
doc = emptyDocument()
for _, section := range config.Sections() {
dict, err := config.Items(section)
if err != nil {
continue
}
dictRefact := make(map[string]interface{})
for key, value := range dict {
if boolValue, err := strconv.ParseBool(value); err == nil {
dictRefact[key] = boolValue
} else if floatValue, err := strconv.ParseFloat(value, 64); err == nil {
dictRefact[key] = floatValue
} else if strings.Contains(value, ",") {
re := regexp.MustCompile(`\w+`)
matches := re.FindAllString(value, -1)
if len(matches) > 0 {
dictRefact[key] = matches
} else {
dictRefact[key] = []string{}
}
} else if strings.Contains(value, ":") {
re := regexp.MustCompile(`\w+`)
matches := re.FindAllString(value, -1)
if len(matches) > 0 {
dictRefact[key] = matches
} else {
dictRefact[key] = []string{}
}
} else if value == "[]" {
dictRefact[key] = []string{}
} else {
dictRefact[key] = value
}
}
(*doc)[section] = dictRefact
}

return doc
}

// SupportedExtensions returns extensions supported by this parser, which are only ini extension
func (p *Parser) SupportedExtensions() []string {
return []string{".cfg", ".conf"}
}

// SupportedTypes returns types supported by this parser, which is ansible
func (p *Parser) SupportedTypes() map[string]bool {
return map[string]bool{
"ansible": true,
}
}

// GetKind returns CFG constant kind
func (p *Parser) GetKind() model.FileKind {
return model.KindCFG
}

// GetCommentToken return the comment token of CFG/CONF - #
func (p *Parser) GetCommentToken() string {
return "#"
}

// GetResolvedFiles returns resolved files
func (p *Parser) GetResolvedFiles() map[string]model.ResolvedFile {
return make(map[string]model.ResolvedFile)
}

// StringifyContent converts original content into string formatted version
func (p *Parser) StringifyContent(content []byte) (string, error) {
return string(content), nil
}

func emptyDocument() *model.Document {
return &model.Document{}
}
92 changes: 92 additions & 0 deletions pkg/parser/ansible/ini/config/parser_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package ansibleconfig

import (
"encoding/json"
"testing"

"github.com/Checkmarx/kics/pkg/model"
"github.com/stretchr/testify/require"
)

// TestParser_GetKind tests the functions [GetKind()] and all the methods called by them
func TestParser_GetKind(t *testing.T) {
p := &Parser{}
require.Equal(t, model.KindCFG, p.GetKind())
}

// TestParser_SupportedExtensions tests the functions [SupportedExtensions()] and all the methods called by them
func TestParser_SupportedExtensions(t *testing.T) {
p := &Parser{}
require.Equal(t, []string{".cfg", ".conf"}, p.SupportedExtensions())
}

// TestParser_SupportedExtensions tests the functions [SupportedTypes()] and all the methods called by them
func TestParser_SupportedTypes(t *testing.T) {
p := &Parser{}
require.Equal(t, map[string]bool{
"ansible": true,
}, p.SupportedTypes())
}

// TestParser_Parse tests the functions [Parse()] and all the methods called by them
func TestParser_Parse(t *testing.T) {
type args struct {
content []byte
}
tests := []struct {
name string
p *Parser
args args
want string
wantErr bool
}{
{
name: "CFG to model.Document",
p: &Parser{},
args: args{
content: []byte(`
[defaults]
inventory = /etc/ansible/hosts
library = /usr/share/ansible/
module_utils = /usr/share/ansible/plugins/modules/
inventory_plugins = /usr/share/ansible/plugins/inventory/
roles_path = /etc/ansible/roles
stdout_callback = yaml
forks = 10
strategy = free
httpapi_plugins=~/.ansible/plugins/httpapi:/usr/share/ansible/plugins/httpapi
internal_poll_interval=0.001
inventory_plugins=~/.ansible/plugins/inventory:/usr/share/ansible/plugins/inventory
jinja2_extensions=[]

[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=30m -o ControlPath=/tmp/ansible-ssh-%h-%p-%r

[callback_plugins]
profile_tasks = yes
`),
},
want: "",
wantErr: false,
},
}
for i, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := &Parser{}
switch i {
case 0:
got, _, err := p.Parse("", tt.args.content)
if (err != nil) != tt.wantErr {
t.Errorf("Parser() error = %v, wantErr %v", err, tt.wantErr)
return
}
_, err = json.Marshal(got)
if err != nil {
t.Errorf("json.Marshal() error = %v, wantErr %v", err, tt.wantErr)
return
}
}
})
}
}
2 changes: 2 additions & 0 deletions pkg/scan/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/Checkmarx/kics/pkg/kics"
"github.com/Checkmarx/kics/pkg/model"
"github.com/Checkmarx/kics/pkg/parser"
ansibleConfigParser "github.com/Checkmarx/kics/pkg/parser/ansible/ini/config"
ansibleHostsParser "github.com/Checkmarx/kics/pkg/parser/ansible/ini/hosts"
buildahParser "github.com/Checkmarx/kics/pkg/parser/buildah"
dockerParser "github.com/Checkmarx/kics/pkg/parser/docker"
Expand Down Expand Up @@ -227,6 +228,7 @@ func (c *Client) createService(
Add(&dockerParser.Parser{}).
Add(&protoParser.Parser{}).
Add(&buildahParser.Parser{}).
Add(&ansibleConfigParser.Parser{}).
Add(&ansibleHostsParser.Parser{}).
Build(querySource.Types, querySource.CloudProviders)
if err != nil {
Expand Down
Loading
Loading