diff --git a/.gitignore b/.gitignore index 51d299351..9870b3d4d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ deployments/certs horusec-analysis-*.json cmd/horusec/start/examples/ vendor +obj/ diff --git a/.semver.yaml b/.semver.yaml index 0a08d69e9..4dd4d8cd3 100644 --- a/.semver.yaml +++ b/.semver.yaml @@ -1,4 +1,4 @@ alpha: 0 beta: 0 rc: 0 -release: v2.1.0 +release: v2.2.0 diff --git a/Makefile b/Makefile index e2c8914b9..efb0cb2e5 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ coverage: test: $(GO) clean -testcache - $(GO) test -v $(GO_LIST_TO_TEST) -timeout=5m -parallel=1 -failfast -short + $(GO) test -v $(GO_LIST_TO_TEST) -race -timeout=5m -parallel=1 -failfast -short test-e2e: $(GO) clean -testcache diff --git a/cmd/app/generate/generate.go b/cmd/app/generate/generate.go index bc7528a26..2ea80f6c1 100644 --- a/cmd/app/generate/generate.go +++ b/cmd/app/generate/generate.go @@ -66,6 +66,7 @@ func (g *Generate) createAndWriteOnFile() error { return g.writeConfigOnFile(outputFile) } +//nolint:gomnd // magic number func (g *Generate) createAndOpenFile() (outputFile *os.File, err error) { if _, err = os.Create(g.configs.GetConfigFilePath()); err != nil { return nil, err @@ -90,6 +91,7 @@ func (g *Generate) writeConfigOnFile(outputFile *os.File) error { return nil } +//nolint:gomnd // magic number func (g *Generate) readFileAndCreateNewKeys() error { configFile, err := os.OpenFile(g.configs.GetConfigFilePath(), os.O_CREATE|os.O_WRONLY, 0600) if err != nil { diff --git a/cmd/app/start/start.go b/cmd/app/start/start.go index 2ccd9d7b1..794fbd198 100644 --- a/cmd/app/start/start.go +++ b/cmd/app/start/start.go @@ -117,7 +117,8 @@ func (s *Start) CreateStartCommand() *cobra.Command { BoolP("information-severity", "I", s.configs.GetEnableInformationSeverity(), "Used to enable or disable information severity vulnerabilities, information vulnerabilities can contain a lot of false positives. Example: -I=\"true\"") _ = startCmd.PersistentFlags(). StringSliceP("show-vulnerabilities-types", "", s.configs.GetShowVulnerabilitiesTypes(), "Used to show in the output vulnerabilities of types: Vulnerability, Risk Accepted, False Positive, Corrected. Example --show-vulnerabilities-types=\"Vulnerability, Risk Accepted\"") - + _ = startCmd.PersistentFlags(). + BoolP("enable-owasp-dependency-check", "w", s.configs.GetEnableOwaspDependencyCheck(), "Enable owasp dependency check. Example -w=\"true\". Default: false") if !dist.IsStandAlone() { _ = startCmd.PersistentFlags(). BoolP("disable-docker", "D", s.configs.GetDisableDocker(), "Used to run horusec without docker if enabled it will only run the following tools: horusec-csharp, horusec-kotlin, horusec-java, horusec-kubernetes, horusec-leaks, horusec-nodejs, horusec-dart, horusec-nginx. Example: -D=\"true\"") diff --git a/config/.example-horusec-cli.json b/config/.example-horusec-cli.json index 4b1fa634b..1e42de352 100644 --- a/config/.example-horusec-cli.json +++ b/config/.example-horusec-cli.json @@ -18,6 +18,7 @@ "horusecCliWorkDir": {}, "horusecCliRepositoryName": "horus", "horusecCliDisableDocker": true, + "horusecCLiEnableOwaspDependencyCheck": true, "horusecCliCustomRulesPath": "test", "horusecCliFalsePositiveHashes": [ "hash1", diff --git a/config/config.go b/config/config.go index c69754730..e0dcea82f 100644 --- a/config/config.go +++ b/config/config.go @@ -21,17 +21,16 @@ import ( "path/filepath" "strings" - enumsVulnerability "github.com/ZupIT/horusec-devkit/pkg/enums/vulnerability" - - "github.com/sirupsen/logrus" - "github.com/google/uuid" "github.com/iancoleman/strcase" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" + enumsVulnerability "github.com/ZupIT/horusec-devkit/pkg/enums/vulnerability" "github.com/ZupIT/horusec-devkit/pkg/utils/env" "github.com/ZupIT/horusec-devkit/pkg/utils/logger" + "github.com/ZupIT/horusec/config/dist" customImages "github.com/ZupIT/horusec/internal/entities/custom_images" "github.com/ZupIT/horusec/internal/entities/toolsconfig" @@ -86,6 +85,8 @@ func (c *Config) NewConfigsFromCobraAndLoadsCmdStartFlags(cmd *cobra.Command) IC c.SetCustomRulesPath(c.extractFlagValueString(cmd, "custom-rules-path", c.GetCustomRulesPath())) c.SetEnableInformationSeverity(c.extractFlagValueBool(cmd, "information-severity", c.GetEnableInformationSeverity())) c.SetShowVulnerabilitiesTypes(c.extractFlagValueStringSlice(cmd, "show-vulnerabilities-types", c.GetShowVulnerabilitiesTypes())) + c.SetEnableOwaspDependencyCheck(c.extractFlagValueBool(cmd, "enable-owasp-dependency-check", c.GetDisableDocker())) + return c } @@ -121,6 +122,7 @@ func (c *Config) NewConfigsFromViper() IConfig { c.SetEnableInformationSeverity(viper.GetBool(c.toLowerCamel(EnvEnableInformationSeverity))) c.SetCustomImages(viper.Get(c.toLowerCamel(EnvCustomImages))) c.SetShowVulnerabilitiesTypes(viper.GetStringSlice(c.toLowerCamel(EnvShowVulnerabilitiesTypes))) + c.SetEnableOwaspDependencyCheck(viper.GetBool(c.toLowerCamel(EnvEnableOwaspDependencyCheck))) return c } @@ -150,6 +152,7 @@ func (c *Config) NewConfigsFromEnvironments() IConfig { c.SetCustomRulesPath(env.GetEnvOrDefault(EnvCustomRulesPath, c.customRulesPath)) c.SetEnableInformationSeverity(env.GetEnvOrDefaultBool(EnvEnableInformationSeverity, c.enableInformationSeverity)) c.SetShowVulnerabilitiesTypes(c.factoryParseInputToSliceString(env.GetEnvOrDefaultInterface(EnvShowVulnerabilitiesTypes, c.showVulnerabilitiesTypes))) + c.SetEnableOwaspDependencyCheck(env.GetEnvOrDefaultBool(EnvEnableOwaspDependencyCheck, c.enableOwaspDependencyCheck)) return c } @@ -474,6 +477,7 @@ func (c *Config) toMap() map[string]interface{} { "enableInformationSeverity": c.enableInformationSeverity, "customImages": c.customImages, "showVulnerabilitiesTypes": c.showVulnerabilitiesTypes, + "enableOwaspDependencyCheck": c.enableOwaspDependencyCheck, } } @@ -517,6 +521,7 @@ func (c *Config) ToMapLowerCase() map[string]interface{} { c.toLowerCamel(EnvEnableInformationSeverity): c.GetEnableInformationSeverity(), c.toLowerCamel(EnvCustomImages): c.GetCustomImages(), c.toLowerCamel(EnvShowVulnerabilitiesTypes): c.GetShowVulnerabilitiesTypes(), + c.toLowerCamel(EnvEnableOwaspDependencyCheck): c.GetEnableOwaspDependencyCheck(), } } @@ -618,3 +623,11 @@ func (c *Config) SetCustomImages(configData interface{}) { c.customImages = customImg } + +func (c *Config) GetEnableOwaspDependencyCheck() bool { + return c.enableOwaspDependencyCheck +} + +func (c *Config) SetEnableOwaspDependencyCheck(enableOwaspDependencyCheck bool) { + c.enableOwaspDependencyCheck = enableOwaspDependencyCheck +} diff --git a/config/config_test.go b/config/config_test.go index e2ac6faa2..1f5b2d309 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -69,6 +69,7 @@ func TestNewHorusecConfig(t *testing.T) { assert.Equal(t, false, configs.GetEnableInformationSeverity()) assert.Equal(t, 0, len(configs.GetCustomImages())) assert.Equal(t, 1, len(configs.GetShowVulnerabilitiesTypes())) + assert.Equal(t, false, configs.GetEnableOwaspDependencyCheck()) }) t.Run("Should change horusec config and return your new values", func(t *testing.T) { currentPath, _ := os.Getwd() @@ -102,6 +103,7 @@ func TestNewHorusecConfig(t *testing.T) { configs.SetEnableInformationSeverity(true) configs.SetCustomImages(map[languages.Language]string{languages.Go: "test/test"}) configs.SetShowVulnerabilitiesTypes([]string{vulnerability.Vulnerability.ToString()}) + configs.SetEnableOwaspDependencyCheck(true) assert.NotEqual(t, configs.GetDefaultConfigFilePath(), configs.GetConfigFilePath()) assert.NotEqual(t, "http://0.0.0.0:8000", configs.GetHorusecAPIUri()) @@ -132,6 +134,7 @@ func TestNewHorusecConfig(t *testing.T) { assert.Equal(t, true, configs.GetEnableInformationSeverity()) assert.Equal(t, []string{vulnerability.Vulnerability.ToString()}, configs.GetShowVulnerabilitiesTypes()) assert.NotEqual(t, map[languages.Language]string{}, configs.GetCustomImages()) + assert.Equal(t, true, configs.GetEnableOwaspDependencyCheck()) }) t.Run("Should return horusec config using new viper file", func(t *testing.T) { viper.Reset() @@ -166,6 +169,7 @@ func TestNewHorusecConfig(t *testing.T) { assert.Equal(t, true, configs.GetDisableDocker()) assert.Equal(t, "test", configs.GetCustomRulesPath()) assert.Equal(t, true, configs.GetEnableInformationSeverity()) + assert.Equal(t, true, configs.GetEnableOwaspDependencyCheck()) assert.Equal(t, []string{vulnerability.Vulnerability.ToString(), vulnerability.FalsePositive.ToString()}, configs.GetShowVulnerabilitiesTypes()) assert.Equal(t, toolsconfig.ToolConfig{ IsToIgnore: true, @@ -205,6 +209,7 @@ func TestNewHorusecConfig(t *testing.T) { assert.Equal(t, map[string]string{"x-headers": "some-other-value"}, configs.GetHeaders()) assert.Equal(t, "test", configs.GetContainerBindProjectPath()) assert.Equal(t, true, configs.GetEnableInformationSeverity()) + assert.Equal(t, true, configs.GetEnableOwaspDependencyCheck()) assert.Equal(t, toolsconfig.ToolConfig{ IsToIgnore: true, }, configs.GetToolsConfig()[tools.GoSec]) @@ -231,6 +236,7 @@ func TestNewHorusecConfig(t *testing.T) { assert.NoError(t, os.Setenv(EnvHeaders, "{\"x-auth\": \"987654321\"}")) assert.NoError(t, os.Setenv(EnvContainerBindProjectPath, "./my-path")) assert.NoError(t, os.Setenv(EnvDisableDocker, "true")) + assert.NoError(t, os.Setenv(EnvEnableOwaspDependencyCheck, "true")) assert.NoError(t, os.Setenv(EnvCustomRulesPath, "test")) assert.NoError(t, os.Setenv(EnvEnableInformationSeverity, "true")) assert.NoError(t, os.Setenv(EnvShowVulnerabilitiesTypes, fmt.Sprintf("%s, %s", vulnerability.Vulnerability.ToString(), vulnerability.RiskAccepted.ToString()))) @@ -260,6 +266,7 @@ func TestNewHorusecConfig(t *testing.T) { assert.Equal(t, true, configs.GetDisableDocker()) assert.Equal(t, "test", configs.GetCustomRulesPath()) assert.Equal(t, true, configs.GetEnableInformationSeverity()) + assert.Equal(t, true, configs.GetEnableOwaspDependencyCheck()) assert.Equal(t, []string{vulnerability.Vulnerability.ToString(), vulnerability.RiskAccepted.ToString()}, configs.GetShowVulnerabilitiesTypes()) }) t.Run("Should return horusec config using viper file and override by environment and override by flags", func(t *testing.T) { @@ -296,6 +303,7 @@ func TestNewHorusecConfig(t *testing.T) { assert.Equal(t, map[string]string{"x-headers": "some-other-value"}, configs.GetHeaders()) assert.Equal(t, "test", configs.GetContainerBindProjectPath()) assert.Equal(t, true, configs.GetEnableInformationSeverity()) + assert.Equal(t, true, configs.GetEnableOwaspDependencyCheck()) assert.Equal(t, toolsconfig.ToolConfig{ IsToIgnore: true, }, configs.GetToolsConfig()[tools.GoSec]) @@ -324,6 +332,7 @@ func TestNewHorusecConfig(t *testing.T) { assert.NoError(t, os.Setenv(EnvDisableDocker, "true")) assert.NoError(t, os.Setenv(EnvCustomRulesPath, "test")) assert.NoError(t, os.Setenv(EnvEnableInformationSeverity, "true")) + assert.NoError(t, os.Setenv(EnvEnableOwaspDependencyCheck, "true")) assert.NoError(t, os.Setenv(EnvShowVulnerabilitiesTypes, fmt.Sprintf("%s, %s", vulnerability.Vulnerability.ToString(), vulnerability.RiskAccepted.ToString()))) configs.NewConfigsFromEnvironments() assert.Equal(t, configFilePath, configs.GetConfigFilePath()) @@ -352,6 +361,7 @@ func TestNewHorusecConfig(t *testing.T) { assert.Equal(t, true, configs.GetDisableDocker()) assert.Equal(t, "test", configs.GetCustomRulesPath()) assert.Equal(t, true, configs.GetEnableInformationSeverity()) + assert.Equal(t, true, configs.GetEnableOwaspDependencyCheck()) cobraCmd := &cobra.Command{ Use: "start", Short: "Start horusec-cli", @@ -416,6 +426,7 @@ func TestToLowerCamel(t *testing.T) { assert.Equal(t, "horusecCliWorkDir", configs.toLowerCamel(EnvWorkDir)) assert.Equal(t, "horusecCliCustomImages", configs.toLowerCamel(EnvCustomImages)) assert.Equal(t, "horusecCliShowVulnerabilitiesTypes", configs.toLowerCamel(EnvShowVulnerabilitiesTypes)) + assert.Equal(t, "horusecCliEnableOwaspDependencyCheck", configs.toLowerCamel(EnvEnableOwaspDependencyCheck)) }) } diff --git a/config/entity.go b/config/entity.go index 9896fbf71..7923d6e8a 100644 --- a/config/entity.go +++ b/config/entity.go @@ -48,6 +48,7 @@ const ( EnvEnableInformationSeverity = "HORUSEC_CLI_ENABLE_INFORMATION_SEVERITY" EnvCustomImages = "HORUSEC_CLI_CUSTOM_IMAGES" EnvShowVulnerabilitiesTypes = "HORUSEC_CLI_SHOW_VULNERABILITIES_TYPES" + EnvEnableOwaspDependencyCheck = "HORUSEC_CLI_ENABLE_OWASP_DEPENDENCY_CHECK" ) type Config struct { @@ -77,6 +78,7 @@ type Config struct { enableCommitAuthor bool disableDocker bool enableInformationSeverity bool + enableOwaspDependencyCheck bool severitiesToIgnore []string filesOrPathsToIgnore []string falsePositiveHashes []string diff --git a/config/interface.go b/config/interface.go index 921e4b4d1..a34a5130b 100644 --- a/config/interface.go +++ b/config/interface.go @@ -111,4 +111,7 @@ type IConfig interface { SetShowVulnerabilitiesTypes(vulnerabilitiesTypes []string) GetShowVulnerabilitiesTypes() []string + + GetEnableOwaspDependencyCheck() bool + SetEnableOwaspDependencyCheck(enableOwaspDependencyCheck bool) } diff --git a/deployments/all-version-cli.txt b/deployments/all-version-cli.txt index 594cfc8fb..f087f9952 100644 --- a/deployments/all-version-cli.txt +++ b/deployments/all-version-cli.txt @@ -3,3 +3,4 @@ v2-0-0 v2-0-1 v2-0-2 v2-1-0 +v2-2-0 diff --git a/deployments/version-cli-latest.txt b/deployments/version-cli-latest.txt index 1e5021ca9..883071001 100644 --- a/deployments/version-cli-latest.txt +++ b/deployments/version-cli-latest.txt @@ -1 +1 @@ -v2-1-0 +v2-2-0 diff --git a/examples/csharp/example1/NetCoreVulnerabilities/NetCoreVulnerabilities.csproj b/examples/csharp/example1/NetCoreVulnerabilities/NetCoreVulnerabilities.csproj index 4641ecf25..d7e858075 100644 --- a/examples/csharp/example1/NetCoreVulnerabilities/NetCoreVulnerabilities.csproj +++ b/examples/csharp/example1/NetCoreVulnerabilities/NetCoreVulnerabilities.csproj @@ -1,14 +1,16 @@ - - - - Exe - netcoreapp3.1 - - - - - - - - - + + + Exe + netcoreapp3.1 + + + + + + + + + + + + \ No newline at end of file diff --git a/go.mod b/go.mod index b8cfc6d1d..e55611fa2 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.16 require ( github.com/Microsoft/go-winio v0.5.0 // indirect - github.com/ZupIT/horusec-devkit v1.0.4 + github.com/ZupIT/horusec-devkit v1.0.7 github.com/ZupIT/horusec-engine v0.3.3-0.20210428113330-765b5cfcf9b1 github.com/bmatcuk/doublestar/v2 v2.0.4 github.com/containerd/containerd v1.5.1 // indirect diff --git a/go.sum b/go.sum index ab2438f97..b71080b58 100644 --- a/go.sum +++ b/go.sum @@ -74,8 +74,16 @@ github.com/ZupIT/horusec-devkit v1.0.2 h1:AItfaHVYgVXaaCjHcBgKkT4FrQ/XEYv7jnBiOi github.com/ZupIT/horusec-devkit v1.0.2/go.mod h1:0mlKsix5/t+kFlVukmOS65xpJymiYAv8bmJj5eiykZU= github.com/ZupIT/horusec-devkit v1.0.3-0.20210601122747-70b80bd3b85a h1:OwqdhE65HJ3ofnVEEYbeatPcQyrRfIZTXiWqVkxjtA4= github.com/ZupIT/horusec-devkit v1.0.3-0.20210601122747-70b80bd3b85a/go.mod h1:0mlKsix5/t+kFlVukmOS65xpJymiYAv8bmJj5eiykZU= +github.com/ZupIT/horusec-devkit v1.0.4-0.20210610121339-a6e36de9097f h1:1ipUc0jwX5UOg6tgZZMsrg9B018pjh9ehtaNvaTJQPE= +github.com/ZupIT/horusec-devkit v1.0.4-0.20210610121339-a6e36de9097f/go.mod h1:0mlKsix5/t+kFlVukmOS65xpJymiYAv8bmJj5eiykZU= +github.com/ZupIT/horusec-devkit v1.0.4-0.20210614190314-6b82fbba5da2 h1:+MJ8AtdkeGKfkm+3aQOS+SYZykiwqrW6s+VOomQdXbE= +github.com/ZupIT/horusec-devkit v1.0.4-0.20210614190314-6b82fbba5da2/go.mod h1:0mlKsix5/t+kFlVukmOS65xpJymiYAv8bmJj5eiykZU= github.com/ZupIT/horusec-devkit v1.0.4 h1:SpgBiqp4AZOuMHqanwmV/Q/Ib2tAKQlfeBS1jH+mgpc= github.com/ZupIT/horusec-devkit v1.0.4/go.mod h1:0mlKsix5/t+kFlVukmOS65xpJymiYAv8bmJj5eiykZU= +github.com/ZupIT/horusec-devkit v1.0.5 h1:ElaAFRZUebqzSCF2jHLc21TeBYUCI/8Z+MIt4Rwq9uo= +github.com/ZupIT/horusec-devkit v1.0.5/go.mod h1:0mlKsix5/t+kFlVukmOS65xpJymiYAv8bmJj5eiykZU= +github.com/ZupIT/horusec-devkit v1.0.7 h1:o9EUpIpoYtcjEwJJtYVRQ+z4Gf0LBzJImnbsNfQyzU8= +github.com/ZupIT/horusec-devkit v1.0.7/go.mod h1:0mlKsix5/t+kFlVukmOS65xpJymiYAv8bmJj5eiykZU= github.com/ZupIT/horusec-engine v0.3.3-0.20210428113330-765b5cfcf9b1 h1:6kCZzpEKjNeNsQzoD7Qx93KhuYLF6U4SUJThC2YLRsQ= github.com/ZupIT/horusec-engine v0.3.3-0.20210428113330-765b5cfcf9b1/go.mod h1:J1pXu3E+r5BGW+5tcprMVWJ8JFIcFbAiGWSgqYCavJo= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= diff --git a/horusec-config.json b/horusec-config.json index 312f45a22..f97034ee0 100644 --- a/horusec-config.json +++ b/horusec-config.json @@ -22,15 +22,16 @@ "horusecCliEnableGitHistoryAnalysis": false, "horusecCliEnableInformationSeverity": false, "horusecCliFalsePositiveHashes": [ - "e09bfb33f98e86cdcf547f72c7be3e99474de1b2ec8d6ded6ad7f6caaa78cd7c", - "5f5397c5a451b73e73be34c7a0330ec26f00fe41df15cdb317f87df3211aa563", - "bfd91103f38d189f60a7080b9bae6aad940e6b7637b4218017a80c4b4c15385a", + "37fa0cfe47519c1b2b6a8e29538571b81fd8787ca4217825ae6d8dcf86d70de8", + "85492fbc829b64336a4f858022fbe52f05e27ee18d7a8fbdf5ffd23991ebd7a9", "06f6ce2402e20f1e885e5d59f66db4dde44dfdd2eaf821d86b1d066a707c9fff", - "00bf1f0dbed9d82a929b8353f093f26c48e49c3db1a5e23bfb6f6eafba808b2c", + "362a89c4517db256b648e9b1d21ddb0d99018e7c7b9f9b45d200ede54a49363d", + "5fc8f08b377cdc0c92913da73a2d8d8acd85896993e04ae4c15e34ecb829d8b5", + "3e64eb0ec371e5ef7d97adec60d3b94cb7dd5a1189951f2a45ed1827e6781d30", + "9c205ee4b31bea1254f4e8031958995912312a524105469cb49e757d59558496", "b176f4967e7b0e54faabb9688d1d9ff6f10959d4a34280b9e035bfd63c4f352e", - "316176f18dac308bbcfc3ece628796eb438c8387a7d0da83f583fcacab3a01c4", - "1dbef4655a4a2378e67acf89bf9b78c13041634a63a8ef0a84ff5e6237d17216", - "37fa0cfe47519c1b2b6a8e29538571b81fd8787ca4217825ae6d8dcf86d70de8" + "2eab7620998c54bcbdb1da9ad96f54c3b6ac7b5e0babbff8f502ec10594479ad", + "85d4e95cd519dda872c8da0bc50b742ef067bb9f1e5b9fea42924eb21c5e688e" ], "horusecCliFilesOrPathsToIgnore": [ "**/e2e/**", diff --git a/internal/controllers/analyzer/analyzer.go b/internal/controllers/analyzer/analyzer.go index 811c748de..20e1862e7 100644 --- a/internal/controllers/analyzer/analyzer.go +++ b/internal/controllers/analyzer/analyzer.go @@ -16,31 +16,26 @@ package analyzer import ( "fmt" + "log" "os" "os/signal" - "strconv" "strings" + "sync" "time" - "github.com/ZupIT/horusec-devkit/pkg/enums/confidence" - - "github.com/ZupIT/horusec/internal/services/formatters/nginx/horusecnginx" - "github.com/ZupIT/horusec/internal/services/formatters/swift/horusecswift" + "github.com/google/uuid" "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" enumsAnalysis "github.com/ZupIT/horusec-devkit/pkg/enums/analysis" + "github.com/ZupIT/horusec-devkit/pkg/enums/confidence" + "github.com/ZupIT/horusec-devkit/pkg/enums/languages" "github.com/ZupIT/horusec-devkit/pkg/enums/severities" enumsVulnerability "github.com/ZupIT/horusec-devkit/pkg/enums/vulnerability" - "github.com/ZupIT/horusec/internal/entities/monitor" - "github.com/ZupIT/horusec/internal/utils/file" - - "github.com/google/uuid" - - "github.com/ZupIT/horusec-devkit/pkg/enums/languages" "github.com/ZupIT/horusec-devkit/pkg/utils/logger" - cliConfig "github.com/ZupIT/horusec/config" - languageDetect "github.com/ZupIT/horusec/internal/controllers/language_detect" + + "github.com/ZupIT/horusec/config" + languagedetect "github.com/ZupIT/horusec/internal/controllers/language_detect" "github.com/ZupIT/horusec/internal/controllers/printresults" "github.com/ZupIT/horusec/internal/enums/images" "github.com/ZupIT/horusec/internal/helpers/messages" @@ -48,11 +43,13 @@ import ( dockerClient "github.com/ZupIT/horusec/internal/services/docker/client" "github.com/ZupIT/horusec/internal/services/formatters" "github.com/ZupIT/horusec/internal/services/formatters/c/flawfinder" + dotnetcli "github.com/ZupIT/horusec/internal/services/formatters/csharp/dotnet_cli" "github.com/ZupIT/horusec/internal/services/formatters/csharp/horuseccsharp" "github.com/ZupIT/horusec/internal/services/formatters/csharp/scs" "github.com/ZupIT/horusec/internal/services/formatters/dart/horusecdart" "github.com/ZupIT/horusec/internal/services/formatters/elixir/mixaudit" "github.com/ZupIT/horusec/internal/services/formatters/elixir/sobelow" + dependencycheck "github.com/ZupIT/horusec/internal/services/formatters/generic/dependency_check" "github.com/ZupIT/horusec/internal/services/formatters/generic/semgrep" "github.com/ZupIT/horusec/internal/services/formatters/go/gosec" "github.com/ZupIT/horusec/internal/services/formatters/hcl" @@ -63,14 +60,17 @@ import ( "github.com/ZupIT/horusec/internal/services/formatters/kotlin/horuseckotlin" "github.com/ZupIT/horusec/internal/services/formatters/leaks/gitleaks" "github.com/ZupIT/horusec/internal/services/formatters/leaks/horusecleaks" + "github.com/ZupIT/horusec/internal/services/formatters/nginx/horusecnginx" "github.com/ZupIT/horusec/internal/services/formatters/php/phpcs" "github.com/ZupIT/horusec/internal/services/formatters/python/bandit" "github.com/ZupIT/horusec/internal/services/formatters/python/safety" "github.com/ZupIT/horusec/internal/services/formatters/ruby/brakeman" "github.com/ZupIT/horusec/internal/services/formatters/ruby/bundler" "github.com/ZupIT/horusec/internal/services/formatters/shell/shellcheck" + "github.com/ZupIT/horusec/internal/services/formatters/swift/horusecswift" "github.com/ZupIT/horusec/internal/services/formatters/yaml/horuseckubernetes" horusecAPI "github.com/ZupIT/horusec/internal/services/horusec_api" + "github.com/ZupIT/horusec/internal/utils/file" ) type Interface interface { @@ -78,31 +78,30 @@ type Interface interface { } type Analyzer struct { - monitor *monitor.Monitor - dockerSDK docker.Interface - analysis *analysis.Analysis - config cliConfig.IConfig - languageDetect languageDetect.Interface - printController printresults.Interface - horusecAPIService horusecAPI.IService - formatterService formatters.IService + docker docker.Interface + analysis *analysis.Analysis + config config.IConfig + languageDetect languagedetect.Interface + printController printresults.Interface + horusec horusecAPI.IService + formatter formatters.IService } -func NewAnalyzer(config cliConfig.IConfig) Interface { +func NewAnalyzer(cfg config.IConfig) Interface { entity := &analysis.Analysis{ ID: uuid.New(), CreatedAt: time.Now(), Status: enumsAnalysis.Running, } - dockerAPI := docker.NewDockerAPI(dockerClient.NewDockerClient(), config, entity.ID) + dockerAPI := docker.NewDockerAPI(dockerClient.NewDockerClient(), cfg, entity.ID) return &Analyzer{ - dockerSDK: dockerAPI, - analysis: entity, - config: config, - languageDetect: languageDetect.NewLanguageDetect(config, entity.ID), - printController: printresults.NewPrintResults(entity, config), - horusecAPIService: horusecAPI.NewHorusecAPIService(config), - formatterService: formatters.NewFormatterService(entity, dockerAPI, config, nil), + docker: dockerAPI, + analysis: entity, + config: cfg, + languageDetect: languagedetect.NewLanguageDetect(cfg, entity.ID), + printController: printresults.NewPrintResults(entity, cfg), + horusec: horusecAPI.NewHorusecAPIService(cfg), + formatter: formatters.NewFormatterService(entity, dockerAPI, cfg), } } @@ -128,7 +127,7 @@ func (a *Analyzer) removeHorusecFolder() { err := os.RemoveAll(a.config.GetProjectPath() + file.ReplacePathSeparator("/.horusec")) logger.LogErrorWithLevel(messages.MsgErrorRemoveAnalysisFolder, err) if !a.config.GetDisableDocker() { - a.dockerSDK.DeleteContainersFromAPI() + a.docker.DeleteContainersFromAPI() } } @@ -137,16 +136,14 @@ func (a *Analyzer) runAnalysis() (totalVulns int, err error) { if err != nil { return 0, err } - - a.setMonitor(monitor.NewMonitor()) a.startDetectVulnerabilities(langs) return a.sendAnalysisAndStartPrintResults() } func (a *Analyzer) sendAnalysisAndStartPrintResults() (int, error) { a.formatAnalysisToSendToAPI() - a.horusecAPIService.SendAnalysis(a.analysis) - analysisSaved := a.horusecAPIService.GetAnalysis(a.analysis.ID) + a.horusec.SendAnalysis(a.analysis) + analysisSaved := a.horusec.GetAnalysis(a.analysis.ID) if analysisSaved != nil && analysisSaved.ID != uuid.Nil { a.analysis = analysisSaved } @@ -176,42 +173,60 @@ func (a *Analyzer) formatAnalysisToSendToAPI() { } } -func (a *Analyzer) setMonitor(monitorToSet *monitor.Monitor) { - a.monitor = monitorToSet - a.formatterService.SetMonitor(monitorToSet) -} - +// nolint:funlen,gocyclo +// NOTE: We ignore the funlen and gocyclo lint here because concurrency code is complicated +// +// startDetectVulnerabilities handle execution of all analysis in parallel func (a *Analyzer) startDetectVulnerabilities(langs []languages.Language) { - for _, language := range langs { - for _, projectSubPath := range a.config.GetWorkDir().GetArrayByLanguage(language) { - a.logProjectSubPath(language, projectSubPath) - langFunc := a.mapDetectVulnerabilityByLanguage()[language] - if langFunc != nil { - go langFunc(projectSubPath) - } - } - } + var wg sync.WaitGroup + done := make(chan struct{}) - a.runMonitorTimeout(a.config.GetTimeoutInSecondsAnalysis()) -} + wd := a.config.GetWorkDir() + funcs := a.mapDetectVulnerabilityByLanguage() -func (a *Analyzer) runMonitorTimeout(monitorNumber int64) { - if monitorNumber <= 0 { - a.dockerSDK.DeleteContainersFromAPI() - a.config.SetIsTimeout(true) - } + go func() { + defer close(done) + for _, language := range langs { + for _, projectSubPath := range wd.GetArrayByLanguage(language) { + a.logProjectSubPath(language, projectSubPath) + + if fn, exist := funcs[language]; exist { + wg.Add(1) + go func() { + defer wg.Done() + if err := fn(&wg, projectSubPath); err != nil { + a.setAnalysisError(err) + } + }() + } + } + } + wg.Wait() + }() - if !a.monitor.IsFinished() && !a.config.GetIsTimeout() { - logger.LogInfoWithLevel( - fmt.Sprintf(messages.MsgInfoMonitorTimeoutIn + strconv.Itoa(int(monitorNumber)) + "s")) - time.Sleep(time.Duration(a.config.GetMonitorRetryInSeconds()) * time.Second) - a.runMonitorTimeout(monitorNumber - a.config.GetMonitorRetryInSeconds()) + timeout := a.config.GetTimeoutInSecondsAnalysis() + timer := time.After(time.Duration(timeout) * time.Second) + retry := a.config.GetMonitorRetryInSeconds() + for { + select { + case <-done: + return + case <-timer: + a.docker.DeleteContainersFromAPI() + a.config.SetIsTimeout(true) + return + default: + msg := fmt.Sprintf("%s%ds", messages.MsgInfoMonitorTimeoutIn, timeout) + logger.LogInfoWithLevel(msg) + time.Sleep(time.Duration(retry) * time.Second) + timeout -= retry + } } } //nolint:funlen // all Languages is greater than 15 -func (a *Analyzer) mapDetectVulnerabilityByLanguage() map[languages.Language]func(string) { - return map[languages.Language]func(string){ +func (a *Analyzer) mapDetectVulnerabilityByLanguage() map[languages.Language]func(*sync.WaitGroup, string) error { + return map[languages.Language]func(*sync.WaitGroup, string) error{ languages.CSharp: a.detectVulnerabilityCsharp, languages.Leaks: a.detectVulnerabilityLeaks, languages.Go: a.detectVulnerabilityGo, @@ -233,200 +248,148 @@ func (a *Analyzer) mapDetectVulnerabilityByLanguage() map[languages.Language]fun } } -func (a *Analyzer) detectVulneravilitySwift(projectSubPath string) { - a.monitor.AddProcess(1) - go horusecswift.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) +func (a *Analyzer) detectVulneravilitySwift(_ *sync.WaitGroup, projectSubPath string) error { + horusecswift.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + return nil } -func (a *Analyzer) detectVulnerabilityCsharp(projectSubPath string) { - const TotalProcess = 2 - a.monitor.AddProcess(TotalProcess) - go horuseccsharp.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) +func (a *Analyzer) detectVulnerabilityCsharp(wg *sync.WaitGroup, projectSubPath string) error { + spawn(wg, horuseccsharp.NewFormatter(a.formatter), projectSubPath) - if err := a.dockerSDK.PullImage(a.getCustomOrDefaultImage(languages.CSharp)); err != nil { - a.setErrorAndRemoveProcess(err, TotalProcess) - return + if err := a.docker.PullImage(a.getCustomOrDefaultImage(languages.CSharp)); err != nil { + return err } - go scs.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) + scs.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + dotnetcli.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + return nil } -func (a *Analyzer) detectVulnerabilityLeaks(projectSubPath string) { - const TotalProcess = 2 - a.monitor.AddProcess(TotalProcess) - go horusecleaks.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) - a.executeGitLeaks(projectSubPath) -} +func (a *Analyzer) detectVulnerabilityLeaks(wg *sync.WaitGroup, projectSubPath string) error { + spawn(wg, horusecleaks.NewFormatter(a.formatter), projectSubPath) -func (a *Analyzer) executeGitLeaks(projectSubPath string) { - const TotalProcess = 1 if a.config.GetEnableGitHistoryAnalysis() { logger.LogWarnWithLevel(messages.MsgWarnGitHistoryEnable) - if err := a.dockerSDK.PullImage(a.getCustomOrDefaultImage(languages.Leaks)); err != nil { - a.setErrorAndRemoveProcess(err, TotalProcess) - return + if err := a.docker.PullImage(a.getCustomOrDefaultImage(languages.Leaks)); err != nil { + return err } - - go gitleaks.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) - } else { - a.monitor.RemoveProcess(TotalProcess) + gitleaks.NewFormatter(a.formatter).StartAnalysis(projectSubPath) } + return nil } -func (a *Analyzer) detectVulnerabilityGo(projectSubPath string) { - const TotalProcess = 1 - a.monitor.AddProcess(TotalProcess) - - if err := a.dockerSDK.PullImage(a.getCustomOrDefaultImage(languages.Go)); err != nil { - a.setErrorAndRemoveProcess(err, TotalProcess) - return +func (a *Analyzer) detectVulnerabilityGo(_ *sync.WaitGroup, projectSubPath string) error { + if err := a.docker.PullImage(a.getCustomOrDefaultImage(languages.Go)); err != nil { + return err } - - go gosec.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) + gosec.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + return nil } -func (a *Analyzer) detectVulnerabilityJava(projectSubPath string) { - const TotalProcess = 1 - a.monitor.AddProcess(TotalProcess) - go horusecjava.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) +func (a *Analyzer) detectVulnerabilityJava(_ *sync.WaitGroup, projectSubPath string) error { + horusecjava.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + return nil } -func (a *Analyzer) detectVulnerabilityKotlin(projectSubPath string) { - const TotalProcess = 1 - a.monitor.AddProcess(TotalProcess) - go horuseckotlin.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) +func (a *Analyzer) detectVulnerabilityKotlin(_ *sync.WaitGroup, projectSubPath string) error { + horuseckotlin.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + return nil } -func (a *Analyzer) detectVulnerabilityNginx(projectSubPath string) { - const TotalProcess = 1 - a.monitor.AddProcess(TotalProcess) - go horusecnginx.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) +func (a *Analyzer) detectVulnerabilityNginx(_ *sync.WaitGroup, projectSubPath string) error { + horusecnginx.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + return nil } -func (a *Analyzer) detectVulnerabilityJavascript(projectSubPath string) { - const TotalProcess = 3 - a.monitor.AddProcess(TotalProcess) - go horusecnodejs.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) +func (a *Analyzer) detectVulnerabilityJavascript(wg *sync.WaitGroup, projectSubPath string) error { + spawn(wg, horusecnodejs.NewFormatter(a.formatter), projectSubPath) - if err := a.dockerSDK.PullImage(a.getCustomOrDefaultImage(languages.Javascript)); err != nil { - a.setErrorAndRemoveProcess(err, TotalProcess) - return + if err := a.docker.PullImage(a.getCustomOrDefaultImage(languages.Javascript)); err != nil { + return err } - - go yarnaudit.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) - go npmaudit.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) + spawn(wg, yarnaudit.NewFormatter(a.formatter), projectSubPath) + npmaudit.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + return nil } -func (a *Analyzer) detectVulnerabilityPython(projectSubPath string) { - const TotalProcess = 2 - a.monitor.AddProcess(TotalProcess) - - if err := a.dockerSDK.PullImage(a.getCustomOrDefaultImage(languages.Python)); err != nil { - a.setErrorAndRemoveProcess(err, TotalProcess) - return +func (a *Analyzer) detectVulnerabilityPython(wg *sync.WaitGroup, projectSubPath string) error { + if err := a.docker.PullImage(a.getCustomOrDefaultImage(languages.Python)); err != nil { + return err } - - go bandit.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) - go safety.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) + spawn(wg, bandit.NewFormatter(a.formatter), projectSubPath) + safety.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + return nil } -func (a *Analyzer) detectVulnerabilityRuby(projectSubPath string) { - const TotalProcess = 2 - a.monitor.AddProcess(TotalProcess) - - if err := a.dockerSDK.PullImage(a.getCustomOrDefaultImage(languages.Ruby)); err != nil { - a.setErrorAndRemoveProcess(err, TotalProcess) - return +func (a *Analyzer) detectVulnerabilityRuby(wg *sync.WaitGroup, projectSubPath string) error { + if err := a.docker.PullImage(a.getCustomOrDefaultImage(languages.Ruby)); err != nil { + return err } - - go brakeman.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) - go bundler.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) + spawn(wg, brakeman.NewFormatter(a.formatter), projectSubPath) + bundler.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + return nil } -func (a *Analyzer) detectVulnerabilityHCL(projectSubPath string) { - const TotalProcess = 1 - a.monitor.AddProcess(TotalProcess) - - if err := a.dockerSDK.PullImage(a.getCustomOrDefaultImage(languages.HCL)); err != nil { - a.setErrorAndRemoveProcess(err, TotalProcess) - return +func (a *Analyzer) detectVulnerabilityHCL(_ *sync.WaitGroup, projectSubPath string) error { + if err := a.docker.PullImage(a.getCustomOrDefaultImage(languages.HCL)); err != nil { + return err } - - go hcl.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) + hcl.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + return nil } -func (a *Analyzer) detectVulnerabilityYaml(projectSubPath string) { - const TotalProcess = 1 - a.monitor.AddProcess(TotalProcess) - go horuseckubernetes.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) +func (a *Analyzer) detectVulnerabilityYaml(_ *sync.WaitGroup, projectSubPath string) error { + horuseckubernetes.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + return nil } -func (a *Analyzer) detectVulnerabilityC(projectSubPath string) { - const TotalProcess = 1 - a.monitor.AddProcess(TotalProcess) - - if err := a.dockerSDK.PullImage(a.getCustomOrDefaultImage(languages.C)); err != nil { - a.setErrorAndRemoveProcess(err, TotalProcess) - return +func (a *Analyzer) detectVulnerabilityC(_ *sync.WaitGroup, projectSubPath string) error { + if err := a.docker.PullImage(a.getCustomOrDefaultImage(languages.C)); err != nil { + return err } - - go flawfinder.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) + flawfinder.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + return nil } -func (a *Analyzer) detectVulnerabilityPHP(projectSubPath string) { - const TotalProcess = 1 - a.monitor.AddProcess(TotalProcess) - - if err := a.dockerSDK.PullImage(a.getCustomOrDefaultImage(languages.PHP)); err != nil { - a.setErrorAndRemoveProcess(err, TotalProcess) - return +func (a *Analyzer) detectVulnerabilityPHP(_ *sync.WaitGroup, projectSubPath string) error { + if err := a.docker.PullImage(a.getCustomOrDefaultImage(languages.PHP)); err != nil { + return err } - - go phpcs.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) + phpcs.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + return nil } -func (a *Analyzer) detectVulnerabilityGeneric(projectSubPath string) { - const TotalProcess = 1 - a.monitor.AddProcess(TotalProcess) - - if err := a.dockerSDK.PullImage(a.getCustomOrDefaultImage(languages.Generic)); err != nil { - a.setErrorAndRemoveProcess(err, TotalProcess) - return +func (a *Analyzer) detectVulnerabilityGeneric(_ *sync.WaitGroup, projectSubPath string) error { + if err := a.docker.PullImage(a.getCustomOrDefaultImage(languages.Generic)); err != nil { + return err } - go semgrep.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) + semgrep.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + dependencycheck.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + return nil } -func (a *Analyzer) detectVulnerabilityDart(projectSubPath string) { - const TotalProcess = 1 - a.monitor.AddProcess(TotalProcess) - go horusecdart.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) +func (a *Analyzer) detectVulnerabilityDart(_ *sync.WaitGroup, projectSubPath string) error { + horusecdart.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + return nil } -func (a *Analyzer) detectVulnerabilityElixir(projectSubPath string) { - const TotalProcess = 2 - a.monitor.AddProcess(TotalProcess) - - if err := a.dockerSDK.PullImage(a.getCustomOrDefaultImage(languages.Elixir)); err != nil { - a.setErrorAndRemoveProcess(err, TotalProcess) - return +func (a *Analyzer) detectVulnerabilityElixir(wg *sync.WaitGroup, projectSubPath string) error { + if err := a.docker.PullImage(a.getCustomOrDefaultImage(languages.Elixir)); err != nil { + return err } - - go mixaudit.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) - go sobelow.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) + spawn(wg, mixaudit.NewFormatter(a.formatter), projectSubPath) + sobelow.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + return nil } -func (a *Analyzer) detectVulnerabilityShell(projectSubPath string) { - const TotalProcess = 1 - a.monitor.AddProcess(TotalProcess) - - if err := a.dockerSDK.PullImage(a.getCustomOrDefaultImage(languages.Shell)); err != nil { - a.setErrorAndRemoveProcess(err, TotalProcess) - return +func (a *Analyzer) detectVulnerabilityShell(_ *sync.WaitGroup, projectSubPath string) error { + if err := a.docker.PullImage(a.getCustomOrDefaultImage(languages.Shell)); err != nil { + return err } - - go shellcheck.NewFormatter(a.formatterService).StartAnalysis(projectSubPath) + shellcheck.NewFormatter(a.formatter).StartAnalysis(projectSubPath) + return nil } func (a *Analyzer) logProjectSubPath(language languages.Language, subPath string) { @@ -460,11 +423,6 @@ func (a *Analyzer) setFalsePositive() *analysis.Analysis { return a.analysis } -func (a *Analyzer) setErrorAndRemoveProcess(err error, processNumber int) { - a.setAnalysisError(err) - a.monitor.RemoveProcess(processNumber) -} - func (a *Analyzer) setAnalysisError(err error) { if err != nil { toAppend := "" @@ -620,3 +578,11 @@ func (a *Analyzer) removeVulnerabilitiesByTypes() *analysis.Analysis { return a.analysis } + +func spawn(wg *sync.WaitGroup, f formatters.IFormatter, src string) { + wg.Add(1) + go func() { + defer wg.Done() + f.StartAnalysis(src) + }() +} diff --git a/internal/controllers/analyzer/analyzer_test.go b/internal/controllers/analyzer/analyzer_test.go index d17dd944a..cafc195c5 100644 --- a/internal/controllers/analyzer/analyzer_test.go +++ b/internal/controllers/analyzer/analyzer_test.go @@ -21,7 +21,6 @@ import ( "testing" entitiesAnalysis "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" - "github.com/ZupIT/horusec/internal/entities/monitor" horusecAPI "github.com/ZupIT/horusec/internal/services/horusec_api" "github.com/ZupIT/horusec/internal/utils/mock" @@ -94,12 +93,12 @@ func TestAnalyzer_AnalysisDirectory(t *testing.T) { dockerSDK := docker.NewDockerAPI(dockerMocker, configs, uuid.New()) controller := &Analyzer{ - dockerSDK: dockerSDK, - config: configs, - languageDetect: languageDetectMock, - printController: printResultMock, - horusecAPIService: horusecAPIMock, - formatterService: formatters.NewFormatterService(&entitiesAnalysis.Analysis{}, dockerSDK, configs, &monitor.Monitor{}), + docker: dockerSDK, + config: configs, + languageDetect: languageDetectMock, + printController: printResultMock, + horusec: horusecAPIMock, + formatter: formatters.NewFormatterService(&entitiesAnalysis.Analysis{}, dockerSDK, configs), } controller.analysis = &entitiesAnalysis.Analysis{ID: uuid.New()} @@ -151,12 +150,12 @@ func TestAnalyzer_AnalysisDirectory(t *testing.T) { dockerSDK := docker.NewDockerAPI(dockerMocker, configs, uuid.New()) controller := &Analyzer{ - dockerSDK: dockerSDK, - config: configs, - languageDetect: languageDetectMock, - printController: printResultMock, - horusecAPIService: horusecAPIMock, - formatterService: formatters.NewFormatterService(&entitiesAnalysis.Analysis{}, dockerSDK, configs, &monitor.Monitor{}), + docker: dockerSDK, + config: configs, + languageDetect: languageDetectMock, + printController: printResultMock, + horusec: horusecAPIMock, + formatter: formatters.NewFormatterService(&entitiesAnalysis.Analysis{}, dockerSDK, configs), } controller.analysis = &entitiesAnalysis.Analysis{ID: uuid.New()} @@ -193,12 +192,12 @@ func TestAnalyzer_AnalysisDirectory(t *testing.T) { dockerSDK := docker.NewDockerAPI(dockerMocker, configs, uuid.New()) controller := &Analyzer{ - dockerSDK: dockerSDK, - config: configs, - languageDetect: languageDetectMock, - printController: printResultMock, - horusecAPIService: horusecAPIMock, - formatterService: formatters.NewFormatterService(&entitiesAnalysis.Analysis{}, dockerSDK, configs, &monitor.Monitor{}), + docker: dockerSDK, + config: configs, + languageDetect: languageDetectMock, + printController: printResultMock, + horusec: horusecAPIMock, + formatter: formatters.NewFormatterService(&entitiesAnalysis.Analysis{}, dockerSDK, configs), } controller.analysis = &entitiesAnalysis.Analysis{ID: uuid.New()} diff --git a/internal/controllers/printresults/print_results.go b/internal/controllers/printresults/print_results.go index 9198102ff..35d0afbef 100644 --- a/internal/controllers/printresults/print_results.go +++ b/internal/controllers/printresults/print_results.go @@ -193,6 +193,7 @@ func (pr *PrintResults) parseFilePathToAbsAndCreateOutputJSON(bytesToWrite []byt return pr.openJSONFileAndWriteBytes(bytesToWrite, completePath) } +//nolint:gomnd // magic number func (pr *PrintResults) openJSONFileAndWriteBytes(bytesToWrite []byte, completePath string) error { outputFile, err := os.OpenFile(completePath, os.O_CREATE|os.O_WRONLY, 0600) if err != nil { diff --git a/internal/entities/docker/analysis_data.go b/internal/entities/docker/analysis_data.go index a5f76f232..0bd126cc0 100644 --- a/internal/entities/docker/analysis_data.go +++ b/internal/entities/docker/analysis_data.go @@ -16,6 +16,7 @@ package docker import ( "fmt" + "strings" "github.com/ZupIT/horusec-devkit/pkg/enums/languages" "github.com/ZupIT/horusec/internal/enums/images" @@ -46,3 +47,12 @@ func (a *AnalysisData) GetCustomOrDefaultImage() string { return a.DefaultImage } + +func (a *AnalysisData) SetSlnName(slnName string) { + if slnName == "" { + a.CMD = strings.ReplaceAll(a.CMD, "{{SLN_NAME}}", "solution file not found") + return + } + + a.CMD = strings.ReplaceAll(a.CMD, "{{SLN_NAME}}", slnName) +} diff --git a/internal/entities/docker/analysis_data_test.go b/internal/entities/docker/analysis_data_test.go index 8053a18e6..091aaa214 100644 --- a/internal/entities/docker/analysis_data_test.go +++ b/internal/entities/docker/analysis_data_test.go @@ -64,3 +64,23 @@ func TestGetCustomOrDefaultImage(t *testing.T) { assert.Equal(t, "test/default", data.GetCustomOrDefaultImage()) }) } + +func TestSetSlnName(t *testing.T) { + t.Run("should success set sln name", func(t *testing.T) { + data := &AnalysisData{ + CMD: "{{SLN_NAME}}", + } + + data.SetSlnName("test") + assert.Equal(t, "test", data.CMD) + }) + + t.Run("should return not found sln name", func(t *testing.T) { + data := &AnalysisData{ + CMD: "{{SLN_NAME}}", + } + + data.SetSlnName("") + assert.Equal(t, "solution file not found", data.CMD) + }) +} diff --git a/internal/entities/monitor/monitor_test.go b/internal/entities/monitor/monitor_test.go deleted file mode 100644 index 3bf4dbb41..000000000 --- a/internal/entities/monitor/monitor_test.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2020 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 monitor - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestNewMonitor(t *testing.T) { - t.Run("should creates a Monitor instance", func(t *testing.T) { - monitor := NewMonitor() - assert.NotNil(t, monitor) - }) -} - -func TestAddProcess(t *testing.T) { - t.Run("should increment processes and start the monitor", func(t *testing.T) { - monitor := NewMonitor() - monitor.AddProcess(1) - assert.True(t, monitor.IsRunning()) - assert.Equal(t, 1, monitor.GetProcess()) - }) -} - -func TestRemoveProcess(t *testing.T) { - t.Run("should decrement processes and stop the monitor", func(t *testing.T) { - monitor := NewMonitor() - monitor.AddProcess(1) - monitor.RemoveProcess(1) - - assert.True(t, monitor.IsFinished()) - assert.Equal(t, 0, monitor.GetProcess()) - }) -} diff --git a/internal/entities/toolsconfig/tools_config.go b/internal/entities/toolsconfig/tools_config.go index f76c5f074..254ee5efb 100644 --- a/internal/entities/toolsconfig/tools_config.go +++ b/internal/entities/toolsconfig/tools_config.go @@ -15,45 +15,49 @@ type ToolConfig struct { } type ToolsConfigsStruct struct { - Bandit ToolConfig `json:"bandit"` - BundlerAudit ToolConfig `json:"bundleraudit"` - Brakeman ToolConfig `json:"brakeman"` - 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"` + Bandit ToolConfig `json:"bandit"` + BundlerAudit ToolConfig `json:"bundleraudit"` + Brakeman ToolConfig `json:"brakeman"` + 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"` } // nolint:funlen // toMap is necessary more 15 lines func (t *ToolsConfigsStruct) ToMap() MapToolConfig { return MapToolConfig{ - tools.Bandit: t.Bandit, - tools.BundlerAudit: t.BundlerAudit, - tools.Brakeman: t.Brakeman, - tools.Flawfinder: t.Flawfinder, - tools.GitLeaks: t.GitLeaks, - tools.GoSec: t.GoSec, - tools.HorusecEngine: t.HorusecEngine, - tools.MixAudit: t.MixAudit, - tools.NpmAudit: t.NpmAudit, - tools.PhpCS: t.PhpCS, - tools.Safety: t.Safety, - tools.SecurityCodeScan: t.SecurityCodeScan, - tools.Semgrep: t.Semgrep, - tools.ShellCheck: t.ShellCheck, - tools.Sobelow: t.Sobelow, - tools.TfSec: t.TfSec, - tools.YarnAudit: t.YarnAudit, + tools.Bandit: t.Bandit, + tools.BundlerAudit: t.BundlerAudit, + tools.Brakeman: t.Brakeman, + tools.Flawfinder: t.Flawfinder, + tools.GitLeaks: t.GitLeaks, + tools.GoSec: t.GoSec, + tools.HorusecEngine: t.HorusecEngine, + tools.MixAudit: t.MixAudit, + tools.NpmAudit: t.NpmAudit, + tools.PhpCS: t.PhpCS, + tools.Safety: t.Safety, + tools.SecurityCodeScan: t.SecurityCodeScan, + tools.Semgrep: t.Semgrep, + tools.ShellCheck: t.ShellCheck, + tools.Sobelow: t.Sobelow, + tools.TfSec: t.TfSec, + tools.YarnAudit: t.YarnAudit, + tools.OwaspDependencyCheck: t.OwaspDependencyCheck, + tools.DotnetCli: t.DotnetCli, } } diff --git a/internal/enums/images/images.go b/internal/enums/images/images.go index 7b429453c..05084c82e 100644 --- a/internal/enums/images/images.go +++ b/internal/enums/images/images.go @@ -5,9 +5,9 @@ import "github.com/ZupIT/horusec-devkit/pkg/enums/languages" const ( DefaultRegistry = "docker.io" C = "horuszup/horusec-c:v1.0.0" - Csharp = "horuszup/horusec-csharp:v1.0.0" + Csharp = "horuszup/horusec-csharp:v1.0.1" Elixir = "horuszup/horusec-elixir:v1.0.0" - Generic = "horuszup/horusec-generic:v1.0.0" + Generic = "horuszup/horusec-generic:v1.0.1" Go = "horuszup/horusec-go:v1.0.0" HCL = "horuszup/horusec-hcl:v1.0.0" Javascript = "horuszup/horusec-js:v1.0.0" diff --git a/internal/services/engines/rules.go b/internal/services/engines/rules.go index e7436eee6..2948bf616 100644 --- a/internal/services/engines/rules.go +++ b/internal/services/engines/rules.go @@ -23,6 +23,7 @@ func (r *RuleManager) GetAllRules() []engine.Rule { return r.rules } +//nolint:gomnd // magic number func (r *RuleManager) GetTextUnitByRulesExt(src string) ([]engine.Unit, error) { textUnits, err := text.LoadDirIntoMultiUnit(src, 5, r.extensions) if err != nil { diff --git a/internal/services/engines/rules_test.go b/internal/services/engines/rules_test.go index a4b8e7362..7bea230ee 100644 --- a/internal/services/engines/rules_test.go +++ b/internal/services/engines/rules_test.go @@ -3,6 +3,9 @@ package engines_test import ( "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/ZupIT/horusec-engine/text" "github.com/ZupIT/horusec/internal/services/engines" "github.com/ZupIT/horusec/internal/services/engines/csharp" @@ -14,8 +17,6 @@ import ( "github.com/ZupIT/horusec/internal/services/engines/nginx" "github.com/ZupIT/horusec/internal/services/engines/nodejs" "github.com/ZupIT/horusec/internal/services/engines/swift" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestGetRules(t *testing.T) { diff --git a/internal/services/engines/swift/and/and.go b/internal/services/engines/swift/and/and.go index ce0e45b27..44449ad1c 100644 --- a/internal/services/engines/swift/and/and.go +++ b/internal/services/engines/swift/and/and.go @@ -79,7 +79,7 @@ func NewSwiftAndTLS13NotUsed() text.TextRule { Metadata: engine.Metadata{ ID: "50264a8c-c23f-11eb-a035-13ab0aa767e8", Name: "TLS 1.3 not used", - Description: "TLS 1.3 should be used. Detected old version - TLS 1.2.", + Description: "Older versions of SSL/TLS protocol like \"SSLv3\" have been proven to be insecure. This rule raises an issue when an SSL/TLS context is created with an insecure protocol version (ie: a protocol different from \"TLSv1.2\", \"TLSv1.3\", \"DTLSv1.2\" or \"DTLSv1.3\"). For more information checkout the CWE-326 (https://cwe.mitre.org/data/definitions/326.html) and CWE-327 (https://cwe.mitre.org/data/definitions/327.html) advisory.", Severity: severities.Medium.ToString(), Confidence: confidence.Low.ToString(), }, @@ -114,8 +114,8 @@ func NewSwiftAndWeakMD5CryptoCipher() text.TextRule { return text.TextRule{ Metadata: engine.Metadata{ ID: "81e073b6-c205-11eb-a035-13ab0aa767e8", - Name: "Weak Cipher Mode", - Description: "MD5 is a weak hash, which can generate repeated hashes. For more information checkout the CWE-327 (https://cwe.mitre.org/data/definitions/327.html) advisory.", + Name: "Weak MD5 hash using", + Description: "The MD5 hash algorithm that was used is considered weak. It can also cause hash collisions. It is always recommended to use some CHF (Cryptographic Hash Function), which is mathematically strong and not reversible. SHA512 would be the most recommended hash for storing the password and it is also important to adopt some type of Salt, so that the Hash is more secure. For more information checkout the CWE-327 (https://cwe.mitre.org/data/definitions/327.html) advisory.", Severity: severities.Medium.ToString(), Confidence: confidence.Low.ToString(), }, @@ -131,8 +131,8 @@ func NewSwiftAndWeakCommonDesCryptoCipher() text.TextRule { return text.TextRule{ Metadata: engine.Metadata{ ID: "90e9196c-c205-11eb-a035-13ab0aa767e8", - Name: "Weak Cipher Mode", - Description: "DES is a weak hash, which can generate repeated hashes", + Name: "Weak DES hash using", + Description: "DES is considered strong ciphers for modern applications. Currently, NIST recommends the usage of AES block ciphers instead of DES. For more information checkout the CWE-326 (https://cwe.mitre.org/data/definitions/326.html) advisory", Severity: severities.Medium.ToString(), Confidence: confidence.Low.ToString(), }, @@ -148,8 +148,8 @@ func NewSwiftAndWeakIDZDesCryptoCipher() text.TextRule { return text.TextRule{ Metadata: engine.Metadata{ ID: "9b0d9d46-c205-11eb-a035-13ab0aa767e8", - Name: "Weak Cipher Mode", - Description: "Cipher algorithms should be robust", + Name: "Weak DES hash using", + Description: "DES is considered strong ciphers for modern applications. Currently, NIST recommends the usage of AES block ciphers instead of DES. For more information checkout the CWE-326 (https://cwe.mitre.org/data/definitions/326.html) advisory", Severity: severities.Medium.ToString(), Confidence: confidence.Low.ToString(), }, diff --git a/internal/services/engines/swift/or/or.go b/internal/services/engines/swift/or/or.go index dd2d4065c..55e316103 100644 --- a/internal/services/engines/swift/or/or.go +++ b/internal/services/engines/swift/or/or.go @@ -27,8 +27,8 @@ func NewSwiftOrMD6Collision() text.TextRule { return text.TextRule{ Metadata: engine.Metadata{ ID: "b3523bc0-c235-11eb-a035-13ab0aa767e8", - Name: "MD6 collision", - Description: "MD6 is a weak hash known to have hash collisions.", + Name: "Weak MD6 hash using", + Description: "MD6 is a weak hash, which can generate repeated hashes. For more information checkout the CWE-327 (https://cwe.mitre.org/data/definitions/327.html) advisory.", Severity: severities.Medium.ToString(), Confidence: confidence.Low.ToString(), }, @@ -44,8 +44,8 @@ func NewSwiftOrMD5Collision() text.TextRule { return text.TextRule{ Metadata: engine.Metadata{ ID: "7754d218-c235-11eb-a035-13ab0aa767e8", - Name: "MD5 collision", - Description: "MD5 is a weak hash known to have hash collisions.", + Name: "Weak MD5 hash using", + Description: "MD5 is a weak hash, which can generate repeated hashes. For more information checkout the CWE-327 (https://cwe.mitre.org/data/definitions/327.html) advisory.", Severity: severities.Medium.ToString(), Confidence: confidence.Low.ToString(), }, @@ -61,8 +61,8 @@ func NewSwiftOrSha1Collision() text.TextRule { return text.TextRule{ Metadata: engine.Metadata{ ID: "791eee9a-c234-11eb-a035-13ab0aa767e8", - Name: "SHA1 collision", - Description: "SHA1 is a weak hash known to have hash collisions.", + Name: "Weak SHA1 hash using", + Description: "SHA1 is a weak hash, which can generate repeated hashes. For more information checkout the CWE-327 (https://cwe.mitre.org/data/definitions/327.html) advisory.", Severity: severities.Medium.ToString(), Confidence: confidence.Low.ToString(), }, @@ -141,7 +141,7 @@ func NewSwiftOrLoadHTMLString() text.TextRule { Metadata: engine.Metadata{ ID: "18447476-c231-11eb-a035-13ab0aa767e8", Name: "Javascript injection", - Description: `User input in "loadHTMLString" will result in JavaScript Injection.`, + Description: `User input not sanitized in "loadHTMLString" can result in an injection of JavaScript in the context of your application, allowing access to private data. For more information checkout the CWE-95 (https://cwe.mitre.org/data/definitions/95.html) advisory.`, Severity: severities.Medium.ToString(), Confidence: confidence.Low.ToString(), }, @@ -158,7 +158,7 @@ func NewSwiftOrWeakDesCryptoCipher() text.TextRule { Metadata: engine.Metadata{ ID: "a6eec2ac-c205-11eb-a035-13ab0aa767e8", Name: "Weak Cipher Mode", - Description: "DES is a weak hash, which can generate repeated hashes", + Description: "DES is considered strong ciphers for modern applications. Currently, NIST recommends the usage of AES block ciphers instead of DES. For more information checkout the CWE-326 (https://cwe.mitre.org/data/definitions/326.html) advisory", Severity: severities.Medium.ToString(), Confidence: confidence.Low.ToString(), }, diff --git a/internal/services/engines/swift/regular/regular.go b/internal/services/engines/swift/regular/regular.go index 33fad404e..60c886c50 100644 --- a/internal/services/engines/swift/regular/regular.go +++ b/internal/services/engines/swift/regular/regular.go @@ -123,8 +123,8 @@ func NewSwiftRegularMD4Collision() text.TextRule { return text.TextRule{ Metadata: engine.Metadata{ ID: "32d631d6-c235-11eb-a035-13ab0aa767e8", - Name: "MD4 collision", - Description: "MD4 is a weak hash known to have hash collisions.", + Name: "Weak MD4 hash using", + Description: "MD4 is a weak hash, which can generate repeated hashes. For more information checkout the CWE-327 (https://cwe.mitre.org/data/definitions/327.html) advisory.", Severity: severities.Medium.ToString(), Confidence: confidence.Low.ToString(), }, @@ -139,8 +139,8 @@ func NewSwiftRegularMD2Collision() text.TextRule { return text.TextRule{ Metadata: engine.Metadata{ ID: "363a5186-c235-11eb-a035-13ab0aa767e8", - Name: "MD2 collision", - Description: "MD2 is a weak hash known to have hash collisions.", + Name: "Weak MD2 hash using", + Description: "MD2 is a weak hash, which can generate repeated hashes. For more information checkout the CWE-327 (https://cwe.mitre.org/data/definitions/327.html) advisory.", Severity: severities.Medium.ToString(), Confidence: confidence.Low.ToString(), }, diff --git a/internal/services/formatters/c/flawfinder/formatter.go b/internal/services/formatters/c/flawfinder/formatter.go index 6362a5814..4e51a0565 100644 --- a/internal/services/formatters/c/flawfinder/formatter.go +++ b/internal/services/formatters/c/flawfinder/formatter.go @@ -47,7 +47,6 @@ func (f *Formatter) StartAnalysis(projectSubPath string) { f.SetAnalysisError(f.startFlawfinder(projectSubPath), tools.Flawfinder, projectSubPath) f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.Flawfinder, languages.C) - f.SetToolFinishedAnalysis() } func (f *Formatter) startFlawfinder(projectSubPath string) error { diff --git a/internal/services/formatters/c/flawfinder/formatter_test.go b/internal/services/formatters/c/flawfinder/formatter_test.go index fd7b55e37..0276ff85b 100644 --- a/internal/services/formatters/c/flawfinder/formatter_test.go +++ b/internal/services/formatters/c/flawfinder/formatter_test.go @@ -23,7 +23,6 @@ import ( "github.com/ZupIT/horusec/internal/entities/toolsconfig" entitiesAnalysis "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" - "github.com/ZupIT/horusec/internal/entities/monitor" "github.com/stretchr/testify/assert" @@ -67,7 +66,7 @@ func TestStartCFlawfinder(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") @@ -86,7 +85,7 @@ func TestStartCFlawfinder(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) assert.NotPanics(t, func() { @@ -102,7 +101,7 @@ func TestStartCFlawfinder(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", errors.New("test")) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) assert.NotPanics(t, func() { @@ -117,7 +116,7 @@ func TestStartCFlawfinder(t *testing.T) { config.SetWorkDir(&workdir.WorkDir{}) config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{Flawfinder: toolsconfig.ToolConfig{IsToIgnore: true}}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") diff --git a/internal/services/formatters/csharp/deployments/.semver.yaml b/internal/services/formatters/csharp/deployments/.semver.yaml index 36a2accc3..1443f2773 100644 --- a/internal/services/formatters/csharp/deployments/.semver.yaml +++ b/internal/services/formatters/csharp/deployments/.semver.yaml @@ -1,4 +1,4 @@ alpha: 0 beta: 0 rc: 0 -release: v1.0.0 +release: v1.0.1 diff --git a/internal/services/formatters/csharp/deployments/Dockerfile b/internal/services/formatters/csharp/deployments/Dockerfile index c6b00c613..9c6e1da10 100644 --- a/internal/services/formatters/csharp/deployments/Dockerfile +++ b/internal/services/formatters/csharp/deployments/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM mcr.microsoft.com/dotnet/sdk:5.0-alpine +FROM mcr.microsoft.com/dotnet/runtime:5.0-alpine RUN apk add jq diff --git a/internal/services/formatters/csharp/dotnet_cli/config.go b/internal/services/formatters/csharp/dotnet_cli/config.go new file mode 100644 index 000000000..97be6634d --- /dev/null +++ b/internal/services/formatters/csharp/dotnet_cli/config.go @@ -0,0 +1,22 @@ +// 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 dotnetcli + +const CMD = ` + {{WORK_DIR}} + dotnet restore > /tmp/dotnet-cli-restore-ANALYSISID.txt + dotnet list package --vulnerable + chmod -R 777 . + ` diff --git a/internal/services/formatters/csharp/dotnet_cli/entities/dependency.go b/internal/services/formatters/csharp/dotnet_cli/entities/dependency.go new file mode 100644 index 000000000..23cb0359c --- /dev/null +++ b/internal/services/formatters/csharp/dotnet_cli/entities/dependency.go @@ -0,0 +1,69 @@ +// 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 entities + +import ( + "fmt" + "strings" + + "github.com/ZupIT/horusec-devkit/pkg/enums/severities" + + "github.com/ZupIT/horusec/internal/services/formatters/csharp/dotnet_cli/enums" +) + +type Dependency struct { + Name string `json:"name"` + Version string `json:"version"` + Severity string `json:"severity"` + Description string `json:"description"` +} + +func (d *Dependency) SetName(name string) { + d.Name = strings.TrimSpace(name) +} + +func (d *Dependency) SetVersion(version string) { + d.Version = strings.TrimSpace(version) +} + +func (d *Dependency) SetDescription(description string) { + d.Description = strings.TrimSpace(description) +} + +func (d *Dependency) SetSeverity(severity string) { + severity = strings.ReplaceAll(severity, "\u001B[31m", "") + severity = strings.ReplaceAll(severity, "\u001B[33m", "") + d.Severity = strings.TrimSpace(severity) +} + +//nolint:funlen // need to have more than 11 statements +func (d *Dependency) GetSeverity() severities.Severity { + switch d.Severity { + case enums.Critical: + return severities.Critical + case enums.High: + return severities.High + case enums.Moderate: + return severities.Medium + case enums.Low: + return severities.Low + default: + return severities.Unknown + } +} + +func (d *Dependency) GetDescription() string { + return fmt.Sprintf(enums.DependencyDescription, d.Description) +} diff --git a/internal/services/formatters/csharp/dotnet_cli/entities/dependency_test.go b/internal/services/formatters/csharp/dotnet_cli/entities/dependency_test.go new file mode 100644 index 000000000..2ed827ba8 --- /dev/null +++ b/internal/services/formatters/csharp/dotnet_cli/entities/dependency_test.go @@ -0,0 +1,86 @@ +// 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 entities + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/ZupIT/horusec-devkit/pkg/enums/severities" +) + +func TestSetName(t *testing.T) { + t.Run("should success set name", func(t *testing.T) { + dependency := &Dependency{} + + dependency.SetName("test") + assert.Equal(t, "test", dependency.Name) + }) +} + +func TestSetVersion(t *testing.T) { + t.Run("should success set version", func(t *testing.T) { + dependency := &Dependency{} + + dependency.SetVersion("test") + assert.Equal(t, "test", dependency.Version) + }) +} + +func TestSetDescription(t *testing.T) { + t.Run("should success set description", func(t *testing.T) { + dependency := &Dependency{} + + dependency.SetDescription("test") + assert.Equal(t, "test", dependency.Description) + }) +} + +func TestSetSeverity(t *testing.T) { + t.Run("should success set severity", func(t *testing.T) { + dependency := &Dependency{} + + dependency.SetSeverity("test") + assert.Equal(t, "test", dependency.Severity) + + dependency.SetSeverity("\u001B[31m Critical") + assert.Equal(t, "Critical", dependency.Severity) + + dependency.SetSeverity("\u001B[33m Moderate") + assert.Equal(t, "Moderate", dependency.Severity) + }) +} + +func TestGetSeverity(t *testing.T) { + t.Run("should success get severity", func(t *testing.T) { + dependency := &Dependency{} + + dependency.Severity = "Critical" + assert.Equal(t, severities.Critical, dependency.GetSeverity()) + + dependency.Severity = "High" + assert.Equal(t, severities.High, dependency.GetSeverity()) + + dependency.Severity = "Moderate" + assert.Equal(t, severities.Medium, dependency.GetSeverity()) + + dependency.Severity = "Low" + assert.Equal(t, severities.Low, dependency.GetSeverity()) + + dependency.Severity = "Test" + assert.Equal(t, severities.Unknown, dependency.GetSeverity()) + }) +} diff --git a/internal/services/formatters/csharp/dotnet_cli/enums/errors.go b/internal/services/formatters/csharp/dotnet_cli/enums/errors.go new file mode 100644 index 000000000..677cffc5f --- /dev/null +++ b/internal/services/formatters/csharp/dotnet_cli/enums/errors.go @@ -0,0 +1,5 @@ +package enums + +import "errors" + +var ErrorSolutionNotFound = errors.New("{DOTNET CLI} solution file not found") diff --git a/internal/services/formatters/csharp/dotnet_cli/enums/messages.go b/internal/services/formatters/csharp/dotnet_cli/enums/messages.go new file mode 100644 index 000000000..bbe85ec5c --- /dev/null +++ b/internal/services/formatters/csharp/dotnet_cli/enums/messages.go @@ -0,0 +1,6 @@ +package enums + +const ( + DependencyDescription = "A possible vulnerable dependency was found, please checkout " + + "the following url for more information (%s)." +) diff --git a/internal/services/formatters/csharp/dotnet_cli/enums/values.go b/internal/services/formatters/csharp/dotnet_cli/enums/values.go new file mode 100644 index 000000000..1a28e0542 --- /dev/null +++ b/internal/services/formatters/csharp/dotnet_cli/enums/values.go @@ -0,0 +1,31 @@ +// 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 enums + +const ( + IndexDependencyName = 0 + IndexDependencyVersion = 2 + IndexDependencySeverity = 3 + IndexDependencyDescription = 4 + Critical = "Critical" + High = "High" + Moderate = "Moderate" + Low = "Low" + FilePathReplace = "/.horusec/%s" + CsProjExt = ".csproj" + SolutionNotFound = "A project or solution file could not be found" + AutoReferencedPacket = "(A)" + Separator = ">" +) diff --git a/internal/services/formatters/csharp/dotnet_cli/formatter.go b/internal/services/formatters/csharp/dotnet_cli/formatter.go new file mode 100644 index 000000000..16ad796c6 --- /dev/null +++ b/internal/services/formatters/csharp/dotnet_cli/formatter.go @@ -0,0 +1,174 @@ +// 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 dotnetcli + +import ( + "fmt" + "strings" + + "github.com/ZupIT/horusec-devkit/pkg/entities/vulnerability" + "github.com/ZupIT/horusec-devkit/pkg/enums/confidence" + "github.com/ZupIT/horusec-devkit/pkg/enums/languages" + "github.com/ZupIT/horusec-devkit/pkg/enums/tools" + "github.com/ZupIT/horusec-devkit/pkg/utils/logger" + + dockerEntities "github.com/ZupIT/horusec/internal/entities/docker" + "github.com/ZupIT/horusec/internal/enums/images" + "github.com/ZupIT/horusec/internal/helpers/messages" + "github.com/ZupIT/horusec/internal/services/formatters" + "github.com/ZupIT/horusec/internal/services/formatters/csharp/dotnet_cli/entities" + "github.com/ZupIT/horusec/internal/services/formatters/csharp/dotnet_cli/enums" + "github.com/ZupIT/horusec/internal/utils/file" + vulnHash "github.com/ZupIT/horusec/internal/utils/vuln_hash" +) + +type Formatter struct { + formatters.IService +} + +func NewFormatter(service formatters.IService) formatters.IFormatter { + return &Formatter{ + IService: service, + } +} + +func (f *Formatter) StartAnalysis(projectSubPath string) { + if f.ToolIsToIgnore(tools.DotnetCli) || f.IsDockerDisabled() { + logger.LogDebugWithLevel(messages.MsgDebugToolIgnored + tools.DotnetCli.ToString()) + return + } + + f.SetAnalysisError(f.startDotnetCli(projectSubPath), tools.DotnetCli, projectSubPath) + f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.DotnetCli, languages.CSharp) +} + +func (f *Formatter) startDotnetCli(projectSubPath string) error { + f.LogDebugWithReplace(messages.MsgDebugToolStartAnalysis, tools.DotnetCli, languages.CSharp) + + output, err := f.checkOutputErrors(f.ExecuteContainer(f.getConfigData(projectSubPath))) + if err != nil { + return err + } + + f.parseOutput(output, projectSubPath) + return nil +} + +func (f *Formatter) getConfigData(projectSubPath string) *dockerEntities.AnalysisData { + analysisData := &dockerEntities.AnalysisData{ + CMD: f.AddWorkDirInCmd(CMD, file.GetSubPathByExtension( + f.GetConfigProjectPath(), projectSubPath, "*.sln"), tools.DotnetCli), + Language: languages.CSharp, + } + + return analysisData.SetData(f.GetCustomImageByLanguage(languages.CSharp), images.Csharp) +} + +func (f *Formatter) parseOutput(output, projectSubPath string) { + if f.isInvalidOutput(output) { + return + } + + for _, value := range strings.Split(output[strings.Index(output, enums.Separator):], enums.Separator) { + dependency := f.parseDependencyValue(value) + if dependency != nil && *dependency != (entities.Dependency{}) { + f.AddNewVulnerabilityIntoAnalysis(f.setVulnerabilityData(dependency, projectSubPath)) + } + } +} + +func (f *Formatter) parseDependencyValue(value string) *entities.Dependency { + dependency := &entities.Dependency{} + + for index, fieldValue := range f.formatOutput(value) { + if strings.TrimSpace(fieldValue) == "" || strings.TrimSpace(fieldValue) == "\n" { + continue + } + + f.parseFieldByIndex(index, fieldValue, dependency) + } + + return dependency +} + +func (f *Formatter) formatOutput(value string) (result []string) { + value = strings.ReplaceAll(value, "\n", "") + value = strings.ReplaceAll(value, "\r", "") + + for _, field := range strings.Split(value, "\u001B[39;49m") { + field = strings.TrimSpace(field) + if field != "" && strings.TrimSpace(field) != enums.AutoReferencedPacket { + result = append(result, field) + } + } + + return result +} + +func (f *Formatter) parseFieldByIndex(index int, fieldValue string, dependency *entities.Dependency) { + switch index { + case enums.IndexDependencyName: + dependency.SetName(fieldValue) + case enums.IndexDependencyVersion: + dependency.SetVersion(fieldValue) + case enums.IndexDependencySeverity: + dependency.SetSeverity(fieldValue) + case enums.IndexDependencyDescription: + dependency.SetDescription(fieldValue) + } +} + +func (f *Formatter) setVulnerabilityData( + dependency *entities.Dependency, projectSubPath string) *vulnerability.Vulnerability { + code, filepath, line := file.GetDependencyCodeFilepathAndLine( + f.GetConfigProjectPath(), projectSubPath, enums.CsProjExt, dependency.Name) + vuln := f.getDefaultVulnerabilityData() + vuln.Details = dependency.GetDescription() + vuln.Code = code + vuln.File = strings.ReplaceAll(filepath, fmt.Sprintf(enums.FilePathReplace, f.GetAnalysisID()), "") + vuln.Line = line + vuln.Severity = dependency.GetSeverity() + vuln = vulnHash.Bind(vuln) + return f.SetCommitAuthor(vuln) +} + +func (f *Formatter) getDefaultVulnerabilityData() *vulnerability.Vulnerability { + vuln := &vulnerability.Vulnerability{} + vuln.SecurityTool = tools.DotnetCli + vuln.Language = languages.CSharp + vuln.Confidence = confidence.High + return vuln +} + +func (f *Formatter) checkOutputErrors(output string, err error) (string, error) { + if err != nil { + return output, err + } + + if strings.Contains(output, enums.SolutionNotFound) { + return output, enums.ErrorSolutionNotFound + } + + return output, nil +} + +func (f *Formatter) isInvalidOutput(output string) bool { + if strings.Contains(output, "Top-level Package") && strings.Contains(output, "Requested") && + strings.Contains(output, "Resolved") && strings.Contains(output, "Severity") { + return false + } + + return true +} diff --git a/internal/services/formatters/csharp/dotnet_cli/formatter_test.go b/internal/services/formatters/csharp/dotnet_cli/formatter_test.go new file mode 100644 index 000000000..d703f14a3 --- /dev/null +++ b/internal/services/formatters/csharp/dotnet_cli/formatter_test.go @@ -0,0 +1,125 @@ +// 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 dotnetcli + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" + + analysisEntities "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" + + cliConfig "github.com/ZupIT/horusec/config" + "github.com/ZupIT/horusec/internal/entities/toolsconfig" + "github.com/ZupIT/horusec/internal/entities/workdir" + "github.com/ZupIT/horusec/internal/services/docker" + "github.com/ZupIT/horusec/internal/services/formatters" +) + +func TestParseOutput(t *testing.T) { + t.Run("should return 3 vulnerability with no errors", func(t *testing.T) { + dockerAPIControllerMock := &docker.Mock{} + dockerAPIControllerMock.On("SetAnalysisID") + analysis := &analysisEntities.Analysis{} + config := &cliConfig.Config{} + config.SetWorkDir(&workdir.WorkDir{}) + + output := "The following sources were used:\n https://api.nuget.org/v3/index.json\n\nProject " + + "`NetCoreVulnerabilities` has the following vulnerable packages\n\u001B[39;49m\u001B[34m " + + " [netcoreapp3.1]: \n\u001B[39;49m Top-level Package \u001B[39;49m \u001B[39;49m" + + " Requested\u001B[39;49m Resolved\u001B[39;49m Severity\u001B[39;49m Advisory URL " + + " \u001B[39;49m\n\u001B[39;49m > adplug " + + " \u001B[39;49m \u001B[39;49m 2.3.1 \u001B[39;49m 2.3.1 \u001B[39;49m\u001B[39;49m\u001B[31m " + + " Critical\u001B[39;49m https://github.com/advisories/GHSA-874w-m2v2-mj64\u001B[39;49m\n\u001B[39;49m " + + " > Gw2Sharp \u001B[39;49m \u001B[39;49m 0.3.0 \u001B[39;49m 0.3.0 " + + " \u001B[39;49m Low \u001B[39;49m " + + " https://github.com/advisories/GHSA-4vr3-9v7h-5f8v\u001B[39;49m\n\u001B[39;49m > log4net " + + " \u001B[39;49m \u001B[39;49m 2.0.9 \u001B[39;49m 2.0.9 " + + " \u001B[39;49m\u001B[39;49m\u001B[31m Critical\u001B[39;49m " + + "https://github.com/advisories/GHSA-2cwj-8chv-9pp9\u001B[39;49m\n" + + dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) + + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) + formatter := NewFormatter(service) + + formatter.StartAnalysis("") + assert.Len(t, analysis.AnalysisVulnerabilities, 3) + }) + + t.Run("should return no vulnerability with no errors", func(t *testing.T) { + dockerAPIControllerMock := &docker.Mock{} + dockerAPIControllerMock.On("SetAnalysisID") + analysis := &analysisEntities.Analysis{} + config := &cliConfig.Config{} + config.SetWorkDir(&workdir.WorkDir{}) + + dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", nil) + + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) + formatter := NewFormatter(service) + + formatter.StartAnalysis("") + assert.Len(t, analysis.AnalysisVulnerabilities, 0) + }) + + t.Run("should return error executing container", func(t *testing.T) { + analysis := &analysisEntities.Analysis{} + dockerAPIControllerMock := &docker.Mock{} + dockerAPIControllerMock.On("SetAnalysisID") + config := &cliConfig.Config{} + config.SetWorkDir(&workdir.WorkDir{}) + + dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return( + "", errors.New("test")) + + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) + formatter := NewFormatter(service) + + formatter.StartAnalysis("") + assert.NotEmpty(t, analysis.Errors) + }) + + t.Run("should return error when solution was not found", func(t *testing.T) { + analysis := &analysisEntities.Analysis{} + dockerAPIControllerMock := &docker.Mock{} + dockerAPIControllerMock.On("SetAnalysisID") + config := &cliConfig.Config{} + config.SetWorkDir(&workdir.WorkDir{}) + + dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return( + "A project or solution file could not be found", nil) + + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) + formatter := NewFormatter(service) + + formatter.StartAnalysis("") + assert.NotEmpty(t, analysis.Errors) + }) + + t.Run("should not execute tool because it's ignored", func(t *testing.T) { + analysis := &analysisEntities.Analysis{} + dockerAPIControllerMock := &docker.Mock{} + config := &cliConfig.Config{} + config.SetWorkDir(&workdir.WorkDir{}) + config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{DotnetCli: toolsconfig.ToolConfig{IsToIgnore: true}}) + + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) + formatter := NewFormatter(service) + + formatter.StartAnalysis("") + }) +} diff --git a/internal/services/formatters/csharp/scs/config.go b/internal/services/formatters/csharp/scs/config.go index 960f49036..02f985b39 100644 --- a/internal/services/formatters/csharp/scs/config.go +++ b/internal/services/formatters/csharp/scs/config.go @@ -1,4 +1,4 @@ -// Copyright 2020 ZUP IT SERVICOS EM TECNOLOGIA E INOVACAO SA +// 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. @@ -14,23 +14,10 @@ package scs -//nolint const CMD = ` {{WORK_DIR}} - dotnet build --nologo -v q > /tmp/build-output-ANALYSISID.txt - - while read -r LINE; do - - FILECODE=$(echo ${LINE} | awk -F ":" '{print $1}' | tr -d " ") - IDDESC=$(echo ${LINE} | awk -F ":" '{print $2}' | awk '{print $1}' | tr -d " ") - ID=$(echo ${LINE} | awk -F ":" '{print $2}' | awk '{print $2}' | tr -d " ") - DESC=$(echo ${LINE} | awk -F ":" '{print $3}' | sed 's/^ *//') - - echo "{\"Filename\" : \"${FILECODE}\", \"IssueSeverity\" : \"${IDDESC}\", \"ErrorID\" : \"${ID}\", \"IssueText\" : \"${DESC}\"}" >> /tmp/build-output-parsed-ANALYSISID.txt - - done < /tmp/build-output-ANALYSISID.txt - - jq '.' /tmp/build-output-parsed-ANALYSISID.txt > /tmp/scs-result-ANALYSISID.json - jq -j -M -c . /tmp/scs-result-ANALYSISID.json + dotnet restore > /tmp/restore-output-ANALYSISID.txt + security-scan {{SLN_NAME}} --export=output-ANALYSISID.json > /tmp/scs-run-output-ANALYSISID.txt + cat output-ANALYSISID.json chmod -R 777 . ` diff --git a/internal/entities/monitor/monitor.go b/internal/services/formatters/csharp/scs/entities/analysis.go similarity index 50% rename from internal/entities/monitor/monitor.go rename to internal/services/formatters/csharp/scs/entities/analysis.go index 6b6bebe6e..a767d86b2 100644 --- a/internal/entities/monitor/monitor.go +++ b/internal/services/formatters/csharp/scs/entities/analysis.go @@ -1,4 +1,4 @@ -// Copyright 2020 ZUP IT SERVICOS EM TECNOLOGIA E INOVACAO SA +// 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. @@ -12,39 +12,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -package monitor +package entities -type Monitor struct { - process int - started bool +type Analysis struct { + Runs []*Run `json:"runs"` } -func NewMonitor() *Monitor { - return &Monitor{ - process: 0, - started: false, +func (a *Analysis) GetRun() *Run { + if len(a.Runs) > 0 { + return a.Runs[0] } -} -func (m *Monitor) AddProcess(n int) { - if !m.started { - m.started = true - } - m.process += n + return nil } -func (m *Monitor) RemoveProcess(n int) { - m.process -= n -} +func (a *Analysis) MapVulnerabilitiesByID() map[string]*Rule { + vulnMap := map[string]*Rule{} -func (m *Monitor) IsFinished() bool { - return m.started && m.process <= 0 -} - -func (m *Monitor) IsRunning() bool { - return m.started && m.process > 0 -} + for _, rule := range a.GetRun().Tool.Driver.Rules { + vulnMap[rule.ID] = rule + } -func (m *Monitor) GetProcess() int { - return m.process + return vulnMap } diff --git a/internal/services/formatters/csharp/scs/entities/analysis_test.go b/internal/services/formatters/csharp/scs/entities/analysis_test.go new file mode 100644 index 000000000..731d31110 --- /dev/null +++ b/internal/services/formatters/csharp/scs/entities/analysis_test.go @@ -0,0 +1,62 @@ +// 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 entities + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseOutput(t *testing.T) { + t.Run("should success get first run of the array", func(t *testing.T) { + analysis := Analysis{Runs: []*Run{{}}} + + assert.NotNil(t, analysis.GetRun()) + }) + + t.Run("should return nil when empty slice", func(t *testing.T) { + analysis := Analysis{} + + assert.Nil(t, analysis.GetRun()) + }) +} + +func TestMapVulnerabilitiesByID(t *testing.T) { + t.Run("should success map vulnerabilities by id", func(t *testing.T) { + analysis := Analysis{ + Runs: []*Run{ + { + Tool: Tool{ + Driver: Driver{ + Rules: []*Rule{ + { + ID: "test", + FullDescription: Message{ + Text: "test", + }, + HelpURI: "test", + }, + }, + }, + }, + }, + }, + } + + result := analysis.MapVulnerabilitiesByID() + assert.NotEmpty(t, result) + }) +} diff --git a/internal/services/formatters/csharp/scs/entities/artifact_location.go b/internal/services/formatters/csharp/scs/entities/artifact_location.go new file mode 100644 index 000000000..77659be06 --- /dev/null +++ b/internal/services/formatters/csharp/scs/entities/artifact_location.go @@ -0,0 +1,19 @@ +// 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 entities + +type ArtifactLocation struct { + URI string `json:"uri"` +} diff --git a/internal/services/formatters/csharp/scs/entities/driver.go b/internal/services/formatters/csharp/scs/entities/driver.go new file mode 100644 index 000000000..f7d7d7cbe --- /dev/null +++ b/internal/services/formatters/csharp/scs/entities/driver.go @@ -0,0 +1,19 @@ +// 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 entities + +type Driver struct { + Rules []*Rule `json:"rules"` +} diff --git a/internal/services/formatters/csharp/scs/entities/location.go b/internal/services/formatters/csharp/scs/entities/location.go new file mode 100644 index 000000000..bc9d6c674 --- /dev/null +++ b/internal/services/formatters/csharp/scs/entities/location.go @@ -0,0 +1,19 @@ +// 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 entities + +type Location struct { + PhysicalLocation PhysicalLocation `json:"physicalLocation"` +} diff --git a/internal/services/formatters/csharp/scs/entities/message.go b/internal/services/formatters/csharp/scs/entities/message.go new file mode 100644 index 000000000..1db469807 --- /dev/null +++ b/internal/services/formatters/csharp/scs/entities/message.go @@ -0,0 +1,19 @@ +// 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 entities + +type Message struct { + Text string `json:"text"` +} diff --git a/internal/services/formatters/csharp/scs/entities/output.go b/internal/services/formatters/csharp/scs/entities/output.go deleted file mode 100644 index ed6916b8c..000000000 --- a/internal/services/formatters/csharp/scs/entities/output.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2020 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 entities - -import ( - "strings" - - enumsSeverities "github.com/ZupIT/horusec-devkit/pkg/enums/severities" - "github.com/ZupIT/horusec/internal/services/formatters/csharp/scs/severities" -) - -type ScsResult struct { - Filename string `json:"Filename"` - IssueSeverity string `json:"IssueSeverity"` - ErrorID string `json:"ErrorID"` - IssueText string `json:"IssueText"` -} - -func (s *ScsResult) IsValid() bool { - return !s.IsEmpty() && s.IsSecurityIssue() -} - -func (s *ScsResult) IsEmpty() bool { - return s.Filename == "" || s.IssueSeverity == "" || s.ErrorID == "" || s.IssueText == "" -} - -func (s *ScsResult) IsSecurityIssue() bool { - if s.ErrorID == "" { - return false - } - - return s.ErrorID[0:2] == "SC" -} - -func (s *ScsResult) GetLine() string { - indexKey := strings.Index(s.Filename, "(") - indexComma := strings.Index(s.Filename, ",") - - if indexKey < 0 || indexComma < 0 { - return "" - } - - return s.Filename[indexKey+1 : indexComma] -} - -func (s *ScsResult) GetColumn() string { - indexComma := strings.Index(s.Filename, ",") - indexKey := strings.Index(s.Filename, ")") - - if indexKey < 0 || indexComma < 0 { - return "" - } - - return s.Filename[indexComma+1 : indexKey] -} - -func (s *ScsResult) GetFilename() string { - index := strings.Index(s.Filename, "(") - - if index < 0 { - return "" - } - - return s.Filename[0:index] -} - -func (s *ScsResult) GetSeverity() enumsSeverities.Severity { - if s.ErrorID == "" { - return enumsSeverities.Unknown - } - - return s.getVulnerabilityMap()[s.ErrorID] -} - -func (s *ScsResult) getVulnerabilityMap() map[string]enumsSeverities.Severity { - values := map[string]enumsSeverities.Severity{} - - for key, value := range severities.MapCriticalValues() { - values[key] = value - } - for key, value := range severities.MapHighValues() { - values[key] = value - } - for key, value := range severities.MapMediumValues() { - values[key] = value - } - for key, value := range severities.MapLowValues() { - values[key] = value - } - return values -} diff --git a/internal/services/formatters/csharp/scs/entities/output_test.go b/internal/services/formatters/csharp/scs/entities/output_test.go deleted file mode 100644 index 910f2ef2d..000000000 --- a/internal/services/formatters/csharp/scs/entities/output_test.go +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2020 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 entities - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/ZupIT/horusec-devkit/pkg/enums/severities" -) - -func TestIsTypeSC(t *testing.T) { - t.Run("should return true for security issue", func(t *testing.T) { - output := ScsResult{ - Filename: "test", - IssueSeverity: "test", - ErrorID: "SC", - IssueText: "test", - } - - assert.True(t, output.IsSecurityIssue()) - }) - - t.Run("should return false for security issue", func(t *testing.T) { - output := ScsResult{ - Filename: "test", - IssueSeverity: "test", - ErrorID: "CS", - IssueText: "test", - } - - assert.False(t, output.IsSecurityIssue()) - }) - - t.Run("should return false when empty error ID", func(t *testing.T) { - output := ScsResult{ - Filename: "test", - IssueSeverity: "test", - ErrorID: "", - IssueText: "test", - } - - assert.False(t, output.IsSecurityIssue()) - }) -} - -func TestIsEmpty(t *testing.T) { - t.Run("should false for empty output", func(t *testing.T) { - output := ScsResult{ - Filename: "test", - IssueSeverity: "test", - ErrorID: "test", - IssueText: "test", - } - - assert.False(t, output.IsEmpty()) - }) - - t.Run("should true for empty output", func(t *testing.T) { - output := ScsResult{} - - assert.True(t, output.IsEmpty()) - - output.Filename = "test" - - assert.True(t, output.IsEmpty()) - - output.IssueSeverity = "test" - - assert.True(t, output.IsEmpty()) - - output.ErrorID = "test" - - assert.True(t, output.IsEmpty()) - - output.Filename = "test" - - assert.True(t, output.IsEmpty()) - - output.IssueText = "test" - - assert.False(t, output.IsEmpty()) - }) -} - -func TestIsValid(t *testing.T) { - t.Run("should return true for valid output", func(t *testing.T) { - output := ScsResult{ - Filename: "test", - IssueSeverity: "test", - ErrorID: "SC", - IssueText: "test", - } - - assert.True(t, output.IsValid()) - }) - - t.Run("should return false for invalid output", func(t *testing.T) { - output := ScsResult{ - Filename: "test", - IssueSeverity: "test", - ErrorID: "CS", - IssueText: "test", - } - - assert.False(t, output.IsValid()) - }) -} - -func TestGetDotNetSeverityByCode(t *testing.T) { - output := ScsResult{} - - t.Run("should return a low severity", func(t *testing.T) { - output.ErrorID = "SCS0021" - assert.Equal(t, severities.Low, output.GetSeverity()) - }) - - t.Run("should return a medium severity", func(t *testing.T) { - output.ErrorID = "SCS0012" - assert.Equal(t, severities.Medium, output.GetSeverity()) - }) - - t.Run("should return a high severity", func(t *testing.T) { - output.ErrorID = "SCS0014" - assert.Equal(t, severities.High, output.GetSeverity()) - }) - - t.Run("should return a unknown severity", func(t *testing.T) { - output.ErrorID = "" - assert.Equal(t, severities.Unknown, output.GetSeverity()) - }) -} - -func TestGetLine(t *testing.T) { - t.Run("should return filename line", func(t *testing.T) { - output := ScsResult{Filename: "Vulnerabilities.cs(23,26)"} - assert.NotEmpty(t, output.GetLine()) - assert.Equal(t, "23", output.GetLine()) - - output.Filename = "Vulnerabilities.cs(454,766)" - assert.NotEmpty(t, output.GetLine()) - assert.Equal(t, "454", output.GetLine()) - - output.Filename = "Vulnerabilities.cs(3213,4565)" - assert.NotEmpty(t, output.GetLine()) - assert.Equal(t, "3213", output.GetLine()) - }) - - t.Run("should return empty string when invalid data", func(t *testing.T) { - output := ScsResult{Filename: ""} - assert.Empty(t, output.GetLine()) - }) -} - -func TestGetColumn(t *testing.T) { - t.Run("should return filename column", func(t *testing.T) { - output := ScsResult{Filename: "Vulnerabilities.cs(23,26)"} - assert.NotEmpty(t, output.GetColumn()) - assert.Equal(t, "26", output.GetColumn()) - - output.Filename = "Vulnerabilities.cs(454,766)" - assert.NotEmpty(t, output.GetColumn()) - assert.Equal(t, "766", output.GetColumn()) - - output.Filename = "Vulnerabilities.cs(3213,4565)" - assert.NotEmpty(t, output.GetColumn()) - assert.Equal(t, "4565", output.GetColumn()) - }) - - t.Run("should return empty string when invalid data", func(t *testing.T) { - output := ScsResult{Filename: ""} - assert.Empty(t, output.GetColumn()) - }) -} - -func TestGetFilename(t *testing.T) { - t.Run("should return filename", func(t *testing.T) { - output := ScsResult{Filename: "Vulnerabilities.cs(23,26)"} - assert.NotEmpty(t, output.GetFilename()) - assert.Equal(t, "Vulnerabilities.cs", output.GetFilename()) - - output.Filename = "Test.cs(12312,3123123)" - assert.NotEmpty(t, output.GetFilename()) - assert.Equal(t, "Test.cs", output.GetFilename()) - }) - - t.Run("should return empty string when invalid data", func(t *testing.T) { - output := ScsResult{Filename: ""} - assert.Empty(t, output.GetFilename()) - }) -} diff --git a/internal/services/formatters/csharp/scs/entities/physical_location.go b/internal/services/formatters/csharp/scs/entities/physical_location.go new file mode 100644 index 000000000..3a98e4331 --- /dev/null +++ b/internal/services/formatters/csharp/scs/entities/physical_location.go @@ -0,0 +1,20 @@ +// 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 entities + +type PhysicalLocation struct { + ArtifactLocation ArtifactLocation `json:"artifactLocation"` + Region Region `json:"region"` +} diff --git a/internal/services/formatters/csharp/scs/entities/region.go b/internal/services/formatters/csharp/scs/entities/region.go new file mode 100644 index 000000000..8687b576b --- /dev/null +++ b/internal/services/formatters/csharp/scs/entities/region.go @@ -0,0 +1,20 @@ +// 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 entities + +type Region struct { + StartLine int `json:"startLine"` + StartColumn int `json:"startColumn"` +} diff --git a/internal/services/formatters/csharp/scs/entities/result.go b/internal/services/formatters/csharp/scs/entities/result.go new file mode 100644 index 000000000..44b5afc83 --- /dev/null +++ b/internal/services/formatters/csharp/scs/entities/result.go @@ -0,0 +1,54 @@ +// 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 entities + +import ( + "strconv" + "strings" +) + +type Result struct { + RuleID string `json:"ruleId"` + Message Message `json:"message"` + Locations []*Location `json:"locations"` +} + +func (r *Result) GetLine() string { + if len(r.Locations) > 0 { + return strconv.Itoa(r.Locations[0].PhysicalLocation.Region.StartLine) + } + + return "" +} + +func (r *Result) GetColumn() string { + if len(r.Locations) > 0 { + return strconv.Itoa(r.Locations[0].PhysicalLocation.Region.StartColumn) + } + + return "" +} + +func (r *Result) GetVulnName() string { + return r.Message.Text +} + +func (r *Result) GetFile() string { + if len(r.Locations) > 0 { + return strings.ReplaceAll(r.Locations[0].PhysicalLocation.ArtifactLocation.URI, "file:///src/", "") + } + + return "" +} diff --git a/internal/services/formatters/csharp/scs/entities/result_test.go b/internal/services/formatters/csharp/scs/entities/result_test.go new file mode 100644 index 000000000..5acbb1243 --- /dev/null +++ b/internal/services/formatters/csharp/scs/entities/result_test.go @@ -0,0 +1,149 @@ +// 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 entities + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetLine(t *testing.T) { + t.Run("should success get line", func(t *testing.T) { + result := Result{ + RuleID: "test", + Message: Message{ + Text: "test", + }, + Locations: []*Location{ + { + PhysicalLocation: PhysicalLocation{ + ArtifactLocation: ArtifactLocation{ + URI: "test", + }, + Region: Region{ + StartLine: 1, + StartColumn: 2, + }, + }, + }, + }, + } + + assert.Equal(t, "1", result.GetLine()) + }) + + t.Run("should return empty string", func(t *testing.T) { + result := Result{ + Locations: []*Location{}, + } + + assert.Empty(t, result.GetLine()) + }) +} + +func TestGetColumn(t *testing.T) { + t.Run("should success get column", func(t *testing.T) { + result := Result{ + RuleID: "test", + Message: Message{ + Text: "test", + }, + Locations: []*Location{ + { + PhysicalLocation: PhysicalLocation{ + ArtifactLocation: ArtifactLocation{ + URI: "test", + }, + Region: Region{ + StartLine: 1, + StartColumn: 2, + }, + }, + }, + }, + } + + assert.Equal(t, "2", result.GetColumn()) + }) + + t.Run("should return empty string", func(t *testing.T) { + result := Result{ + Locations: []*Location{}, + } + + assert.Empty(t, result.GetColumn()) + }) +} + +func TestGetVulnName(t *testing.T) { + t.Run("should success get vulnerability name", func(t *testing.T) { + result := Result{ + RuleID: "test", + Message: Message{ + Text: "test", + }, + Locations: []*Location{ + { + PhysicalLocation: PhysicalLocation{ + ArtifactLocation: ArtifactLocation{ + URI: "test", + }, + Region: Region{ + StartLine: 1, + StartColumn: 2, + }, + }, + }, + }, + } + + assert.Equal(t, "test", result.GetVulnName()) + }) +} + +func TestGetFile(t *testing.T) { + t.Run("should success get file", func(t *testing.T) { + result := Result{ + RuleID: "test", + Message: Message{ + Text: "test", + }, + Locations: []*Location{ + { + PhysicalLocation: PhysicalLocation{ + ArtifactLocation: ArtifactLocation{ + URI: "file:///src/test", + }, + Region: Region{ + StartLine: 1, + StartColumn: 2, + }, + }, + }, + }, + } + + assert.Equal(t, "test", result.GetFile()) + }) + + t.Run("should return empty string", func(t *testing.T) { + result := Result{ + Locations: []*Location{}, + } + + assert.Empty(t, result.GetFile()) + }) +} diff --git a/internal/services/formatters/csharp/scs/entities/rule.go b/internal/services/formatters/csharp/scs/entities/rule.go new file mode 100644 index 000000000..47a7d302b --- /dev/null +++ b/internal/services/formatters/csharp/scs/entities/rule.go @@ -0,0 +1,41 @@ +// 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 entities + +import ( + "fmt" + "strings" +) + +type Rule struct { + ID string `json:"id"` + FullDescription Message `json:"fullDescription"` + HelpURI string `json:"helpUri"` +} + +func (r *Rule) getFullDescription() string { + fullDescription := strings.ReplaceAll(r.FullDescription.Text, "{", "") + fullDescription = strings.ReplaceAll(fullDescription, "}", "") + return fullDescription +} + +func (r *Rule) GetDescription(vulnName string) string { + if r.HelpURI == "" { + return vulnName + } + + return fmt.Sprintf("%s\n%s For more information, check the following url (%s).", + vulnName, r.getFullDescription(), r.HelpURI) +} diff --git a/internal/services/formatters/csharp/scs/entities/rule_test.go b/internal/services/formatters/csharp/scs/entities/rule_test.go new file mode 100644 index 000000000..eec160f2c --- /dev/null +++ b/internal/services/formatters/csharp/scs/entities/rule_test.go @@ -0,0 +1,36 @@ +// 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 entities + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetDescription(t *testing.T) { + t.Run("should return empty string", func(t *testing.T) { + rule := Rule{ + ID: "test", + FullDescription: Message{ + Text: "{test}", + }, + HelpURI: "test", + } + + assert.Equal(t, "test\ntest For more information, check the following url (test).", + rule.GetDescription("test")) + }) +} diff --git a/internal/services/formatters/csharp/scs/entities/run.go b/internal/services/formatters/csharp/scs/entities/run.go new file mode 100644 index 000000000..503845f8f --- /dev/null +++ b/internal/services/formatters/csharp/scs/entities/run.go @@ -0,0 +1,20 @@ +// 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 entities + +type Run struct { + Results []*Result `json:"results"` + Tool Tool `json:"tool"` +} diff --git a/internal/services/formatters/csharp/scs/entities/tool.go b/internal/services/formatters/csharp/scs/entities/tool.go new file mode 100644 index 000000000..33278741e --- /dev/null +++ b/internal/services/formatters/csharp/scs/entities/tool.go @@ -0,0 +1,19 @@ +// 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 entities + +type Tool struct { + Driver Driver `json:"driver"` +} diff --git a/internal/services/formatters/csharp/scs/enums/errors.go b/internal/services/formatters/csharp/scs/enums/errors.go new file mode 100644 index 000000000..cd8857e96 --- /dev/null +++ b/internal/services/formatters/csharp/scs/enums/errors.go @@ -0,0 +1,6 @@ +package enums + +import "errors" + +var ErrorFailedToBuildProject = errors.New( + "{SECURITY CODE SCAN} project failed to build. Fix the project issues and try again") diff --git a/internal/services/formatters/csharp/scs/enums/values.go b/internal/services/formatters/csharp/scs/enums/values.go new file mode 100644 index 000000000..18e6f0e60 --- /dev/null +++ b/internal/services/formatters/csharp/scs/enums/values.go @@ -0,0 +1,7 @@ +package enums + +const ( + BuildFailedOutput = "Msbuild failed when processing the file" + SolutionFileNotFound = "solution file not found" + SolutionExt = ".sln" +) diff --git a/internal/services/formatters/csharp/scs/formatter.go b/internal/services/formatters/csharp/scs/formatter.go index 7f975f05e..93b360ee6 100644 --- a/internal/services/formatters/csharp/scs/formatter.go +++ b/internal/services/formatters/csharp/scs/formatter.go @@ -1,4 +1,4 @@ -// Copyright 2020 ZUP IT SERVICOS EM TECNOLOGIA E INOVACAO SA +// 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. @@ -19,27 +19,32 @@ import ( "strings" "github.com/ZupIT/horusec-devkit/pkg/entities/vulnerability" - "github.com/ZupIT/horusec/internal/utils/file" - vulnhash "github.com/ZupIT/horusec/internal/utils/vuln_hash" - "github.com/ZupIT/horusec-devkit/pkg/enums/languages" + "github.com/ZupIT/horusec-devkit/pkg/enums/severities" "github.com/ZupIT/horusec-devkit/pkg/enums/tools" "github.com/ZupIT/horusec-devkit/pkg/utils/logger" + dockerEntities "github.com/ZupIT/horusec/internal/entities/docker" errorsEnums "github.com/ZupIT/horusec/internal/enums/errors" "github.com/ZupIT/horusec/internal/enums/images" "github.com/ZupIT/horusec/internal/helpers/messages" "github.com/ZupIT/horusec/internal/services/formatters" "github.com/ZupIT/horusec/internal/services/formatters/csharp/scs/entities" + "github.com/ZupIT/horusec/internal/services/formatters/csharp/scs/enums" + severitiesScs "github.com/ZupIT/horusec/internal/services/formatters/csharp/scs/severities" + fileUtils "github.com/ZupIT/horusec/internal/utils/file" + vulnHash "github.com/ZupIT/horusec/internal/utils/vuln_hash" ) type Formatter struct { formatters.IService + severities map[string]severities.Severity + vulnerabilitiesByID map[string]*entities.Rule } func NewFormatter(service formatters.IService) formatters.IFormatter { return &Formatter{ - service, + IService: service, } } @@ -51,68 +56,53 @@ func (f *Formatter) StartAnalysis(projectSubPath string) { f.SetAnalysisError(f.startSecurityCodeScan(projectSubPath), tools.SecurityCodeScan, projectSubPath) f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.SecurityCodeScan, languages.CSharp) - f.SetToolFinishedAnalysis() } func (f *Formatter) startSecurityCodeScan(projectSubPath string) error { f.LogDebugWithReplace(messages.MsgDebugToolStartAnalysis, tools.SecurityCodeScan, languages.CSharp) - output, err := f.ExecuteContainer(f.getDockerConfig(projectSubPath)) - if err != nil { + analysisData := f.getDockerConfig(projectSubPath) + if err := f.verifyIsSolutionError(analysisData.CMD); err != nil { return err } - if errSolution := f.verifyIsSolutionError(output, err); errSolution != nil { - return errSolution + output, err := f.CheckOutputErrors(f.ExecuteContainer(analysisData)) + if err != nil { + return err } - f.parseOutput(output, projectSubPath) - return nil -} - -func (f *Formatter) parseOutput(output, projectSubPath string) { - for _, scsResult := range f.newScsResultArrayFromOutput(output) { - f.AddNewVulnerabilityIntoAnalysis(f.setVulnerabilitySeverityData(scsResult, projectSubPath)) - } + return f.parseOutput(output) } -func (f *Formatter) newScsResultArrayFromOutput(dockerOutput string) (outputs []entities.ScsResult) { - for _, output := range f.splitSCSContainerOutput(dockerOutput) { - if output == "" { - continue - } +func (f *Formatter) parseOutput(output string) error { + analysis := &entities.Analysis{} - if result, err := f.parseStringToStruct(output); err == nil && result.IsValid() { - outputs = append(outputs, result) - } + if err := json.Unmarshal([]byte(output), &analysis); err != nil { + return err } - return f.removeDuplicatedOutputs(outputs) -} - -func (f *Formatter) splitSCSContainerOutput(output string) []string { - return strings.SplitAfter(output, "}") -} + f.setSeveritiesAndVulnsByID(analysis) + for _, result := range analysis.GetRun().Results { + f.AddNewVulnerabilityIntoAnalysis(f.setVulnerabilityData(result)) + } -func (f *Formatter) parseStringToStruct(output string) (scsResult entities.ScsResult, err error) { - err = json.Unmarshal([]byte(output), &scsResult) - logger.LogErrorWithLevel(f.GetAnalysisIDErrorMessage(tools.SecurityCodeScan, output), err) - return scsResult, err + return nil } -func (f *Formatter) removeDuplicatedOutputs(scsResults []entities.ScsResult) []entities.ScsResult { - return scsResults[0 : len(scsResults)/2] +func (f *Formatter) setSeveritiesAndVulnsByID(analysis *entities.Analysis) { + f.severities = f.getVulnerabilityMap() + f.vulnerabilitiesByID = analysis.MapVulnerabilitiesByID() } -func (f *Formatter) setVulnerabilitySeverityData(scsResult entities.ScsResult, - projectSubPath string) *vulnerability.Vulnerability { +func (f *Formatter) setVulnerabilityData(result *entities.Result) *vulnerability.Vulnerability { data := f.getDefaultVulnerabilitySeverity() - data.Severity = scsResult.GetSeverity() - data.Details = f.removeCsprojPathFromDetails(scsResult.IssueText) - data.Line = scsResult.GetLine() - data.Column = scsResult.GetColumn() - data.File = f.GetFilepathFromFilename(f.RemoveSrcFolderFromPath(scsResult.GetFilename()), projectSubPath) - data = vulnhash.Bind(data) + data.Severity = f.GetSeverity(result.RuleID) + data.Details = f.GetDetails(result.RuleID, result.GetVulnName()) + data.Line = result.GetLine() + data.Column = result.GetColumn() + data.File = result.GetFile() + data.Code = fileUtils.GetCode(f.GetConfigProjectPath(), result.GetFile(), result.GetLine()) + data = vulnHash.Bind(data) return f.SetCommitAuthor(data) } @@ -125,29 +115,65 @@ func (f *Formatter) getDefaultVulnerabilitySeverity() *vulnerability.Vulnerabili func (f *Formatter) getDockerConfig(projectSubPath string) *dockerEntities.AnalysisData { analysisData := &dockerEntities.AnalysisData{ - CMD: f.AddWorkDirInCmd(CMD, file.GetSubPathByExtension( - f.GetConfigProjectPath(), projectSubPath, "*.sln"), tools.SecurityCodeScan), + CMD: f.AddWorkDirInCmd(CMD, fileUtils.GetSubPathByExtension( + f.GetConfigProjectPath(), projectSubPath, enums.SolutionExt), tools.SecurityCodeScan), Language: languages.CSharp, } + analysisData.SetSlnName(fileUtils.GetFilenameByExt(f.GetConfigProjectPath(), projectSubPath, enums.SolutionExt)) return analysisData.SetData(f.GetCustomImageByLanguage(languages.CSharp), images.Csharp) } -func (f *Formatter) verifyIsSolutionError(output string, err error) error { - if strings.Contains(output, "Specify a project or solution file") { - msg := f.GetAnalysisIDErrorMessage(tools.SecurityCodeScan, output) - logger.LogErrorWithLevel(msg, errorsEnums.ErrSolutionNotFound) +func (f *Formatter) verifyIsSolutionError(cmd string) error { + if strings.Contains(cmd, enums.SolutionFileNotFound) { return errorsEnums.ErrSolutionNotFound } - return err + return nil +} + +func (f *Formatter) GetSeverity(ruleID string) severities.Severity { + if ruleID == "" { + return severities.Unknown + } + + return f.severities[ruleID] } -func (f *Formatter) removeCsprojPathFromDetails(details string) string { - index := strings.Index(details, "[/src/") - if details == "" || index <= 0 { - return details +func (f Formatter) GetDetails(ruleID, vulnName string) string { + if ruleID == "" { + return vulnName + } + + return f.vulnerabilitiesByID[ruleID].GetDescription(vulnName) +} + +func (f *Formatter) getVulnerabilityMap() map[string]severities.Severity { + values := map[string]severities.Severity{} + for key, value := range severitiesScs.MapCriticalValues() { + values[key] = value + } + for key, value := range severitiesScs.MapHighValues() { + values[key] = value + } + for key, value := range severitiesScs.MapMediumValues() { + values[key] = value + } + for key, value := range severitiesScs.MapLowValues() { + values[key] = value + } + + return values +} + +func (f *Formatter) CheckOutputErrors(output string, err error) (string, error) { + if err != nil { + return output, err + } + + if strings.Contains(output, enums.BuildFailedOutput) { + return output, enums.ErrorFailedToBuildProject } - return details[:index] + return output, nil } diff --git a/internal/services/formatters/csharp/scs/formatter_test.go b/internal/services/formatters/csharp/scs/formatter_test.go index 543dace16..40d19f9ba 100644 --- a/internal/services/formatters/csharp/scs/formatter_test.go +++ b/internal/services/formatters/csharp/scs/formatter_test.go @@ -16,107 +16,188 @@ package scs import ( "errors" + "os" "testing" - "github.com/ZupIT/horusec/internal/entities/toolsconfig" - - entitiesAnalysis "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" - "github.com/ZupIT/horusec/internal/entities/monitor" - "github.com/stretchr/testify/assert" + analysisEntities "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" + cliConfig "github.com/ZupIT/horusec/config" + "github.com/ZupIT/horusec/internal/entities/toolsconfig" "github.com/ZupIT/horusec/internal/entities/workdir" "github.com/ZupIT/horusec/internal/services/docker" "github.com/ZupIT/horusec/internal/services/formatters" + "github.com/ZupIT/horusec/internal/services/formatters/csharp/scs/enums" ) +func createSlnFile() error { + path, _ := os.Getwd() + + if err := os.MkdirAll(path+"/.horusec/00000000-0000-0000-0000-000000000000", 0755); err != nil { + return err + } + + _, err := os.Create(path + "/.horusec/00000000-0000-0000-0000-000000000000/test.sln") + return err +} + +func removeSlnFile() error { + path, _ := os.Getwd() + + return os.RemoveAll(path + "/.horusec") +} + func TestParseOutput(t *testing.T) { - t.Run("Should return 4 vulnerabilities with no errors", func(t *testing.T) { + t.Run("should return 4 vulnerabilities with no errors", func(t *testing.T) { + assert.NoError(t, createSlnFile()) + dockerAPIControllerMock := &docker.Mock{} dockerAPIControllerMock.On("SetAnalysisID") - analysis := &entitiesAnalysis.Analysis{} + analysis := &analysisEntities.Analysis{} config := &cliConfig.Config{} config.SetWorkDir(&workdir.WorkDir{}) - output := "{\"Filename\": \"Vulnerabilities.cs(11,22)\", \"IssueSeverity\": \"test\", \"ErrorID\": \"SCS1234\", \"IssueText\": \"test/[/src/test\"}" + - "{\"Filename\": \"Vulnerabilities.cs(33,44)\", \"IssueSeverity\": \"test\", \"ErrorID\": \"SCS0021\", \"IssueText\": \"test\"}" + - "{\"Filename\": \"Vulnerabilities.cs(55,66)\", \"IssueSeverity\": \"test\", \"ErrorID\": \"SCS0012\", \"IssueText\": \"test\"}" + - "{\"Filename\": \"Vulnerabilities.cs(77,88)\", \"IssueSeverity\": \"test\", \"ErrorID\": \"SCS0020\", \"IssueText\": \"test\"}" + - "{\"Filename\": \"Vulnerabilities.cs(11,22)\", \"IssueSeverity\": \"test\", \"ErrorID\": \"SCS1234\", \"IssueText\": \"test\"}" + - "{\"Filename\": \"Vulnerabilities.cs(33,44)\", \"IssueSeverity\": \"test\", \"ErrorID\": \"SCS0021\", \"IssueText\": \"test\"}" + - "{\"Filename\": \"Vulnerabilities.cs(55,66)\", \"IssueSeverity\": \"test\", \"ErrorID\": \"SCS0012\", \"IssueText\": \"test\"}" + - "{\"Filename\": \"Vulnerabilities.cs(77,88)\", \"IssueSeverity\": \"test\", \"ErrorID\": \"SCS0020\", \"IssueText\": \"test\"}" + output := "{ \"$schema\": \"https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.5.json\"," + + " \"version\": \"2.1.0\", \"runs\": [ { \"results\": [ { \"ruleId\": \"SCS0006\", \"ruleIndex\": 0, " + + "\"level\": \"warning\", \"message\": { \"text\": \"Weak hashing function.\" }, \"locations\": [ " + + "{ \"physicalLocation\": { \"artifactLocation\": { \"uri\": " + + "\"file:///src/NetCoreVulnerabilities/Vulnerabilities.cs\" }, \"region\": { \"startLine\": 22, " + + "\"startColumn\": 32, \"endLine\": 22, \"endColumn\": 63 } } } ], \"properties\": { \"warningLevel\": " + + "1 } }, { \"ruleId\": \"SCS0006\", \"ruleIndex\": 0, \"level\": \"warning\", \"message\": { \"text\":" + + " \"Weak hashing function.\" }, \"locations\": [ { \"physicalLocation\": { \"artifactLocation\":" + + " { \"uri\": \"file:///src/NetCoreVulnerabilities/Vulnerabilities.cs\" }, \"region\": { \"startLine\": " + + "15, \"startColumn\": 32, \"endLine\": 15, \"endColumn\": 63 } } } ], \"properties\":" + + " { \"warningLevel\": 1 } }, { \"ruleId\": \"SCS0005\", \"ruleIndex\": 1, \"level\": \"warning\"," + + " \"message\": { \"text\": \"Weak random number generator.\" }, \"locations\": [ { \"physicalLocation\":" + + " { \"artifactLocation\": { \"uri\": \"file:///src/NetCoreVulnerabilities/Vulnerabilities.cs\" }," + + " \"region\": { \"startLine\": 37, \"startColumn\": 13, \"endLine\": 37, \"endColumn\": 26 } } } ]," + + " \"properties\": { \"warningLevel\": 1 } }, { \"ruleId\": \"\", \"ruleIndex\": 2, \"level\":" + + " \"warning\", \"message\": { \"text\": \"Hardcoded value in 'string password'.\" }, \"locations\": [" + + " { \"physicalLocation\": { \"artifactLocation\": { \"uri\": " + + "\"file:///src/NetCoreVulnerabilities/Vulnerabilities.cs\" }, \"region\": { \"startLine\": 28, " + + "\"startColumn\": 34, \"endLine\": 28, \"endColumn\": 88 } } } ], \"relatedLocations\": [ {" + + " \"physicalLocation\": { \"artifactLocation\": { \"uri\": " + + "\"file:///src/NetCoreVulnerabilities/Vulnerabilities.cs\" }, \"region\": { \"startLine\": 28," + + " \"startColumn\": 34, \"endLine\": 28, \"endColumn\": 88 } } } ], \"properties\": { \"warningLevel\":" + + " 1 } } ], \"tool\": { \"driver\": { \"name\": \"Security Code Scan\", \"version\": \"5.1.1.0\"," + + " \"dottedQuadFileVersion\": \"5.1.1.0\", \"semanticVersion\": \"5.1.1\", \"language\": \"\", \"rules\":" + + " [ { \"id\": \"SCS0006\", \"shortDescription\": { \"text\": \"Weak hashing function.\" }," + + " \"fullDescription\": { \"text\": \"SHA1 is no longer considered as a strong hashing algorithm.\" }," + + " \"helpUri\": \"https://security-code-scan.github.io/#SCS0006\", \"properties\":" + + " { \"category\": \"Security\" } }, { \"id\": \"SCS0005\", \"shortDescription\": { \"text\":" + + " \"Weak random number generator.\" }, \"fullDescription\": { \"text\": \"It is possible to predict" + + " the next numbers of a pseudo random generator. Use a cryptographically strong generator for security" + + " sensitive purposes.\" }, \"helpUri\": \"https://security-code-scan.github.io/#SCS0005\"," + + " \"properties\": { \"category\": \"Security\" } }, { \"id\": \"SCS0015\", \"shortDescription\":" + + " { \"text\": \"Hardcoded value in '{0}'.\" }, \"fullDescription\": { \"text\":" + + " \"The secret value to this API appears to be hardcoded. Consider moving the value" + + " to externalized configuration to avoid leakage of secret information.\" }, \"helpUri\": " + + "\"https://security-code-scan.github.io/#SCS0015\", \"properties\": { \"category\": \"Security\" " + + "} } ] } }, \"columnKind\": \"utf16CodeUnits\" } ] }" dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") assert.Len(t, analysis.AnalysisVulnerabilities, 4) + + assert.NoError(t, removeSlnFile()) }) - t.Run("Should error not found cs proj", func(t *testing.T) { - analysis := &entitiesAnalysis.Analysis{} + t.Run("should return error when unmarshalling output", func(t *testing.T) { + assert.NoError(t, createSlnFile()) + + analysis := &analysisEntities.Analysis{} dockerAPIControllerMock := &docker.Mock{} dockerAPIControllerMock.On("SetAnalysisID") config := &cliConfig.Config{} config.SetWorkDir(&workdir.WorkDir{}) - output := "Specify a project or solution file" + output := "test" dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") assert.NotEmpty(t, analysis.Errors) + + assert.NoError(t, removeSlnFile()) }) - t.Run("Should error executing container", func(t *testing.T) { - analysis := &entitiesAnalysis.Analysis{} + t.Run("should return build error", func(t *testing.T) { + assert.NoError(t, createSlnFile()) + + analysis := &analysisEntities.Analysis{} dockerAPIControllerMock := &docker.Mock{} dockerAPIControllerMock.On("SetAnalysisID") config := &cliConfig.Config{} config.SetWorkDir(&workdir.WorkDir{}) - dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", errors.New("test")) + output := enums.BuildFailedOutput + + dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") assert.NotEmpty(t, analysis.Errors) + + assert.NoError(t, removeSlnFile()) }) - t.Run("Should not execute tool because it's ignored", func(t *testing.T) { - analysis := &entitiesAnalysis.Analysis{} + t.Run("should return error not found solution file", func(t *testing.T) { + analysis := &analysisEntities.Analysis{} dockerAPIControllerMock := &docker.Mock{} + dockerAPIControllerMock.On("SetAnalysisID") config := &cliConfig.Config{} config.SetWorkDir(&workdir.WorkDir{}) - config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{SecurityCodeScan: toolsconfig.ToolConfig{IsToIgnore: true}}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", nil) + + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") + assert.NotEmpty(t, analysis.Errors) }) -} -func TestParseStringToStruct(t *testing.T) { - t.Run("Should return error when unmarshall a invalid data", func(t *testing.T) { + t.Run("should return error executing container", func(t *testing.T) { + assert.NoError(t, createSlnFile()) + + analysis := &analysisEntities.Analysis{} + dockerAPIControllerMock := &docker.Mock{} + dockerAPIControllerMock.On("SetAnalysisID") config := &cliConfig.Config{} config.SetWorkDir(&workdir.WorkDir{}) - service := formatters.NewFormatterService(&entitiesAnalysis.Analysis{}, nil, config, &monitor.Monitor{}) + dockerAPIControllerMock.On("CreateLanguageAnalysisContainer"). + Return("", errors.New("test")) + + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) + formatter := NewFormatter(service) - formatter := Formatter{ - service, - } + formatter.StartAnalysis("") + assert.NotEmpty(t, analysis.Errors) - _, err := formatter.parseStringToStruct("!!!") - assert.Error(t, err) + assert.NoError(t, removeSlnFile()) + }) + + t.Run("should not execute tool because it's ignored", func(t *testing.T) { + analysis := &analysisEntities.Analysis{} + dockerAPIControllerMock := &docker.Mock{} + config := &cliConfig.Config{} + config.SetWorkDir(&workdir.WorkDir{}) + config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{SecurityCodeScan: toolsconfig.ToolConfig{IsToIgnore: true}}) + + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) + formatter := NewFormatter(service) + + formatter.StartAnalysis("") }) } diff --git a/internal/services/formatters/csharp/scs/severities/dotnet_critical.go b/internal/services/formatters/csharp/scs/severities/dotnet_critical.go index 6aebb46a4..2f9116be9 100644 --- a/internal/services/formatters/csharp/scs/severities/dotnet_critical.go +++ b/internal/services/formatters/csharp/scs/severities/dotnet_critical.go @@ -1,3 +1,17 @@ +// 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 severities import "github.com/ZupIT/horusec-devkit/pkg/enums/severities" diff --git a/internal/services/formatters/csharp/scs/severities/dotnet_critital_test.go b/internal/services/formatters/csharp/scs/severities/dotnet_critital_test.go index 73c44609c..c43a869fd 100644 --- a/internal/services/formatters/csharp/scs/severities/dotnet_critital_test.go +++ b/internal/services/formatters/csharp/scs/severities/dotnet_critital_test.go @@ -1,3 +1,17 @@ +// 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 severities import ( diff --git a/internal/services/formatters/csharp/scs/severities/dotnet_high.go b/internal/services/formatters/csharp/scs/severities/dotnet_high.go index 569b339d1..31a8706b6 100644 --- a/internal/services/formatters/csharp/scs/severities/dotnet_high.go +++ b/internal/services/formatters/csharp/scs/severities/dotnet_high.go @@ -1,4 +1,4 @@ -// Copyright 2020 ZUP IT SERVICOS EM TECNOLOGIA E INOVACAO SA +// 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. diff --git a/internal/services/formatters/csharp/scs/severities/dotnet_high_test.go b/internal/services/formatters/csharp/scs/severities/dotnet_high_test.go index b7e38cc92..7b4115337 100644 --- a/internal/services/formatters/csharp/scs/severities/dotnet_high_test.go +++ b/internal/services/formatters/csharp/scs/severities/dotnet_high_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 ZUP IT SERVICOS EM TECNOLOGIA E INOVACAO SA +// 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. diff --git a/internal/services/formatters/csharp/scs/severities/dotnet_low.go b/internal/services/formatters/csharp/scs/severities/dotnet_low.go index 474c2dbbe..110578012 100644 --- a/internal/services/formatters/csharp/scs/severities/dotnet_low.go +++ b/internal/services/formatters/csharp/scs/severities/dotnet_low.go @@ -1,4 +1,4 @@ -// Copyright 2020 ZUP IT SERVICOS EM TECNOLOGIA E INOVACAO SA +// 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. diff --git a/internal/services/formatters/csharp/scs/severities/dotnet_low_test.go b/internal/services/formatters/csharp/scs/severities/dotnet_low_test.go index 75acd839a..e56fd0326 100644 --- a/internal/services/formatters/csharp/scs/severities/dotnet_low_test.go +++ b/internal/services/formatters/csharp/scs/severities/dotnet_low_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 ZUP IT SERVICOS EM TECNOLOGIA E INOVACAO SA +// 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. diff --git a/internal/services/formatters/csharp/scs/severities/dotnet_medium.go b/internal/services/formatters/csharp/scs/severities/dotnet_medium.go index 0db439f0a..1926a8a0d 100644 --- a/internal/services/formatters/csharp/scs/severities/dotnet_medium.go +++ b/internal/services/formatters/csharp/scs/severities/dotnet_medium.go @@ -1,4 +1,4 @@ -// Copyright 2020 ZUP IT SERVICOS EM TECNOLOGIA E INOVACAO SA +// 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. diff --git a/internal/services/formatters/csharp/scs/severities/dotnet_medium_test.go b/internal/services/formatters/csharp/scs/severities/dotnet_medium_test.go index 3e7e8bdf5..cbc310a5c 100644 --- a/internal/services/formatters/csharp/scs/severities/dotnet_medium_test.go +++ b/internal/services/formatters/csharp/scs/severities/dotnet_medium_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 ZUP IT SERVICOS EM TECNOLOGIA E INOVACAO SA +// 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. diff --git a/internal/services/formatters/default_engine_formatter.go b/internal/services/formatters/default_engine_formatter.go index c870abd71..8ce3bb52a 100644 --- a/internal/services/formatters/default_engine_formatter.go +++ b/internal/services/formatters/default_engine_formatter.go @@ -50,7 +50,6 @@ func (f *DefaultFormatter) StartAnalysis(src string) { } f.svc.SetAnalysisError(f.execEngineAndParseResults(src), tools.HorusecEngine, src) f.svc.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.HorusecEngine, f.language) - f.svc.SetToolFinishedAnalysis() } func (f *DefaultFormatter) execEngineAndParseResults(src string) error { diff --git a/internal/services/formatters/default_engine_formatter_test.go b/internal/services/formatters/default_engine_formatter_test.go index 73f78d4e5..41316718a 100644 --- a/internal/services/formatters/default_engine_formatter_test.go +++ b/internal/services/formatters/default_engine_formatter_test.go @@ -29,8 +29,9 @@ import ( "github.com/ZupIT/horusec/internal/services/formatters/swift/horusecswift" "github.com/ZupIT/horusec/internal/services/formatters/yaml/horuseckubernetes" - "github.com/ZupIT/horusec/internal/services/formatters/dart/horusecdart" "github.com/stretchr/testify/assert" + + "github.com/ZupIT/horusec/internal/services/formatters/dart/horusecdart" ) func TestStartAnalysis(t *testing.T) { diff --git a/internal/services/formatters/elixir/mixaudit/formatter.go b/internal/services/formatters/elixir/mixaudit/formatter.go index 7e127f301..3c19861ed 100644 --- a/internal/services/formatters/elixir/mixaudit/formatter.go +++ b/internal/services/formatters/elixir/mixaudit/formatter.go @@ -50,7 +50,6 @@ func (f *Formatter) StartAnalysis(projectSubPath string) { f.SetAnalysisError(f.startMixAudit(projectSubPath), tools.MixAudit, projectSubPath) f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.MixAudit, languages.Elixir) - f.SetToolFinishedAnalysis() } func (f *Formatter) startMixAudit(projectSubPath string) error { diff --git a/internal/services/formatters/elixir/mixaudit/formatter_test.go b/internal/services/formatters/elixir/mixaudit/formatter_test.go index 3daf33e96..f30b9840d 100644 --- a/internal/services/formatters/elixir/mixaudit/formatter_test.go +++ b/internal/services/formatters/elixir/mixaudit/formatter_test.go @@ -20,8 +20,6 @@ import ( "github.com/ZupIT/horusec/internal/entities/toolsconfig" - "github.com/ZupIT/horusec/internal/entities/monitor" - "github.com/stretchr/testify/assert" "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" @@ -46,7 +44,7 @@ func TestStartCFlawfinder(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") @@ -65,7 +63,7 @@ func TestStartCFlawfinder(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config) formatter := NewFormatter(service) assert.NotPanics(t, func() { @@ -81,7 +79,7 @@ func TestStartCFlawfinder(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", errors.New("test")) - service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config) formatter := NewFormatter(service) assert.NotPanics(t, func() { @@ -96,7 +94,7 @@ func TestStartCFlawfinder(t *testing.T) { config.SetWorkDir(&workdir.WorkDir{}) config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{MixAudit: toolsconfig.ToolConfig{IsToIgnore: true}}) - service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") diff --git a/internal/services/formatters/elixir/sobelow/formatter.go b/internal/services/formatters/elixir/sobelow/formatter.go index 370f67c1d..9f86e1b56 100644 --- a/internal/services/formatters/elixir/sobelow/formatter.go +++ b/internal/services/formatters/elixir/sobelow/formatter.go @@ -55,7 +55,6 @@ func (f *Formatter) StartAnalysis(projectSubPath string) { f.SetAnalysisError(f.startSobelow(projectSubPath), tools.Sobelow, projectSubPath) f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.Sobelow, languages.Elixir) - f.SetToolFinishedAnalysis() } func (f *Formatter) startSobelow(projectSubPath string) error { diff --git a/internal/services/formatters/elixir/sobelow/formatter_test.go b/internal/services/formatters/elixir/sobelow/formatter_test.go index 2f40eb4bd..8b8bcf3e8 100644 --- a/internal/services/formatters/elixir/sobelow/formatter_test.go +++ b/internal/services/formatters/elixir/sobelow/formatter_test.go @@ -20,8 +20,6 @@ import ( "github.com/ZupIT/horusec/internal/entities/toolsconfig" - "github.com/ZupIT/horusec/internal/entities/monitor" - "github.com/stretchr/testify/assert" "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" @@ -55,7 +53,7 @@ func TestStartCFlawfinder(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") @@ -74,7 +72,7 @@ func TestStartCFlawfinder(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) assert.NotPanics(t, func() { @@ -90,7 +88,7 @@ func TestStartCFlawfinder(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", errors.New("test")) - service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config) formatter := NewFormatter(service) assert.NotPanics(t, func() { @@ -105,7 +103,7 @@ func TestStartCFlawfinder(t *testing.T) { config.SetWorkDir(&workdir.WorkDir{}) config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{Sobelow: toolsconfig.ToolConfig{IsToIgnore: true}}) - service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") diff --git a/internal/services/formatters/generic/dependency_check/config.go b/internal/services/formatters/generic/dependency_check/config.go new file mode 100644 index 000000000..ea7c2f5b9 --- /dev/null +++ b/internal/services/formatters/generic/dependency_check/config.go @@ -0,0 +1,22 @@ +// 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 dependencycheck + +//nolint:lll // docker cmd config +const CMD = ` + {{WORK_DIR}} + /bin/dependency-check/bin/dependency-check.sh --scan /src --format JSON --out /tmp/result-ANALYSISID.json >> /tmp/output-ANALYSISID.txt + cat /tmp/result-ANALYSISID.json + ` diff --git a/internal/services/formatters/generic/dependency_check/entities/analysis.go b/internal/services/formatters/generic/dependency_check/entities/analysis.go new file mode 100644 index 000000000..bc4a75087 --- /dev/null +++ b/internal/services/formatters/generic/dependency_check/entities/analysis.go @@ -0,0 +1,19 @@ +// 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 entities + +type Analysis struct { + Dependencies []*Dependency `json:"dependencies"` +} diff --git a/internal/services/formatters/generic/dependency_check/entities/dependency.go b/internal/services/formatters/generic/dependency_check/entities/dependency.go new file mode 100644 index 000000000..cea5fe6e1 --- /dev/null +++ b/internal/services/formatters/generic/dependency_check/entities/dependency.go @@ -0,0 +1,48 @@ +// 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 entities + +import ( + "strings" +) + +type Dependency struct { + FileName string `json:"fileName"` + FilePath string `json:"filePath"` + Vulnerabilities []*Vulnerability `json:"vulnerabilities"` +} + +func (d *Dependency) GetVulnerability() *Vulnerability { + for _, vulnerability := range d.Vulnerabilities { + if strings.Contains(vulnerability.Name, "CWE") { + return vulnerability + } + } + + if len(d.Vulnerabilities) >= 1 { + return d.Vulnerabilities[0] + } + + return nil +} + +func (d *Dependency) GetFile() string { + index := strings.Index(d.FilePath, "?") + if index < 0 { + return d.FilePath + } + + return d.FilePath[:index] +} diff --git a/internal/services/formatters/generic/dependency_check/entities/dependency_test.go b/internal/services/formatters/generic/dependency_check/entities/dependency_test.go new file mode 100644 index 000000000..48185e65b --- /dev/null +++ b/internal/services/formatters/generic/dependency_check/entities/dependency_test.go @@ -0,0 +1,83 @@ +// 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 entities + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetVulnerability(t *testing.T) { + t.Run("should success get vulnerability without cwe", func(t *testing.T) { + dependency := &Dependency{ + FileName: "test", + FilePath: "test", + Vulnerabilities: []*Vulnerability{ + { + Description: "test", + Severity: "test", + Name: "test", + }, + }, + } + + assert.NotNil(t, dependency.GetVulnerability()) + }) + + t.Run("should success get vulnerability with cwe", func(t *testing.T) { + dependency := &Dependency{ + FileName: "test", + FilePath: "test", + Vulnerabilities: []*Vulnerability{ + { + Description: "test", + Severity: "test", + Name: "CWE test", + }, + }, + } + + assert.NotNil(t, dependency.GetVulnerability()) + }) + + t.Run("should return nil when do not contains vulnerability", func(t *testing.T) { + dependency := &Dependency{} + + assert.Nil(t, dependency.GetVulnerability()) + }) +} + +func TestGetFile(t *testing.T) { + t.Run("should success get file", func(t *testing.T) { + dependency := &Dependency{ + FilePath: "test?test", + } + + file := dependency.GetFile() + assert.NotEmpty(t, file) + assert.Equal(t, "test", file) + }) + + t.Run("should success get file", func(t *testing.T) { + dependency := &Dependency{ + FilePath: "test2", + } + + file := dependency.GetFile() + assert.NotEmpty(t, file) + assert.Equal(t, "test2", file) + }) +} diff --git a/internal/services/formatters/generic/dependency_check/entities/vulnerability.go b/internal/services/formatters/generic/dependency_check/entities/vulnerability.go new file mode 100644 index 000000000..e04d3c7ac --- /dev/null +++ b/internal/services/formatters/generic/dependency_check/entities/vulnerability.go @@ -0,0 +1,52 @@ +// 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 entities + +import ( + "strings" + + "github.com/ZupIT/horusec-devkit/pkg/enums/severities" +) + +const ( + Highest = "highest" + Critical = "critical" + High = "high" + Moderate = "moderate" + Medium = "medium" + Low = "low" +) + +type Vulnerability struct { + Description string `json:"description"` + Severity string `json:"severity"` + Name string `json:"name"` +} + +//nolint:funlen // need to be bigger than 10 +func (v *Vulnerability) GetSeverity() severities.Severity { + switch strings.ToLower(v.Severity) { + case Highest, Critical: + return severities.Critical + case High: + return severities.High + case Moderate, Medium: + return severities.Medium + case Low: + return severities.Low + default: + return severities.Unknown + } +} diff --git a/internal/services/formatters/generic/dependency_check/entities/vulnerability_test.go b/internal/services/formatters/generic/dependency_check/entities/vulnerability_test.go new file mode 100644 index 000000000..f5c0c054c --- /dev/null +++ b/internal/services/formatters/generic/dependency_check/entities/vulnerability_test.go @@ -0,0 +1,50 @@ +// 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 entities + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/ZupIT/horusec-devkit/pkg/enums/severities" +) + +func TestGetSeverity(t *testing.T) { + t.Run("should success severity", func(t *testing.T) { + vuln := &Vulnerability{} + + vuln.Severity = "highest" + assert.Equal(t, severities.Critical, vuln.GetSeverity()) + + vuln.Severity = "critical" + assert.Equal(t, severities.Critical, vuln.GetSeverity()) + + vuln.Severity = "high" + assert.Equal(t, severities.High, vuln.GetSeverity()) + + vuln.Severity = "moderate" + assert.Equal(t, severities.Medium, vuln.GetSeverity()) + + vuln.Severity = "medium" + assert.Equal(t, severities.Medium, vuln.GetSeverity()) + + vuln.Severity = "low" + assert.Equal(t, severities.Low, vuln.GetSeverity()) + + vuln.Severity = "test" + assert.Equal(t, severities.Unknown, vuln.GetSeverity()) + }) +} diff --git a/internal/services/formatters/generic/dependency_check/formatter.go b/internal/services/formatters/generic/dependency_check/formatter.go new file mode 100644 index 000000000..381db8ad3 --- /dev/null +++ b/internal/services/formatters/generic/dependency_check/formatter.go @@ -0,0 +1,116 @@ +// 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 dependencycheck + +import ( + "encoding/json" + "strings" + + "github.com/ZupIT/horusec-devkit/pkg/entities/vulnerability" + "github.com/ZupIT/horusec-devkit/pkg/enums/languages" + "github.com/ZupIT/horusec-devkit/pkg/enums/tools" + "github.com/ZupIT/horusec-devkit/pkg/utils/logger" + + dockerEntities "github.com/ZupIT/horusec/internal/entities/docker" + "github.com/ZupIT/horusec/internal/enums/images" + "github.com/ZupIT/horusec/internal/helpers/messages" + "github.com/ZupIT/horusec/internal/services/formatters" + dependencyCheckEntities "github.com/ZupIT/horusec/internal/services/formatters/generic/dependency_check/entities" + vulnhash "github.com/ZupIT/horusec/internal/utils/vuln_hash" +) + +type Formatter struct { + formatters.IService +} + +func NewFormatter(service formatters.IService) formatters.IFormatter { + return &Formatter{ + service, + } +} + +func (f *Formatter) StartAnalysis(projectSubPath string) { + if f.ToolIsToIgnore(tools.OwaspDependencyCheck) || f.IsDockerDisabled() || f.IsOwaspDependencyCheckDisable() { + logger.LogDebugWithLevel(messages.MsgDebugToolIgnored + tools.OwaspDependencyCheck.ToString()) + return + } + + f.SetAnalysisError(f.startDependencyCheck(projectSubPath), tools.OwaspDependencyCheck, projectSubPath) + f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.OwaspDependencyCheck, languages.Generic) +} + +func (f *Formatter) startDependencyCheck(projectSubPath string) error { + f.LogDebugWithReplace(messages.MsgDebugToolStartAnalysis, tools.OwaspDependencyCheck, languages.Generic) + + output, err := f.ExecuteContainer(f.getConfigData(projectSubPath)) + if err != nil { + return err + } + + return f.parseOutput(output) +} + +func (f *Formatter) getConfigData(projectSubPath string) *dockerEntities.AnalysisData { + analysisData := &dockerEntities.AnalysisData{ + CMD: f.AddWorkDirInCmd(CMD, projectSubPath, tools.OwaspDependencyCheck), + Language: languages.Generic, + } + + return analysisData.SetData(f.GetCustomImageByLanguage(languages.Generic), images.Generic) +} + +func (f *Formatter) parseOutput(output string) error { + var analysis *dependencyCheckEntities.Analysis + + index := strings.Index(output, "{") + if index < 0 || output == "" { + return nil + } + + if err := json.Unmarshal([]byte(output[index:]), &analysis); err != nil { + return err + } + + f.parseToVulnerability(analysis) + return nil +} + +func (f *Formatter) parseToVulnerability(analysis *dependencyCheckEntities.Analysis) { + for _, dependency := range analysis.Dependencies { + vulnData := dependency.GetVulnerability() + if vulnData == nil { + continue + } + + f.AddNewVulnerabilityIntoAnalysis(f.setVulnerabilityData(vulnData, dependency)) + } +} + +func (f *Formatter) setVulnerabilityData(vulnData *dependencyCheckEntities.Vulnerability, + dependency *dependencyCheckEntities.Dependency) *vulnerability.Vulnerability { + vuln := f.getDefaultVulnerabilitySeverity() + vuln.Severity = vulnData.GetSeverity() + vuln.Details = vulnData.Description + vuln.Code = f.GetCodeWithMaxCharacters(dependency.FileName, 0) + vuln.File = f.RemoveSrcFolderFromPath(dependency.GetFile()) + return vulnhash.Bind(vuln) +} + +func (f *Formatter) getDefaultVulnerabilitySeverity() *vulnerability.Vulnerability { + vulnerabilitySeverity := &vulnerability.Vulnerability{} + vulnerabilitySeverity.SecurityTool = tools.OwaspDependencyCheck + vulnerabilitySeverity.Language = languages.Generic + return vulnerabilitySeverity +} diff --git a/internal/services/formatters/generic/dependency_check/formatter_test.go b/internal/services/formatters/generic/dependency_check/formatter_test.go new file mode 100644 index 000000000..8fb096daa --- /dev/null +++ b/internal/services/formatters/generic/dependency_check/formatter_test.go @@ -0,0 +1,143 @@ +// 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 dependencycheck + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" + + entitiesAnalysis "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" + + cliConfig "github.com/ZupIT/horusec/config" + "github.com/ZupIT/horusec/internal/entities/toolsconfig" + "github.com/ZupIT/horusec/internal/entities/workdir" + "github.com/ZupIT/horusec/internal/services/docker" + "github.com/ZupIT/horusec/internal/services/formatters" +) + +const output = "{ \"dependencies\":[ { \"isVirtual\":false, \"fileName\":\"app.js\", \"filePath\":\"\\/src\\/app.js" + + "\", \"md5\":\"d3bf3a62b0f02ffaa11516d7f1aa7c83\", \"sha1\":\"eac9ad35db19d0f30eee6f4704e57634ea4f6f9c\", \"sh" + + "a256\":\"39bf0d903ffaef9ecddbdb40a3658a2bd5a3fdb1741ccd58a6cf58be841ec692\", \"evidenceCollected\":{ \"vendor" + + "Evidence\":[ ], \"productEvidence\":[ ], \"versionEvidence\":[ ] } }, { \"isVirtual\":true, \"fileName\":\"co" + + "okie-signature:1.0.3\", \"filePath\":\"\\/src\\/package-lock.json?cookie-signature\", \"projectReferences\":[" + + " \"package-lock.json: transitive\" ], \"evidenceCollected\":{ \"vendorEvidence\":[ { \"type\":\"vendor\", \"c" + + "onfidence\":\"HIGH\", \"source\":\"package.json\", \"name\":\"name\", \"value\":\"cookie-signature\" } ], \"p" + + "roductEvidence\":[ { \"type\":\"product\", \"confidence\":\"HIGHEST\", \"source\":\"package.json\", \"name\"" + + ":\"name\", \"value\":\"cookie-signature\" } ], \"versionEvidence\":[ { \"type\":\"version\", \"confidence\"" + + ":\"HIGHEST\", \"source\":\"package.json\", \"name\":\"version\", \"value\":\"1.0.3\" } ] }, \"packages\":[ { " + + "\"id\":\"pkg:npm\\/cookie-signature@1.0.3\", \"confidence\":\"HIGHEST\", \"url\":\"https:\\/\\/ossindex.sona" + + "type.org\\/component\\/pkg:npm\\/cookie-signature@1.0.3?utm_source=dependency-check&utm_medium=integration&ut" + + "m_content=6.2.2\" } ], \"vulnerabilities\":[ { \"source\":\"NPM\", \"name\":\"134\", \"unscored\":\"true\"," + + " \"severity\":\"moderate\", \"cwes\":[ ], \"description\":\"Affected versions of `cookie-signature` are vul" + + "nerable to timing attacks as a result of using a fail-early comparison instead of a constant-time comparison" + + ". \\n\\nTiming attacks remove the exponential increase in entropy gained from increased secret length, by pr" + + "oviding per-character feedback on the correctness of a guess via miniscule timing differences.\\n\\nUnder fav" + + "orable network conditions, an attacker can exploit this to guess the secret in no more than `charset*length` " + + "guesses, instead of `charset^length` guesses required were the timing attack not present. \\n\", \"notes\":\"" + + "\", \"references\":[ { \"source\":\"Advisory 134: Timing Attack\", \"name\":\"- [Commit #3979108](https:\\/\\" + + "/github.com\\/tj\\/node-cookie-signature\\/commit\\/39791081692e9e14aa62855369e1c7f80fbfd50e)\" } ], \"vulnera" + + "bleSoftware\":[ { \"software\":{ \"id\":\"cpe:2.3:a:*:cookie-signature:\\\\<\\\\=1.0.5:*:*:*:*:*:*:*\" } } ]" + + " }, { \"source\":\"OSSINDEX\", \"name\":\"CWE-208: Information Exposure Through Timing Discrepancy\", \"unsc" + + "ored\":\"true\", \"severity\":\"Unknown\", \"cwes\":[ \"CWE-208\" ], \"description\":\"Two separate operatio" + + "ns in a product require different amounts of time to complete, in a way that is observable to an actor and " + + "reveals security-relevant information about the state of the product, such as whether a particular operation" + + " was successful or not.\", \"notes\":\"\", \"references\":[ { \"source\":\"OSSINDEX\", \"url\":\"https:\\/\\" + + "/ossindex.sonatype.org\\/vulnerability\\/bf671e3a-9d6a-4d46-a724-01b92b80e7a3?component-type=npm&component-n" + + "ame=cookie-signature&utm_source=dependency-check&utm_medium=integration&utm_content=6.2.2\", \"name\":\"CWE-" + + "208: Information Exposure Through Timing Discrepancy\" } ], \"vulnerableSoftware\":[ { \"software\":{ \"id\"" + + ":\"cpe:2.3:a:*:cookie-signature:1.0.3:*:*:*:*:*:*:*\", \"vulnerabilityIdMatched\":\"true\" } } ] } ] } ] }" + +func TestStartGenericOwaspDependencyCheck(t *testing.T) { + t.Run("should success execute container and process output", func(t *testing.T) { + dockerAPIControllerMock := &docker.Mock{} + analysis := &entitiesAnalysis.Analysis{} + config := &cliConfig.Config{} + config.SetWorkDir(&workdir.WorkDir{}) + + config.SetEnableOwaspDependencyCheck(true) + dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) + + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) + formatter := NewFormatter(service) + + formatter.StartAnalysis("") + + assert.NotEmpty(t, analysis) + assert.Len(t, analysis.AnalysisVulnerabilities, 1) + }) + + t.Run("should return error when invalid output", func(t *testing.T) { + dockerAPIControllerMock := &docker.Mock{} + analysis := &entitiesAnalysis.Analysis{} + config := &cliConfig.Config{} + config.SetWorkDir(&workdir.WorkDir{}) + + dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("{", nil) + + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) + formatter := NewFormatter(service) + + assert.NotPanics(t, func() { + formatter.StartAnalysis("") + }) + }) + + t.Run("should return error when executing container", func(t *testing.T) { + dockerAPIControllerMock := &docker.Mock{} + analysis := &entitiesAnalysis.Analysis{} + config := &cliConfig.Config{} + config.SetWorkDir(&workdir.WorkDir{}) + + dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", errors.New("test")) + + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) + formatter := NewFormatter(service) + + assert.NotPanics(t, func() { + formatter.StartAnalysis("") + }) + }) + + t.Run("should return nil when empty output", func(t *testing.T) { + dockerAPIControllerMock := &docker.Mock{} + analysis := &entitiesAnalysis.Analysis{} + config := &cliConfig.Config{} + config.SetWorkDir(&workdir.WorkDir{}) + + dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", nil) + + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) + formatter := NewFormatter(service) + + assert.NotPanics(t, func() { + formatter.StartAnalysis("") + }) + }) + + t.Run("should not execute tool because it's ignored", func(t *testing.T) { + analysis := &entitiesAnalysis.Analysis{} + dockerAPIControllerMock := &docker.Mock{} + config := &cliConfig.Config{} + config.SetWorkDir(&workdir.WorkDir{}) + config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{OwaspDependencyCheck: toolsconfig.ToolConfig{IsToIgnore: true}}) + + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) + formatter := NewFormatter(service) + + formatter.StartAnalysis("") + }) +} diff --git a/internal/services/formatters/generic/deployments/.semver.yaml b/internal/services/formatters/generic/deployments/.semver.yaml index 36a2accc3..1443f2773 100644 --- a/internal/services/formatters/generic/deployments/.semver.yaml +++ b/internal/services/formatters/generic/deployments/.semver.yaml @@ -1,4 +1,4 @@ alpha: 0 beta: 0 rc: 0 -release: v1.0.0 +release: v1.0.1 diff --git a/internal/services/formatters/generic/deployments/Dockerfile b/internal/services/formatters/generic/deployments/Dockerfile index 02b3304aa..06ae720c1 100644 --- a/internal/services/formatters/generic/deployments/Dockerfile +++ b/internal/services/formatters/generic/deployments/Dockerfile @@ -12,6 +12,25 @@ # See the License for the specific language governing permissions and # limitations under the License. +FROM azul/zulu-openjdk-alpine:14 AS jlink + +RUN "$JAVA_HOME/bin/jlink" --compress=2 \ + --module-path /opt/java/openjdk/jmods \ + --add-modules java.base,java.compiler,java.datatransfer,jdk.crypto.ec,java.desktop,java.instrument,java.logging,java.management,java.naming,java.rmi,java.scripting,java.security.sasl,java.sql,java.transaction.xa,java.xml,jdk.unsupported \ + --output /jlinked + FROM python:alpine RUN pip install semgrep + +COPY --from=jlink /jlinked /opt/jdk/ + +ENV JAVA_HOME=/opt/jdk + +RUN apk update && apk add curl + +RUN curl -o /bin/dependency-check-6.2.2-release.zip -LO https://github.com/jeremylong/DependencyCheck/releases/download/v6.2.2/dependency-check-6.2.2-release.zip + +RUN unzip /bin/dependency-check-6.2.2-release.zip -d /bin + +RUN rm /bin/dependency-check-6.2.2-release.zip diff --git a/internal/services/formatters/generic/semgrep/formatter.go b/internal/services/formatters/generic/semgrep/formatter.go index 41730730c..ce992fab3 100644 --- a/internal/services/formatters/generic/semgrep/formatter.go +++ b/internal/services/formatters/generic/semgrep/formatter.go @@ -52,7 +52,6 @@ func (f *Formatter) StartAnalysis(projectSubPath string) { f.SetAnalysisError(f.startSemgrep(projectSubPath), tools.SecurityCodeScan, projectSubPath) f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.Semgrep, languages.Generic) - f.SetToolFinishedAnalysis() } func (f *Formatter) startSemgrep(projectSubPath string) error { diff --git a/internal/services/formatters/generic/semgrep/formatter_test.go b/internal/services/formatters/generic/semgrep/formatter_test.go index bcbfd155b..85d1e3ba7 100644 --- a/internal/services/formatters/generic/semgrep/formatter_test.go +++ b/internal/services/formatters/generic/semgrep/formatter_test.go @@ -21,7 +21,6 @@ import ( "github.com/ZupIT/horusec/internal/entities/toolsconfig" entitiesAnalysis "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" - "github.com/ZupIT/horusec/internal/entities/monitor" "github.com/stretchr/testify/assert" @@ -48,7 +47,7 @@ func TestParseOutput(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") @@ -71,7 +70,7 @@ func TestParseOutput(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") @@ -94,7 +93,7 @@ func TestParseOutput(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") @@ -112,7 +111,7 @@ func TestParseOutput(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") @@ -128,7 +127,7 @@ func TestParseOutput(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", errors.New("test")) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") @@ -142,7 +141,7 @@ func TestParseOutput(t *testing.T) { config.SetWorkDir(&workdir.WorkDir{}) config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{Semgrep: toolsconfig.ToolConfig{IsToIgnore: true}}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") diff --git a/internal/services/formatters/go/gosec/formatter.go b/internal/services/formatters/go/gosec/formatter.go index ecb8ebf9e..7f9fa774c 100644 --- a/internal/services/formatters/go/gosec/formatter.go +++ b/internal/services/formatters/go/gosec/formatter.go @@ -50,7 +50,6 @@ func (f *Formatter) StartAnalysis(projectSubPath string) { f.SetAnalysisError(f.startGoSec(projectSubPath), tools.GoSec, projectSubPath) f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.GoSec, languages.Go) - f.SetToolFinishedAnalysis() } func (f *Formatter) startGoSec(projectSubPath string) error { diff --git a/internal/services/formatters/go/gosec/formatter_test.go b/internal/services/formatters/go/gosec/formatter_test.go index d5972abe9..7a5e97e4e 100644 --- a/internal/services/formatters/go/gosec/formatter_test.go +++ b/internal/services/formatters/go/gosec/formatter_test.go @@ -20,8 +20,6 @@ import ( "github.com/ZupIT/horusec/internal/entities/toolsconfig" - "github.com/ZupIT/horusec/internal/entities/monitor" - "github.com/stretchr/testify/assert" "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" @@ -40,7 +38,7 @@ func TestGoLang_StartAnalysis(t *testing.T) { config := &cliConfig.Config{} config.SetWorkDir(&workdir.WorkDir{}) - service := formatters.NewFormatterService(&analysis.Analysis{}, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(&analysis.Analysis{}, dockerAPIControllerMock, config) golangAnalyzer := NewFormatter(service) @@ -72,7 +70,7 @@ func TestGoLang_StartAnalysis(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(outputAnalysis, nil) - service := formatters.NewFormatterService(&analysis.Analysis{}, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(&analysis.Analysis{}, dockerAPIControllerMock, config) golangAnalyzer := NewFormatter(service) @@ -89,7 +87,7 @@ func TestGoLang_StartAnalysis(t *testing.T) { config := &cliConfig.Config{} config.SetWorkDir(&workdir.WorkDir{}) - service := formatters.NewFormatterService(&analysis.Analysis{}, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(&analysis.Analysis{}, dockerAPIControllerMock, config) golangAnalyzer := NewFormatter(service) @@ -108,7 +106,7 @@ func TestGoLang_StartAnalysis(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(outputAnalysis, nil) - service := formatters.NewFormatterService(&analysis.Analysis{}, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(&analysis.Analysis{}, dockerAPIControllerMock, config) golangAnalyzer := NewFormatter(service) @@ -124,7 +122,7 @@ func TestGoLang_StartAnalysis(t *testing.T) { config.SetWorkDir(&workdir.WorkDir{}) config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{GoSec: toolsconfig.ToolConfig{IsToIgnore: true}}) - service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") diff --git a/internal/services/formatters/hcl/formatter.go b/internal/services/formatters/hcl/formatter.go index 858193610..f7283655c 100644 --- a/internal/services/formatters/hcl/formatter.go +++ b/internal/services/formatters/hcl/formatter.go @@ -51,7 +51,6 @@ func (f *Formatter) StartAnalysis(projectSubPath string) { f.SetAnalysisError(f.startTfSec(projectSubPath), tools.TfSec, projectSubPath) f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.TfSec, languages.HCL) - f.SetToolFinishedAnalysis() } func (f *Formatter) startTfSec(projectSubPath string) error { diff --git a/internal/services/formatters/hcl/formatter_test.go b/internal/services/formatters/hcl/formatter_test.go index 0163ce20b..d1f586615 100644 --- a/internal/services/formatters/hcl/formatter_test.go +++ b/internal/services/formatters/hcl/formatter_test.go @@ -21,7 +21,6 @@ import ( "github.com/ZupIT/horusec/internal/entities/toolsconfig" entitiesAnalysis "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" - "github.com/ZupIT/horusec/internal/entities/monitor" "github.com/stretchr/testify/assert" @@ -42,7 +41,7 @@ func TestStartHCLTfSec(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") @@ -61,7 +60,7 @@ func TestStartHCLTfSec(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) assert.NotPanics(t, func() { @@ -77,7 +76,7 @@ func TestStartHCLTfSec(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", errors.New("test")) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) assert.NotPanics(t, func() { @@ -90,7 +89,7 @@ func TestStartHCLTfSec(t *testing.T) { config := &cliConfig.Config{} config.SetWorkDir(&workdir.WorkDir{}) config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{TfSec: toolsconfig.ToolConfig{IsToIgnore: true}}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") diff --git a/internal/services/formatters/interface.go b/internal/services/formatters/interface.go index b07e78250..9b7e96f43 100644 --- a/internal/services/formatters/interface.go +++ b/internal/services/formatters/interface.go @@ -22,7 +22,6 @@ import ( engine "github.com/ZupIT/horusec-engine" commitAuthor "github.com/ZupIT/horusec/internal/entities/commit_author" dockerEntities "github.com/ZupIT/horusec/internal/entities/docker" - "github.com/ZupIT/horusec/internal/entities/monitor" "github.com/ZupIT/horusec/internal/entities/toolsconfig" ) @@ -40,9 +39,7 @@ type IService interface { GetConfigProjectPath() string GetToolsConfig() toolsconfig.MapToolConfig GetAnalysis() *entitiesAnalysis.Analysis - SetToolFinishedAnalysis() SetAnalysisError(err error, tool tools.Tool, projectSubPath string) - SetMonitor(monitor *monitor.Monitor) RemoveSrcFolderFromPath(filepath string) string GetCodeWithMaxCharacters(code string, column int) string ToolIsToIgnore(tool tools.Tool) bool @@ -55,4 +52,5 @@ type IService interface { GetCustomRulesByLanguage(lang languages.Language) []engine.Rule GetConfigCMDByFileExtension(projectSubPath, imageCmd, ext string, tool tools.Tool) string GetCustomImageByLanguage(language languages.Language) string + IsOwaspDependencyCheckDisable() bool } diff --git a/internal/services/formatters/javascript/npmaudit/formatter.go b/internal/services/formatters/javascript/npmaudit/formatter.go index 2b5c12958..736b246b1 100644 --- a/internal/services/formatters/javascript/npmaudit/formatter.go +++ b/internal/services/formatters/javascript/npmaudit/formatter.go @@ -54,7 +54,6 @@ func (f *Formatter) StartAnalysis(projectSubPath string) { f.SetAnalysisError(f.startNpmAudit(projectSubPath), tools.NpmAudit, projectSubPath) f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.NpmAudit, languages.Javascript) - f.SetToolFinishedAnalysis() } func (f *Formatter) startNpmAudit(projectSubPath string) error { diff --git a/internal/services/formatters/javascript/npmaudit/formatter_test.go b/internal/services/formatters/javascript/npmaudit/formatter_test.go index fc047a5d2..eea98646d 100644 --- a/internal/services/formatters/javascript/npmaudit/formatter_test.go +++ b/internal/services/formatters/javascript/npmaudit/formatter_test.go @@ -21,7 +21,6 @@ import ( "github.com/ZupIT/horusec/internal/entities/toolsconfig" entitiesAnalysis "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" - "github.com/ZupIT/horusec/internal/entities/monitor" "github.com/stretchr/testify/assert" @@ -44,7 +43,7 @@ func TestStartNpmAudit(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -63,7 +62,7 @@ func TestStartNpmAudit(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -82,7 +81,7 @@ func TestStartNpmAudit(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -102,7 +101,7 @@ func TestStartNpmAudit(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -119,7 +118,7 @@ func TestStartNpmAudit(t *testing.T) { config := &cliConfig.Config{} config.SetWorkDir(&workdir.WorkDir{}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -131,7 +130,7 @@ func TestStartNpmAudit(t *testing.T) { dockerAPIControllerMock := &docker.Mock{} config := &cliConfig.Config{} config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{NpmAudit: toolsconfig.ToolConfig{IsToIgnore: true}}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") @@ -147,7 +146,7 @@ func TestParseOutputNpm(t *testing.T) { config := &cliConfig.Config{} config.SetWorkDir(&workdir.WorkDir{}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := Formatter{ service, diff --git a/internal/services/formatters/javascript/yarnaudit/formatter.go b/internal/services/formatters/javascript/yarnaudit/formatter.go index 788890460..73012153c 100644 --- a/internal/services/formatters/javascript/yarnaudit/formatter.go +++ b/internal/services/formatters/javascript/yarnaudit/formatter.go @@ -54,7 +54,6 @@ func (f *Formatter) StartAnalysis(projectSubPath string) { f.SetAnalysisError(f.startYarnAudit(projectSubPath), tools.YarnAudit, projectSubPath) f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.YarnAudit, languages.Javascript) - f.SetToolFinishedAnalysis() } func (f *Formatter) startYarnAudit(projectSubPath string) error { diff --git a/internal/services/formatters/javascript/yarnaudit/formatter_test.go b/internal/services/formatters/javascript/yarnaudit/formatter_test.go index 3191bf8d7..5cfd753ad 100644 --- a/internal/services/formatters/javascript/yarnaudit/formatter_test.go +++ b/internal/services/formatters/javascript/yarnaudit/formatter_test.go @@ -21,7 +21,6 @@ import ( "github.com/ZupIT/horusec/internal/entities/toolsconfig" entitiesAnalysis "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" - "github.com/ZupIT/horusec/internal/entities/monitor" "github.com/stretchr/testify/assert" @@ -44,7 +43,7 @@ func TestParseOutputYarn(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -64,7 +63,7 @@ func TestParseOutputYarn(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -84,7 +83,7 @@ func TestParseOutputYarn(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -104,7 +103,7 @@ func TestParseOutputYarn(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -121,7 +120,7 @@ func TestParseOutputYarn(t *testing.T) { config := &cliConfig.Config{} config.SetWorkDir(&workdir.WorkDir{}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -134,7 +133,7 @@ func TestParseOutputYarn(t *testing.T) { config := &cliConfig.Config{} config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{YarnAudit: toolsconfig.ToolConfig{IsToIgnore: true}}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") @@ -150,7 +149,7 @@ func TestParseOutputNpm(t *testing.T) { config := &cliConfig.Config{} config.SetWorkDir(&workdir.WorkDir{}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := Formatter{ service, diff --git a/internal/services/formatters/leaks/gitleaks/formatter.go b/internal/services/formatters/leaks/gitleaks/formatter.go index 5ff16cbc0..90d579d41 100644 --- a/internal/services/formatters/leaks/gitleaks/formatter.go +++ b/internal/services/formatters/leaks/gitleaks/formatter.go @@ -53,7 +53,6 @@ func (f *Formatter) StartAnalysis(projectSubPath string) { f.SetAnalysisError(f.startGitLeaks(projectSubPath), tools.GitLeaks, projectSubPath) f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.GitLeaks, languages.Leaks) - f.SetToolFinishedAnalysis() } func (f *Formatter) startGitLeaks(projectSubPath string) error { diff --git a/internal/services/formatters/leaks/gitleaks/formatter_test.go b/internal/services/formatters/leaks/gitleaks/formatter_test.go index db42e2409..213710132 100644 --- a/internal/services/formatters/leaks/gitleaks/formatter_test.go +++ b/internal/services/formatters/leaks/gitleaks/formatter_test.go @@ -23,7 +23,6 @@ import ( entitiesAnalysis "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" enumsAnalysis "github.com/ZupIT/horusec-devkit/pkg/enums/analysis" - "github.com/ZupIT/horusec/internal/entities/monitor" "github.com/google/uuid" "github.com/stretchr/testify/assert" @@ -69,7 +68,7 @@ func TestLeaks_StartAnalysis(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(outputAnalysis, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) leaksAnalyzer := NewFormatter(service) @@ -87,7 +86,7 @@ func TestLeaks_StartAnalysis(t *testing.T) { dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) leaksAnalyzer := NewFormatter(service) @@ -105,7 +104,7 @@ func TestLeaks_StartAnalysis(t *testing.T) { dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", errors.New("some error")) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) leaksAnalyzer := NewFormatter(service) @@ -126,7 +125,7 @@ func TestLeaks_StartAnalysis(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(outputAnalysis, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) leaksAnalyzer := NewFormatter(service) @@ -140,7 +139,7 @@ func TestLeaks_StartAnalysis(t *testing.T) { config := &cliConfig.Config{} config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{GitLeaks: toolsconfig.ToolConfig{IsToIgnore: true}}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") diff --git a/internal/services/formatters/php/phpcs/formatter.go b/internal/services/formatters/php/phpcs/formatter.go index 5b75eb42a..c2ead6151 100644 --- a/internal/services/formatters/php/phpcs/formatter.go +++ b/internal/services/formatters/php/phpcs/formatter.go @@ -51,7 +51,6 @@ func (f *Formatter) StartAnalysis(projectSubPath string) { f.SetAnalysisError(f.startPhpCs(projectSubPath), tools.PhpCS, projectSubPath) f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.PhpCS, languages.PHP) - f.SetToolFinishedAnalysis() } func (f *Formatter) startPhpCs(projectSubPath string) error { diff --git a/internal/services/formatters/php/phpcs/formatter_test.go b/internal/services/formatters/php/phpcs/formatter_test.go index 6cb1379a2..dc2b979f6 100644 --- a/internal/services/formatters/php/phpcs/formatter_test.go +++ b/internal/services/formatters/php/phpcs/formatter_test.go @@ -24,7 +24,6 @@ import ( entitiesAnalysis "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" cliConfig "github.com/ZupIT/horusec/config" - "github.com/ZupIT/horusec/internal/entities/monitor" "github.com/ZupIT/horusec/internal/entities/workdir" "github.com/ZupIT/horusec/internal/services/docker" "github.com/ZupIT/horusec/internal/services/formatters" @@ -41,7 +40,7 @@ func TestStartCFlawfinder(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") @@ -60,7 +59,7 @@ func TestStartCFlawfinder(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) assert.NotPanics(t, func() { @@ -76,7 +75,7 @@ func TestStartCFlawfinder(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", errors.New("test")) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) assert.NotPanics(t, func() { @@ -89,7 +88,7 @@ func TestStartCFlawfinder(t *testing.T) { config := &cliConfig.Config{} config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{PhpCS: toolsconfig.ToolConfig{IsToIgnore: true}}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") diff --git a/internal/services/formatters/python/bandit/formatter.go b/internal/services/formatters/python/bandit/formatter.go index 5d8b103da..72f77a87a 100644 --- a/internal/services/formatters/python/bandit/formatter.go +++ b/internal/services/formatters/python/bandit/formatter.go @@ -51,7 +51,6 @@ func (f *Formatter) StartAnalysis(projectSubPath string) { f.SetAnalysisError(f.startBandit(projectSubPath), tools.Bandit, projectSubPath) f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.Bandit, languages.Python) - f.SetToolFinishedAnalysis() } func (f *Formatter) startBandit(projectSubPath string) error { diff --git a/internal/services/formatters/python/bandit/formatter_test.go b/internal/services/formatters/python/bandit/formatter_test.go index 8ad2e7e1b..b7d7c47dc 100644 --- a/internal/services/formatters/python/bandit/formatter_test.go +++ b/internal/services/formatters/python/bandit/formatter_test.go @@ -23,7 +23,6 @@ import ( entitiesAnalysis "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" enumHorusec "github.com/ZupIT/horusec-devkit/pkg/enums/analysis" - "github.com/ZupIT/horusec/internal/entities/monitor" "github.com/google/uuid" "github.com/stretchr/testify/assert" @@ -50,7 +49,7 @@ func TestNewFormatter(t *testing.T) { config := &cliConfig.Config{} config.SetWorkDir(&workdir.WorkDir{}) - service := formatters.NewFormatterService(nil, nil, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(nil, nil, config) assert.IsType(t, NewFormatter(service), &Formatter{}) } @@ -66,7 +65,7 @@ func TestFormatter_StartSafety(t *testing.T) { dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", errors.New("Error")) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -86,7 +85,7 @@ func TestFormatter_StartSafety(t *testing.T) { dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -106,7 +105,7 @@ func TestFormatter_StartSafety(t *testing.T) { dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -126,7 +125,7 @@ func TestFormatter_StartSafety(t *testing.T) { dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -145,7 +144,7 @@ func TestFormatter_StartSafety(t *testing.T) { dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("some aleatory text", nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -159,7 +158,7 @@ func TestFormatter_StartSafety(t *testing.T) { config := &cliConfig.Config{} config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{Bandit: toolsconfig.ToolConfig{IsToIgnore: true}}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") diff --git a/internal/services/formatters/python/safety/formatter.go b/internal/services/formatters/python/safety/formatter.go index e0051787a..6e9258ed0 100644 --- a/internal/services/formatters/python/safety/formatter.go +++ b/internal/services/formatters/python/safety/formatter.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" "os" + "path/filepath" "strconv" "strings" @@ -57,7 +58,6 @@ func (f *Formatter) StartAnalysis(projectSubPath string) { f.SetAnalysisError(f.startSafety(projectSubPath), tools.Safety, projectSubPath) f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.Safety, languages.Python) - f.SetToolFinishedAnalysis() } func (f *Formatter) startSafety(projectSubPath string) error { @@ -124,7 +124,7 @@ func (f *Formatter) setupVulnerabilitiesSeveritiesSafety( } func (f *Formatter) getVulnerabilityLineByName(line, fileName string) string { - path := fmt.Sprintf("%s/%s", f.GetConfigProjectPath(), fileName) + path := filepath.Join(f.GetConfigProjectPath(), fileName) fileOpened, err := os.Open(path) if err != nil { return "-" diff --git a/internal/services/formatters/python/safety/formatter_test.go b/internal/services/formatters/python/safety/formatter_test.go index 5754ea078..af2954bf2 100644 --- a/internal/services/formatters/python/safety/formatter_test.go +++ b/internal/services/formatters/python/safety/formatter_test.go @@ -23,7 +23,6 @@ import ( entitiesAnalysis "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" enumHorusec "github.com/ZupIT/horusec-devkit/pkg/enums/analysis" - "github.com/ZupIT/horusec/internal/entities/monitor" "github.com/google/uuid" "github.com/stretchr/testify/assert" @@ -50,7 +49,7 @@ func TestNewFormatter(t *testing.T) { config := &cliConfig.Config{} config.SetWorkDir(&workdir.WorkDir{}) - service := formatters.NewFormatterService(nil, nil, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(nil, nil, config) assert.IsType(t, NewFormatter(service), &Formatter{}) } @@ -66,7 +65,7 @@ func TestFormatter_StartSafety(t *testing.T) { dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", errors.New("Error")) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -85,7 +84,7 @@ func TestFormatter_StartSafety(t *testing.T) { dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -104,7 +103,7 @@ func TestFormatter_StartSafety(t *testing.T) { dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -123,7 +122,7 @@ func TestFormatter_StartSafety(t *testing.T) { dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("some aleatory text", nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -137,7 +136,7 @@ func TestFormatter_StartSafety(t *testing.T) { config := &cliConfig.Config{} config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{Safety: toolsconfig.ToolConfig{IsToIgnore: true}}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") diff --git a/internal/services/formatters/ruby/brakeman/formatter.go b/internal/services/formatters/ruby/brakeman/formatter.go index 7fe7b2a9b..ccd4a1e01 100644 --- a/internal/services/formatters/ruby/brakeman/formatter.go +++ b/internal/services/formatters/ruby/brakeman/formatter.go @@ -51,7 +51,6 @@ func (f *Formatter) StartAnalysis(projectSubPath string) { f.SetAnalysisError(f.startBrakeman(projectSubPath), tools.Brakeman, projectSubPath) f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.Brakeman, languages.Ruby) - f.SetToolFinishedAnalysis() } func (f *Formatter) startBrakeman(projectSubPath string) error { diff --git a/internal/services/formatters/ruby/brakeman/formatter_test.go b/internal/services/formatters/ruby/brakeman/formatter_test.go index 2dc67427b..6931d4e6f 100644 --- a/internal/services/formatters/ruby/brakeman/formatter_test.go +++ b/internal/services/formatters/ruby/brakeman/formatter_test.go @@ -21,7 +21,6 @@ import ( "github.com/ZupIT/horusec/internal/entities/toolsconfig" entitiesAnalysis "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" - "github.com/ZupIT/horusec/internal/entities/monitor" "github.com/stretchr/testify/assert" @@ -45,7 +44,7 @@ func TestParseOutput(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -69,7 +68,7 @@ func TestParseOutput(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -93,7 +92,7 @@ func TestParseOutput(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -114,7 +113,7 @@ func TestParseOutput(t *testing.T) { dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("invalid output", nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -130,7 +129,7 @@ func TestParseOutput(t *testing.T) { config := &cliConfig.Config{} config.SetWorkDir(&workdir.WorkDir{}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -143,7 +142,7 @@ func TestParseOutput(t *testing.T) { config := &cliConfig.Config{} config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{Brakeman: toolsconfig.ToolConfig{IsToIgnore: true}}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") diff --git a/internal/services/formatters/ruby/bundler/formatter.go b/internal/services/formatters/ruby/bundler/formatter.go index 1ba75c34d..3765e7e06 100644 --- a/internal/services/formatters/ruby/bundler/formatter.go +++ b/internal/services/formatters/ruby/bundler/formatter.go @@ -56,7 +56,6 @@ func (f *Formatter) StartAnalysis(projectSubPath string) { f.SetAnalysisError(f.startBundlerAudit(projectSubPath), tools.BundlerAudit, projectSubPath) f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.BundlerAudit, languages.Ruby) - f.SetToolFinishedAnalysis() } func (f *Formatter) startBundlerAudit(projectSubPath string) error { diff --git a/internal/services/formatters/ruby/bundler/formatter_test.go b/internal/services/formatters/ruby/bundler/formatter_test.go index b96c67fea..60ac2ea21 100644 --- a/internal/services/formatters/ruby/bundler/formatter_test.go +++ b/internal/services/formatters/ruby/bundler/formatter_test.go @@ -21,7 +21,6 @@ import ( "github.com/ZupIT/horusec/internal/entities/toolsconfig" entitiesAnalysis "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" - "github.com/ZupIT/horusec/internal/entities/monitor" "github.com/stretchr/testify/assert" @@ -45,7 +44,7 @@ func TestParseOutput(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -66,7 +65,7 @@ func TestParseOutput(t *testing.T) { dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("invalid output", nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -84,7 +83,7 @@ func TestParseOutput(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer"). Return("No such file or directory Errno::ENOENT", nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -102,7 +101,7 @@ func TestParseOutput(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer"). Return("No vulnerabilities found", nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -118,7 +117,7 @@ func TestParseOutput(t *testing.T) { config := &cliConfig.Config{} config.SetWorkDir(&workdir.WorkDir{}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -131,7 +130,7 @@ func TestParseOutput(t *testing.T) { config := &cliConfig.Config{} config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{BundlerAudit: toolsconfig.ToolConfig{IsToIgnore: true}}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") diff --git a/internal/services/formatters/service.go b/internal/services/formatters/service.go index 215701b05..65875e1e0 100644 --- a/internal/services/formatters/service.go +++ b/internal/services/formatters/service.go @@ -20,12 +20,12 @@ import ( "path" "strconv" "strings" + "sync" - entitiesAnalysis "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" + "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" "github.com/ZupIT/horusec-devkit/pkg/entities/vulnerability" "github.com/ZupIT/horusec-devkit/pkg/enums/confidence" commitAuthor "github.com/ZupIT/horusec/internal/entities/commit_author" - "github.com/ZupIT/horusec/internal/entities/monitor" "github.com/ZupIT/horusec/internal/utils/file" vulnhash "github.com/ZupIT/horusec/internal/utils/vuln_hash" @@ -34,33 +34,32 @@ import ( "github.com/ZupIT/horusec-devkit/pkg/enums/tools" "github.com/ZupIT/horusec-devkit/pkg/utils/logger" engine "github.com/ZupIT/horusec-engine" - cliConfig "github.com/ZupIT/horusec/config" + "github.com/ZupIT/horusec/config" dockerEntities "github.com/ZupIT/horusec/internal/entities/docker" "github.com/ZupIT/horusec/internal/entities/toolsconfig" "github.com/ZupIT/horusec/internal/helpers/messages" customRules "github.com/ZupIT/horusec/internal/services/custom_rules" - dockerService "github.com/ZupIT/horusec/internal/services/docker" + "github.com/ZupIT/horusec/internal/services/docker" "github.com/ZupIT/horusec/internal/services/git" ) type Service struct { - analysis *entitiesAnalysis.Analysis - docker dockerService.Interface + mutex *sync.Mutex + analysis *analysis.Analysis + docker docker.Interface gitService git.IService - monitor *monitor.Monitor - config cliConfig.IConfig + config config.IConfig customRulesService customRules.IService } -func NewFormatterService(analysis *entitiesAnalysis.Analysis, docker dockerService.Interface, config cliConfig.IConfig, - monitorEntity *monitor.Monitor) IService { +func NewFormatterService(analysiss *analysis.Analysis, dockerSvc docker.Interface, cfg config.IConfig) IService { return &Service{ - analysis: analysis, - docker: docker, - gitService: git.NewGitService(config), - monitor: monitorEntity, - config: config, - customRulesService: customRules.NewCustomRulesService(config), + mutex: new(sync.Mutex), + analysis: analysiss, + docker: dockerSvc, + gitService: git.NewGitService(cfg), + config: cfg, + customRulesService: customRules.NewCustomRulesService(cfg), } } @@ -113,12 +112,14 @@ func (s *Service) GetAnalysisID() string { return s.analysis.GetIDString() } -func (s *Service) GetAnalysis() *entitiesAnalysis.Analysis { +func (s *Service) GetAnalysis() *analysis.Analysis { return s.analysis } func (s *Service) SetAnalysisError(err error, tool tools.Tool, projectSubPath string) { if err != nil { + s.mutex.Lock() + defer s.mutex.Unlock() s.addAnalysisError(err) msg := s.GetAnalysisIDErrorMessage(tool, "") if projectSubPath != "" { @@ -141,14 +142,6 @@ func (s *Service) addAnalysisError(err error) { } } -func (s *Service) SetToolFinishedAnalysis() { - s.monitor.RemoveProcess(1) -} - -func (s *Service) SetMonitor(monitorToSet *monitor.Monitor) { - s.monitor = monitorToSet -} - func (s *Service) RemoveSrcFolderFromPath(filepath string) string { if filepath == "" || len(filepath) <= 4 || !strings.Contains(filepath[:4], "src") { return filepath @@ -171,9 +164,8 @@ func (s *Service) GetCodeWithMaxCharacters(code string, column int) string { } func (s *Service) ToolIsToIgnore(tool tools.Tool) bool { - if s.config.GetToolsConfig()[tool].IsToIgnore { - s.SetToolFinishedAnalysis() - return true + if tool, exists := s.config.GetToolsConfig()[tool]; exists { + return tool.IsToIgnore } return false } @@ -241,7 +233,7 @@ func (s *Service) setVulnerabilityDataByFindings(findings []engine.Finding, inde func (s *Service) AddNewVulnerabilityIntoAnalysis(vuln *vulnerability.Vulnerability) { s.GetAnalysis().AnalysisVulnerabilities = append(s.GetAnalysis().AnalysisVulnerabilities, - entitiesAnalysis.AnalysisVulnerabilities{ + analysis.AnalysisVulnerabilities{ Vulnerability: *vuln, }) } @@ -267,12 +259,7 @@ func (s *Service) removeHorusecFolder(filepath string) string { } func (s *Service) IsDockerDisabled() bool { - isDisabled := s.config.GetDisableDocker() - if isDisabled { - s.SetToolFinishedAnalysis() - } - - return isDisabled + return s.config.GetDisableDocker() } func (s *Service) GetCustomRulesByLanguage(lang languages.Language) []engine.Rule { @@ -293,3 +280,7 @@ func (s *Service) GetConfigCMDByFileExtension(projectSubPath, imageCmd, ext stri func (s *Service) GetCustomImageByLanguage(language languages.Language) string { return s.config.GetCustomImages()[language.GetCustomImagesKeyByLanguage()] } + +func (s *Service) IsOwaspDependencyCheckDisable() bool { + return !s.config.GetEnableOwaspDependencyCheck() +} diff --git a/internal/services/formatters/service_mock.go b/internal/services/formatters/service_mock.go index e334c0efb..554824384 100644 --- a/internal/services/formatters/service_mock.go +++ b/internal/services/formatters/service_mock.go @@ -25,7 +25,6 @@ import ( engine "github.com/ZupIT/horusec-engine" commitAuthor "github.com/ZupIT/horusec/internal/entities/commit_author" dockerEntities "github.com/ZupIT/horusec/internal/entities/docker" - "github.com/ZupIT/horusec/internal/entities/monitor" "github.com/ZupIT/horusec/internal/entities/toolsconfig" ) @@ -72,18 +71,10 @@ func (m *Mock) GetAnalysis() *entitiesAnalysis.Analysis { return args.Get(0).(*entitiesAnalysis.Analysis) } -func (m *Mock) SetToolFinishedAnalysis() { - _ = m.MethodCalled("SetToolFinishedAnalysis") -} - func (m *Mock) SetAnalysisError(_ error, _ tools.Tool, _ string) { _ = m.MethodCalled("SetAnalysisError") } -func (m *Mock) SetMonitor(_ *monitor.Monitor) { - _ = m.MethodCalled("SetMonitor") -} - func (m *Mock) RemoveSrcFolderFromPath(_ string) string { args := m.MethodCalled("RemoveSrcFolderFromPath") return args.Get(0).(string) @@ -147,3 +138,8 @@ func (m *Mock) GetCustomImageByLanguage(_ languages.Language) string { args := m.MethodCalled("GetCustomImageByLanguage") return args.Get(0).(string) } + +func (m *Mock) IsOwaspDependencyCheckDisable() bool { + args := m.MethodCalled("IsOwaspDependencyCheckDisable") + return args.Get(0).(bool) +} diff --git a/internal/services/formatters/service_test.go b/internal/services/formatters/service_test.go index d3f875b45..6de5dcb47 100644 --- a/internal/services/formatters/service_test.go +++ b/internal/services/formatters/service_test.go @@ -25,7 +25,6 @@ import ( entitiesAnalysis "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" commitAuthor "github.com/ZupIT/horusec/internal/entities/commit_author" - "github.com/ZupIT/horusec/internal/entities/monitor" "github.com/google/uuid" "github.com/stretchr/testify/assert" @@ -63,7 +62,6 @@ func TestMock_AddWorkDirInCmd(t *testing.T) { _ = mock.GetConfigProjectPath() _ = mock.GetAnalysis() mock.SetAnalysisError(errors.New(""), "", "") - mock.SetMonitor(&monitor.Monitor{}) _ = mock.RemoveSrcFolderFromPath("") _ = mock.GetCodeWithMaxCharacters("", 0) }) @@ -77,7 +75,7 @@ func TestExecuteContainer(t *testing.T) { dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("test", nil) - monitorController := NewFormatterService(analysis, dockerAPIControllerMock, &config.Config{}, &monitor.Monitor{}) + monitorController := NewFormatterService(analysis, dockerAPIControllerMock, &config.Config{}) result, err := monitorController.ExecuteContainer(&dockerEntities.AnalysisData{}) assert.NoError(t, err) @@ -87,7 +85,7 @@ func TestExecuteContainer(t *testing.T) { func TestGetAnalysisIDErrorMessage(t *testing.T) { t.Run("should success get error message with replaces", func(t *testing.T) { - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}, &monitor.Monitor{}) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}) result := monitorController.GetAnalysisIDErrorMessage(tools.Bandit, "test") @@ -99,7 +97,7 @@ func TestGetAnalysisIDErrorMessage(t *testing.T) { func TestGetCommitAuthor(t *testing.T) { t.Run("should get commit author", func(t *testing.T) { - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}, &monitor.Monitor{}) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}) result := monitorController.GetCommitAuthor("", "") @@ -112,7 +110,7 @@ func TestGetConfigProjectPath(t *testing.T) { cliConfig := &config.Config{} cliConfig.SetProjectPath("test") - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, cliConfig, &monitor.Monitor{}) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, cliConfig) result := monitorController.GetConfigProjectPath() @@ -128,7 +126,7 @@ func TestAddWorkDirInCmd(t *testing.T) { CSharp: []string{"test"}, }) - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, cliConfig, &monitor.Monitor{}) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, cliConfig) result := monitorController.AddWorkDirInCmd("test", "C#", tools.SecurityCodeScan) @@ -141,7 +139,7 @@ func TestAddWorkDirInCmd(t *testing.T) { CSharp: []string{"test"}, }) - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, cliConfig, &monitor.Monitor{}) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, cliConfig) result := monitorController.AddWorkDirInCmd("test", "C#", tools.SecurityCodeScan) @@ -151,7 +149,7 @@ func TestAddWorkDirInCmd(t *testing.T) { func TestLogDebugWithReplace(t *testing.T) { t.Run("should log debug and not panics", func(t *testing.T) { - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}, &monitor.Monitor{}) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}) assert.NotPanics(t, func() { monitorController.LogDebugWithReplace("test", tools.NpmAudit, languages.Javascript) @@ -162,7 +160,7 @@ func TestLogDebugWithReplace(t *testing.T) { func TestGetAnalysisID(t *testing.T) { t.Run("should success get analysis id", func(t *testing.T) { id := uuid.New() - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{ID: id}, &docker.Mock{}, &config.Config{}, &monitor.Monitor{}) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{ID: id}, &docker.Mock{}, &config.Config{}) assert.Equal(t, id.String(), monitorController.GetAnalysisID()) }) } @@ -170,21 +168,21 @@ func TestGetAnalysisID(t *testing.T) { func TestGetAnalysis(t *testing.T) { t.Run("should success get analysis", func(t *testing.T) { id := uuid.New() - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{ID: id}, &docker.Mock{}, &config.Config{}, &monitor.Monitor{}) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{ID: id}, &docker.Mock{}, &config.Config{}) assert.NotEmpty(t, monitorController.GetAnalysis()) }) } func TestLogAnalysisError(t *testing.T) { t.Run("should not panic when logging error", func(t *testing.T) { - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}, &monitor.Monitor{}) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}) assert.NotPanics(t, func() { monitorController.SetAnalysisError(errors.New("test"), tools.GoSec, "") }) }) t.Run("should not panic when logging error and exists projectSubPath", func(t *testing.T) { - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}, &monitor.Monitor{}) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}) assert.NotPanics(t, func() { monitorController.SetAnalysisError(errors.New("test"), tools.GoSec, "/tmp") @@ -192,57 +190,36 @@ func TestLogAnalysisError(t *testing.T) { }) } -func TestSetLanguageIsFinished(t *testing.T) { - t.Run("should set go as finished", func(t *testing.T) { - currentMonitor := monitor.NewMonitor() - currentMonitor.AddProcess(1) - - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}, nil) - monitorController.SetMonitor(currentMonitor) - - monitorController.SetToolFinishedAnalysis() - assert.Equal(t, 0, currentMonitor.GetProcess()) - }) -} - func TestToolIsToIgnore(t *testing.T) { t.Run("should return true when language is match", func(t *testing.T) { - currentMonitor := monitor.NewMonitor() - currentMonitor.AddProcess(1) configs := &config.Config{} configs.SetToolsConfig(toolsconfig.ToolsConfigsStruct{GoSec: toolsconfig.ToolConfig{IsToIgnore: true}}) - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, configs, &monitor.Monitor{}) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, configs) assert.Equal(t, true, monitorController.ToolIsToIgnore(tools.GoSec)) }) t.Run("should return true when language is match uppercase", func(t *testing.T) { - currentMonitor := monitor.NewMonitor() - currentMonitor.AddProcess(1) configs := &config.Config{} configs.SetToolsConfig(toolsconfig.ToolsConfigsStruct{GoSec: toolsconfig.ToolConfig{IsToIgnore: true}}) - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, configs, &monitor.Monitor{}) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, configs) assert.Equal(t, true, monitorController.ToolIsToIgnore(tools.GoSec)) }) t.Run("should return true when language is match lowercase and multi tools", func(t *testing.T) { - currentMonitor := monitor.NewMonitor() - currentMonitor.AddProcess(1) configs := &config.Config{} configs.SetToolsConfig(toolsconfig.ToolsConfigsStruct{GoSec: toolsconfig.ToolConfig{IsToIgnore: true}, SecurityCodeScan: toolsconfig.ToolConfig{IsToIgnore: true}}) - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, configs, &monitor.Monitor{}) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, configs) assert.Equal(t, true, monitorController.ToolIsToIgnore(tools.GoSec)) }) t.Run("should return false when language is not match", func(t *testing.T) { - currentMonitor := monitor.NewMonitor() - currentMonitor.AddProcess(1) configs := &config.Config{} configs.SetToolsConfig(toolsconfig.ToolsConfigsStruct{SecurityCodeScan: toolsconfig.ToolConfig{IsToIgnore: true}}) - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, configs, &monitor.Monitor{}) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, configs) assert.Equal(t, false, monitorController.ToolIsToIgnore(tools.GoSec)) }) @@ -250,21 +227,21 @@ func TestToolIsToIgnore(t *testing.T) { func TestService_GetCodeWithMaxCharacters(t *testing.T) { t.Run("should return default code", func(t *testing.T) { - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}, nil) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}) code := "text" column := 0 newCode := monitorController.GetCodeWithMaxCharacters(code, column) assert.Equal(t, "text", newCode) }) t.Run("should return default code if column is negative", func(t *testing.T) { - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}, nil) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}) code := "text" column := -1 newCode := monitorController.GetCodeWithMaxCharacters(code, column) assert.Equal(t, "text", newCode) }) t.Run("should return 4:105 characters when text is so bigger", func(t *testing.T) { - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}, nil) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}) code := "text" for i := 0; i < 10; i++ { for i := 0; i <= 9; i++ { @@ -276,7 +253,7 @@ func TestService_GetCodeWithMaxCharacters(t *testing.T) { assert.Equal(t, "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", newCode) }) t.Run("should return first 100 characters when text is so bigger", func(t *testing.T) { - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}, nil) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}) code := "text" for i := 0; i < 10; i++ { for i := 0; i <= 9; i++ { @@ -288,7 +265,7 @@ func TestService_GetCodeWithMaxCharacters(t *testing.T) { assert.Equal(t, "text012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345", newCode) }) t.Run("should return first 100 characters when text contains breaking lines", func(t *testing.T) { - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}, nil) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}) code := `22: func GetMD5(s string) string { 23: h := md5.New() 24: io.WriteString(h, s) // #nohorus @@ -301,7 +278,7 @@ func TestService_GetCodeWithMaxCharacters(t *testing.T) { `, newCode) }) t.Run("should return first 100 characters when text is so bigger", func(t *testing.T) { - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}, nil) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}) code := "text" for i := 0; i <= 200; i++ { code += strconv.Itoa(i) @@ -311,7 +288,7 @@ func TestService_GetCodeWithMaxCharacters(t *testing.T) { assert.Equal(t, "4041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889", newCode) }) t.Run("should return first 100 characters when text is so bigger", func(t *testing.T) { - monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}, nil) + monitorController := NewFormatterService(&entitiesAnalysis.Analysis{}, &docker.Mock{}, &config.Config{}) code := "text" for i := 0; i <= 200; i++ { code += strconv.Itoa(i) diff --git a/internal/services/formatters/shell/shellcheck/formatter.go b/internal/services/formatters/shell/shellcheck/formatter.go index f7b368401..e49a273ac 100644 --- a/internal/services/formatters/shell/shellcheck/formatter.go +++ b/internal/services/formatters/shell/shellcheck/formatter.go @@ -53,7 +53,6 @@ func (f *Formatter) StartAnalysis(projectSubPath string) { f.SetAnalysisError(f.startShellCheck(projectSubPath), tools.ShellCheck, projectSubPath) f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.ShellCheck, languages.Shell) - f.SetToolFinishedAnalysis() } func (f *Formatter) startShellCheck(projectSubPath string) error { diff --git a/internal/services/formatters/shell/shellcheck/formatter_test.go b/internal/services/formatters/shell/shellcheck/formatter_test.go index 3742cfd90..774255793 100644 --- a/internal/services/formatters/shell/shellcheck/formatter_test.go +++ b/internal/services/formatters/shell/shellcheck/formatter_test.go @@ -21,7 +21,6 @@ import ( "github.com/ZupIT/horusec/internal/entities/toolsconfig" entitiesAnalysis "github.com/ZupIT/horusec-devkit/pkg/entities/analysis" - "github.com/ZupIT/horusec/internal/entities/monitor" "github.com/stretchr/testify/assert" @@ -45,7 +44,7 @@ func TestParseOutput(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -66,7 +65,7 @@ func TestParseOutput(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -89,7 +88,7 @@ func TestParseOutput(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -110,7 +109,7 @@ func TestParseOutput(t *testing.T) { dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("invalid output", nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -126,7 +125,7 @@ func TestParseOutput(t *testing.T) { config := &cliConfig.Config{} config.SetWorkDir(&workdir.WorkDir{}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) @@ -138,7 +137,7 @@ func TestParseOutput(t *testing.T) { config := &cliConfig.Config{} config.SetToolsConfig(toolsconfig.ToolsConfigsStruct{ShellCheck: toolsconfig.ToolConfig{IsToIgnore: true}}) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config, &monitor.Monitor{}) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config) formatter := NewFormatter(service) formatter.StartAnalysis("") diff --git a/internal/utils/file/file.go b/internal/utils/file/file.go index e76e3a4a4..a83fdc399 100644 --- a/internal/utils/file/file.go +++ b/internal/utils/file/file.go @@ -1,4 +1,4 @@ -// Copyright 2020 ZUP IT SERVICOS EM TECNOLOGIA E INOVACAO SA +// 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. @@ -15,10 +15,13 @@ package file import ( + "bufio" + "fmt" "io" "os" "os/exec" - "path/filepath" + filepathLib "path/filepath" + "strconv" "strings" ) @@ -29,7 +32,7 @@ func GetAbsFilePathIntoBasePath(filePath, basePath string) string { } for _, path := range strings.Split(string(bytes), "\n") { if strings.Contains(path, filePath) { - absPath, _ := filepath.Abs(path) + absPath, _ := filepathLib.Abs(path) return absPath } } @@ -52,8 +55,8 @@ func GetPathIntoFilename(filename, basePath string) string { } func isSameExtensions(filename, path string) bool { - filenameExt := filepath.Ext(filename) - basePathExt := filepath.Ext(path) + filenameExt := filepathLib.Ext(filename) + basePathExt := filepathLib.Ext(path) return filenameExt == basePathExt } @@ -63,7 +66,7 @@ func ReplacePathSeparator(path string) string { func GetSubPathByExtension(projectPath, subPath, ext string) (finalPath string) { pathToWalk := setProjectPathWithSubPath(projectPath, subPath) - _ = filepath.Walk(pathToWalk, func(walkPath string, info os.FileInfo, err error) error { + _ = filepathLib.Walk(pathToWalk, func(walkPath string, info os.FileInfo, err error) error { if err != nil { return err } @@ -83,7 +86,7 @@ func setExtension(ext string) string { } func verifyMathAndFormat(projectPath, walkPath, ext string) string { - matched, err := filepath.Match(setExtension(ext), filepath.Base(walkPath)) + matched, err := filepathLib.Match(setExtension(ext), filepathLib.Base(walkPath)) if err != nil { return "" } @@ -106,7 +109,7 @@ func setProjectPathWithSubPath(projectPath, projectSubPath string) string { func formatExtPath(projectPath, walkPath string) string { basePathRemoved := strings.ReplaceAll(walkPath, projectPath, "") - extensionFileRemoved := strings.ReplaceAll(basePathRemoved, filepath.Base(walkPath), "") + extensionFileRemoved := strings.ReplaceAll(basePathRemoved, filepathLib.Base(walkPath), "") if extensionFileRemoved != "" && extensionFileRemoved[0:1] == "/" { extensionFileRemoved = extensionFileRemoved[1:] @@ -114,3 +117,105 @@ func formatExtPath(projectPath, walkPath string) string { return extensionFileRemoved } + +func GetFilenameByExt(projectPath, subPath, ext string) (filename string) { + pathToWalk := setProjectPathWithSubPath(projectPath, subPath) + _ = filepathLib.Walk(pathToWalk, func(walkPath string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if filepathLib.Ext(walkPath) == ext { + filename = filepathLib.Base(walkPath) + return io.EOF + } + + return nil + }) + + return filename +} + +func GetCode(projectPath, filepath, desiredLine string) string { + path := fmt.Sprintf("%s%s%s", projectPath, string(os.PathSeparator), filepath) + + file, err := os.Open(path) + if err != nil { + return "" + } + + return strings.TrimSpace(getCodeFromDesiredLine(file, getLine(desiredLine))) +} + +func getLine(desiredLine string) int { + desiredLineParsed, _ := strconv.Atoi(desiredLine) + if desiredLineParsed <= 0 { + return desiredLineParsed + } + + return desiredLineParsed - 1 +} + +func getCodeFromDesiredLine(file *os.File, desiredLine int) string { + var line int + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + if line == desiredLine { + return scanner.Text() + } + + line++ + } + + return "" +} + +func GetDependencyCodeFilepathAndLine(projectPath, subPath, ext, dependency string) (code, file, line string) { + paths, err := getPathsByExtension(projectPath, subPath, ext) + if err != nil { + return "", "", "" + } + + return getDependencyInfo(paths, dependency) +} + +func getPathsByExtension(projectPath, subPath, ext string) ([]string, error) { + var paths []string + + pathToWalk := setProjectPathWithSubPath(projectPath, subPath) + return paths, filepathLib.Walk(pathToWalk, func(walkPath string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if filepathLib.Ext(walkPath) == ext { + paths = append(paths, walkPath) + } + + return nil + }) +} + +//nolint:funlen // improve in the future +func getDependencyInfo(paths []string, dependency string) (code, filepath, _ string) { + var line int + + for _, path := range paths { + file, err := os.Open(path) + if err != nil { + return "", "", "" + } + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line++ + + if strings.Contains(scanner.Text(), dependency) { + return strings.TrimSpace(scanner.Text()), path, strconv.Itoa(line) + } + } + } + + return "", "", "" +} diff --git a/internal/utils/prompt/prompt_test.go b/internal/utils/prompt/prompt_test.go index 20ea54897..59ce3a1b5 100644 --- a/internal/utils/prompt/prompt_test.go +++ b/internal/utils/prompt/prompt_test.go @@ -23,9 +23,7 @@ import ( func TestPrompt_Ask(t *testing.T) { t.Run("Should run command ask without panics", func(t *testing.T) { assert.NotPanics(t, func() { - go func() { - _, _ = NewPrompt().Ask("", "") - }() + _, _ = NewPrompt().Ask("", "") }) }) } @@ -33,9 +31,7 @@ func TestPrompt_Ask(t *testing.T) { func TestPrompt_Select(t *testing.T) { t.Run("Should run command select without panics", func(t *testing.T) { assert.NotPanics(t, func() { - go func() { - _, _ = NewPrompt().Select("", []string{}) - }() + _, _ = NewPrompt().Select("", []string{}) }) }) }