Skip to content

Commit

Permalink
toolsconfig: create tests and rename public objects
Browse files Browse the repository at this point in the history
Previously the toolsconfig package does not have any unit tests and the
public functions and types was a bit confused, since two types was
exported to represents the same "thing".

This commit implements the tests to cover scenarios of toolsconfig
parsing.

This commit also rename ToolsConfigStruct to toolsConfig and also made
private, since this struct is only used as schema to parse the values
and only the Map type is used by other packages. The Map and Config
struct was also renamed to don't be repetitive on names.

The function ParseInterfaceToMapToolsConfig was also renamed to
MustParseToolsConfig to follow the Go standards of functions that can
cause errors that will be not returned. The signature was also changed
to avoid bugs when accepting an empty interface{}, since the viper will
always return a map[string]interface{} when we get the tools config from
config file, this function does not need to accept an empty interface.

A new function Default was also created to return the default values
from tools config.

Updates #718

Signed-off-by: Matheus Alcantara <matheus.alcantara@zup.com.br>
  • Loading branch information
matheusalcantarazup committed Nov 11, 2021
1 parent 77afb54 commit 9bb7c0d
Show file tree
Hide file tree
Showing 26 changed files with 353 additions and 121 deletions.
8 changes: 4 additions & 4 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ type StartOptions struct {
FalsePositiveHashes []string `json:"false_positive_hashes"`
RiskAcceptHashes []string `json:"risk_accept_hashes"`
ShowVulnerabilitiesTypes []string `json:"show_vulnerabilities_types"`
ToolsConfig toolsconfig.MapToolConfig `json:"tools_config"`
ToolsConfig toolsconfig.ToolsConfig `json:"tools_config"`
Headers map[string]string `json:"headers"`
WorkDir *workdir.WorkDir `json:"work_dir"`
CustomImages customimages.CustomImages `json:"custom_images"`
Expand Down Expand Up @@ -167,7 +167,7 @@ func New() *Config {
FalsePositiveHashes: make([]string, 0),
Headers: make(map[string]string),
ContainerBindProjectPath: "",
ToolsConfig: toolsconfig.ParseInterfaceToMapToolsConfig(toolsconfig.ToolConfig{}),
ToolsConfig: toolsconfig.Default(),
ShowVulnerabilitiesTypes: []string{vulnerability.Vulnerability.ToString()},
CustomImages: customimages.NewCustomImages(),
DisableDocker: dist.IsStandAlone(),
Expand Down Expand Up @@ -296,8 +296,8 @@ func (c *Config) LoadFromConfigFile() *Config {
viper.GetString(c.toLowerCamel(EnvContainerBindProjectPath)), c.ContainerBindProjectPath,
)

if cfg := viper.Get(c.toLowerCamel(EnvToolsConfig)); cfg != nil {
c.ToolsConfig = toolsconfig.ParseInterfaceToMapToolsConfig(cfg)
if cfg := viper.GetStringMap(c.toLowerCamel(EnvToolsConfig)); cfg != nil {
c.ToolsConfig = toolsconfig.MustParseToolsConfig(cfg)
}

c.DisableDocker = viper.GetBool(c.toLowerCamel(EnvDisableDocker))
Expand Down
6 changes: 3 additions & 3 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func TestNewHorusecConfig(t *testing.T) {
assert.Equal(t, true, configs.EnableOwaspDependencyCheck)
assert.Equal(t, true, configs.EnableShellCheck)
assert.Equal(t, []string{vulnerability.Vulnerability.ToString(), vulnerability.FalsePositive.ToString()}, configs.ShowVulnerabilitiesTypes)
assert.Equal(t, toolsconfig.ToolConfig{
assert.Equal(t, toolsconfig.Config{
IsToIgnore: true,
}, configs.ToolsConfig[tools.GoSec])
assert.Equal(t, "docker.io/company/go:latest", configs.CustomImages["go"])
Expand Down Expand Up @@ -157,7 +157,7 @@ func TestNewHorusecConfig(t *testing.T) {
assert.Equal(t, true, configs.EnableInformationSeverity)
assert.Equal(t, true, configs.EnableOwaspDependencyCheck)
assert.Equal(t, true, configs.EnableShellCheck)
assert.Equal(t, toolsconfig.ToolConfig{
assert.Equal(t, toolsconfig.Config{
IsToIgnore: true,
}, configs.ToolsConfig[tools.GoSec])
assert.Equal(t, "docker.io/company/go:latest", configs.CustomImages["go"])
Expand Down Expand Up @@ -260,7 +260,7 @@ func TestNewHorusecConfig(t *testing.T) {
assert.Equal(t, true, configs.EnableInformationSeverity)
assert.Equal(t, true, configs.EnableOwaspDependencyCheck)
assert.Equal(t, true, configs.EnableShellCheck)
assert.Equal(t, toolsconfig.ToolConfig{
assert.Equal(t, toolsconfig.Config{
IsToIgnore: true,
}, configs.ToolsConfig[tools.GoSec])
assert.Equal(t, "docker.io/company/go:latest", configs.CustomImages["go"])
Expand Down
98 changes: 62 additions & 36 deletions internal/entities/toolsconfig/tools_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,40 +22,45 @@ import (
"github.com/ZupIT/horusec/internal/helpers/messages"
)

type MapToolConfig map[tools.Tool]ToolConfig
// ToolsConfig is a map of a tool to config for easily index access.
type ToolsConfig map[tools.Tool]Config

type ToolConfig struct {
// Config represents the configuration options for all tools.
type Config struct {
IsToIgnore bool `json:"istoignore"`
}

type ToolsConfigsStruct struct {
Bandit ToolConfig `json:"bandit"`
BundlerAudit ToolConfig `json:"bundleraudit"`
Brakeman ToolConfig `json:"brakeman"`
Checkov ToolConfig `json:"checkov"`
Flawfinder ToolConfig `json:"flawfinder"`
GitLeaks ToolConfig `json:"gitleaks"`
GoSec ToolConfig `json:"gosec"`
HorusecEngine ToolConfig `json:"horusecengine"`
MixAudit ToolConfig `json:"mixaudit"`
NpmAudit ToolConfig `json:"npmaudit"`
PhpCS ToolConfig `json:"phpcs"`
Safety ToolConfig `json:"safety"`
SecurityCodeScan ToolConfig `json:"securitycodescan"`
Semgrep ToolConfig `json:"semgrep"`
ShellCheck ToolConfig `json:"shellcheck"`
Sobelow ToolConfig `json:"sobelow"`
TfSec ToolConfig `json:"tfsec"`
YarnAudit ToolConfig `json:"yarnaudit"`
OwaspDependencyCheck ToolConfig `json:"owaspDependencyCheck"`
DotnetCli ToolConfig `json:"dotnetCli"`
Nancy ToolConfig `json:"nancy"`
Trivy ToolConfig `json:"trivy"`
// toolsConfig represents the schema of configuration tools.
type toolsConfig struct {
Bandit Config `json:"bandit"`
BundlerAudit Config `json:"bundleraudit"`
Brakeman Config `json:"brakeman"`
Checkov Config `json:"checkov"`
Flawfinder Config `json:"flawfinder"`
GitLeaks Config `json:"gitleaks"`
GoSec Config `json:"gosec"`
HorusecEngine Config `json:"horusecengine"`
MixAudit Config `json:"mixaudit"`
NpmAudit Config `json:"npmaudit"`
PhpCS Config `json:"phpcs"`
Safety Config `json:"safety"`
SecurityCodeScan Config `json:"securitycodescan"`
Semgrep Config `json:"semgrep"`
ShellCheck Config `json:"shellcheck"`
Sobelow Config `json:"sobelow"`
TfSec Config `json:"tfsec"`
YarnAudit Config `json:"yarnaudit"`
OwaspDependencyCheck Config `json:"owaspDependencyCheck"`
DotnetCli Config `json:"dotnetCli"`
Nancy Config `json:"nancy"`
Trivy Config `json:"trivy"`
}

// nolint:funlen // toMap is necessary more 15 lines
func (t *ToolsConfigsStruct) ToMap() MapToolConfig {
return MapToolConfig{
// toMap return the tools configuration as ToolsConfig for easily access.
//
// nolint:funlen
func (t *toolsConfig) toMap() ToolsConfig {
return ToolsConfig{
tools.Bandit: t.Bandit,
tools.BundlerAudit: t.BundlerAudit,
tools.Brakeman: t.Brakeman,
Expand All @@ -81,17 +86,38 @@ func (t *ToolsConfigsStruct) ToMap() MapToolConfig {
}
}

func ParseInterfaceToMapToolsConfig(input interface{}) (output MapToolConfig) {
outputStruct := ToolsConfigsStruct{}
bytes, err := json.Marshal(input)
// Default return the default configuration of tools.
//
// The default configuration is enabled for all tools.
func Default() ToolsConfig {
return (&toolsConfig{}).toMap()
}

// MustParseToolsConfig parse a input to ToolsConfig.
//
// If some error occur the default values will be returned and the error
// will be logged.
func MustParseToolsConfig(input map[string]interface{}) ToolsConfig {
cfg, err := parseToolsConfig(input)
if err != nil {
logger.LogErrorWithLevel(messages.MsgErrorParseStringToToolsConfig, err)
return outputStruct.ToMap()
return Default()
}
err = json.Unmarshal(bytes, &outputStruct)
return cfg
}

// parseToolsConfig parse input to ToolsConfig.
func parseToolsConfig(input map[string]interface{}) (ToolsConfig, error) {
var config toolsConfig

bytes, err := json.Marshal(input)
if err != nil {
logger.LogErrorWithLevel(messages.MsgErrorParseStringToToolsConfig, err)
return outputStruct.ToMap()
return nil, err
}
return outputStruct.ToMap()

if err := json.Unmarshal(bytes, &config); err != nil {
return nil, err
}

return config.toMap(), nil
}
141 changes: 141 additions & 0 deletions internal/entities/toolsconfig/tools_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright 2021 ZUP IT SERVICOS EM TECNOLOGIA E INOVACAO SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package toolsconfig

import (
"bytes"
"testing"

"github.com/ZupIT/horusec-devkit/pkg/enums/tools"
"github.com/ZupIT/horusec-devkit/pkg/utils/logger"
"github.com/ZupIT/horusec/internal/helpers/messages"
"github.com/stretchr/testify/assert"
)

func TestDefaultValues(t *testing.T) {
cfg := Default()

tools := tools.Values()

assert.Len(t, cfg, len(tools), "Expected all tools on default values")

for tool, cfg := range cfg {
assert.Contains(t, tools, tool, "Tool %s is invalid", tool)
assert.False(t, cfg.IsToIgnore, "Expected default value as false to IsToIgnore")
}
}

func TestParseToolsConfig(t *testing.T) {
testcases := []struct {
name string
input map[string]interface{}
expected ToolsConfig
output string
}{
{
name: "Should parse values incomplete correctly and return all tools",
input: map[string]interface{}{
"bandit": map[string]bool{
"istoignore": false,
},
"gosec": map[string]bool{
"istoignore": true,
},
},
expected: ToolsConfig{
tools.Bandit: Config{false},
tools.BundlerAudit: Config{false},
tools.Brakeman: Config{false},
tools.Checkov: Config{false},
tools.Flawfinder: Config{false},
tools.GitLeaks: Config{false},
tools.GoSec: Config{true},
tools.HorusecEngine: Config{false},
tools.MixAudit: Config{false},
tools.NpmAudit: Config{false},
tools.PhpCS: Config{false},
tools.Safety: Config{false},
tools.SecurityCodeScan: Config{false},
tools.Semgrep: Config{false},
tools.ShellCheck: Config{false},
tools.Sobelow: Config{false},
tools.TfSec: Config{false},
tools.YarnAudit: Config{false},
tools.OwaspDependencyCheck: Config{false},
tools.DotnetCli: Config{false},
tools.Nancy: Config{false},
tools.Trivy: Config{false},
},
},
{
name: "Should error on invalid configuration and use default values",
input: map[string]interface{}{
"gosec": map[string]string{
"istoigore": "invalid data type",
},
"bandit": "invalid type",
},
expected: Default(),
output: messages.MsgErrorParseStringToToolsConfig,
},
{
name: "Should parse using lower and upper case",
input: map[string]interface{}{
"trivy": map[string]bool{
"istoignore": true,
},
"HorusecEngine": map[string]bool{
"istoignore": true,
},
},
expected: ToolsConfig{
tools.Bandit: Config{false},
tools.BundlerAudit: Config{false},
tools.Brakeman: Config{false},
tools.Checkov: Config{false},
tools.Flawfinder: Config{false},
tools.GitLeaks: Config{false},
tools.GoSec: Config{false},
tools.HorusecEngine: Config{true},
tools.MixAudit: Config{false},
tools.NpmAudit: Config{false},
tools.PhpCS: Config{false},
tools.Safety: Config{false},
tools.SecurityCodeScan: Config{false},
tools.Semgrep: Config{false},
tools.ShellCheck: Config{false},
tools.Sobelow: Config{false},
tools.TfSec: Config{false},
tools.YarnAudit: Config{false},
tools.OwaspDependencyCheck: Config{false},
tools.DotnetCli: Config{false},
tools.Nancy: Config{false},
tools.Trivy: Config{true},
},
},
}

output := bytes.NewBufferString("")
logger.LogSetOutput(output)

for _, tt := range testcases {
t.Run(tt.name, func(t *testing.T) {
config := MustParseToolsConfig(tt.input)

assert.Equal(t, tt.expected, config)
assert.Contains(t, output.String(), tt.output)
})
}
}
7 changes: 4 additions & 3 deletions internal/services/formatters/c/flawfinder/formatter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"errors"
"testing"

"github.com/ZupIT/horusec-devkit/pkg/enums/tools"
"github.com/ZupIT/horusec/internal/entities/toolsconfig"

entitiesAnalysis "github.com/ZupIT/horusec-devkit/pkg/entities/analysis"
Expand Down Expand Up @@ -114,11 +115,11 @@ func TestStartCFlawfinder(t *testing.T) {
dockerAPIControllerMock := &docker.Mock{}
config := &cliConfig.Config{}
config.WorkDir = &workdir.WorkDir{}
config.ToolsConfig = toolsconfig.ParseInterfaceToMapToolsConfig(toolsconfig.ToolsConfigsStruct{
Flawfinder: toolsconfig.ToolConfig{
config.ToolsConfig = toolsconfig.ToolsConfig{
tools.Flawfinder: toolsconfig.Config{
IsToIgnore: true,
},
})
}

service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config)
formatter := NewFormatter(service)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/stretchr/testify/assert"

analysisEntities "github.com/ZupIT/horusec-devkit/pkg/entities/analysis"
"github.com/ZupIT/horusec-devkit/pkg/enums/tools"

cliConfig "github.com/ZupIT/horusec/config"
"github.com/ZupIT/horusec/internal/entities/toolsconfig"
Expand Down Expand Up @@ -115,9 +116,11 @@ func TestParseOutput(t *testing.T) {
dockerAPIControllerMock := &docker.Mock{}
config := &cliConfig.Config{}
config.WorkDir = &workdir.WorkDir{}
config.ToolsConfig = toolsconfig.ParseInterfaceToMapToolsConfig(
toolsconfig.ToolsConfigsStruct{DotnetCli: toolsconfig.ToolConfig{IsToIgnore: true}},
)
config.ToolsConfig = toolsconfig.ToolsConfig{
tools.DotnetCli: toolsconfig.Config{
IsToIgnore: true,
},
}

service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config)
formatter := NewFormatter(service)
Expand Down
9 changes: 6 additions & 3 deletions internal/services/formatters/csharp/scs/formatter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/stretchr/testify/require"

analysisEntities "github.com/ZupIT/horusec-devkit/pkg/entities/analysis"
"github.com/ZupIT/horusec-devkit/pkg/enums/tools"

cliConfig "github.com/ZupIT/horusec/config"
"github.com/ZupIT/horusec/internal/entities/toolsconfig"
Expand Down Expand Up @@ -190,9 +191,11 @@ func TestParseOutput(t *testing.T) {
dockerAPIControllerMock := &docker.Mock{}
config := &cliConfig.Config{}
config.WorkDir = &workdir.WorkDir{}
config.ToolsConfig = toolsconfig.ParseInterfaceToMapToolsConfig(
toolsconfig.ToolsConfigsStruct{SecurityCodeScan: toolsconfig.ToolConfig{IsToIgnore: true}},
)
config.ToolsConfig = toolsconfig.ToolsConfig{
tools.SecurityCodeScan: toolsconfig.Config{
IsToIgnore: true,
},
}

service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config)
formatter := NewFormatter(service)
Expand Down
Loading

0 comments on commit 9bb7c0d

Please sign in to comment.