From f65205cc789a4b9ec88cb557bc5a655112694ac4 Mon Sep 17 00:00:00 2001 From: Matheus Alcantara Date: Wed, 15 Dec 2021 16:19:20 -0300 Subject: [PATCH] brakeman:fix - search for Gemfile's before start analysis If there was no `Gemfile` file in the current directory, Brakeman would generate an error stating that the project to be analyzed was not a Ruby on Rails project. This commit fix this issue by looking for a directory path that contains a `Gemfile` filename and them using this path as a work dir to execute Brakeman. Signed-off-by: Matheus Alcantara --- internal/helpers/messages/debug.go | 2 +- .../formatters/ruby/brakeman/formatter.go | 7 +++- .../ruby/brakeman/formatter_test.go | 19 ++++++----- internal/services/formatters/service.go | 2 +- internal/utils/file/file.go | 32 ++++++++++++++----- 5 files changed, 43 insertions(+), 19 deletions(-) diff --git a/internal/helpers/messages/debug.go b/internal/helpers/messages/debug.go index c954db6c4..de88b0cf3 100644 --- a/internal/helpers/messages/debug.go +++ b/internal/helpers/messages/debug.go @@ -29,7 +29,7 @@ const ( MsgDebugConfigFileRunningOnPath = "{HORUSEC_CLI} Config file running on path: " MsgDebugFolderOrFileIgnored = "{HORUSEC_CLI} The file or folder was ignored to send analysis:" MsgDebugShowConfigs = "{HORUSEC_CLI} The current configuration for this analysis are:" - MsgDebugShowWorkdir = "{HORUSEC_CLI} The workdir setup for run in path:" + MsgDebugShowWorkdir = "{HORUSEC_CLI} Using path %s as workdir to run tool %s" MsgDebugToolIgnored = "{HORUSEC_CLI} The tool was ignored for run in this analysis: " MsgDebugVulnHashToFix = "{HORUSEC_CLI} Vulnerability Hash expected to be FIXED: " MsgDebugDockerImageDoesNotExists = "{HORUSEC_CLI} Image %s does not exists. Pulling from registry" diff --git a/internal/services/formatters/ruby/brakeman/formatter.go b/internal/services/formatters/ruby/brakeman/formatter.go index 260aeefc9..03e2d1593 100644 --- a/internal/services/formatters/ruby/brakeman/formatter.go +++ b/internal/services/formatters/ruby/brakeman/formatter.go @@ -29,6 +29,7 @@ import ( "github.com/ZupIT/horusec/internal/enums/images" "github.com/ZupIT/horusec/internal/helpers/messages" "github.com/ZupIT/horusec/internal/services/formatters" + fileutils "github.com/ZupIT/horusec/internal/utils/file" vulnhash "github.com/ZupIT/horusec/internal/utils/vuln_hash" ) @@ -118,7 +119,11 @@ func (f *Formatter) newVulnerability(output *warning, projectSubPath string) *vu func (f *Formatter) getDockerConfig(projectSubPath string) *docker.AnalysisData { analysisData := &docker.AnalysisData{ - CMD: f.AddWorkDirInCmd(CMD, projectSubPath, tools.Brakeman), + CMD: f.AddWorkDirInCmd( + CMD, + fileutils.GetSubPathByFilename(f.GetConfigProjectPath(), projectSubPath, "Gemfile"), + tools.Brakeman, + ), Language: languages.Ruby, } diff --git a/internal/services/formatters/ruby/brakeman/formatter_test.go b/internal/services/formatters/ruby/brakeman/formatter_test.go index 3979f30f5..911026e3a 100644 --- a/internal/services/formatters/ruby/brakeman/formatter_test.go +++ b/internal/services/formatters/ruby/brakeman/formatter_test.go @@ -33,14 +33,11 @@ func TestParseBrakemanOutput(t *testing.T) { t.Run("Should success parse output to analysis", func(t *testing.T) { analysis := new(analysis.Analysis) - cfg := config.New() - cfg.ProjectPath = testutil.CreateHorusecAnalysisDirectory(t, analysis, testutil.RubyExample1) - dockerAPIControllerMock := testutil.NewDockerMock() dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(outputMock, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, cfg) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, newTestConfig(t, analysis)) formatter := NewFormatter(service) formatter.StartAnalysis("") @@ -68,7 +65,7 @@ func TestParseBrakemanOutput(t *testing.T) { dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config.New()) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, newTestConfig(t, analysis)) formatter := NewFormatter(service) formatter.StartAnalysis("") @@ -86,7 +83,7 @@ func TestParseBrakemanOutput(t *testing.T) { dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config.New()) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, newTestConfig(t, analysis)) formatter := NewFormatter(service) formatter.StartAnalysis("") @@ -101,7 +98,7 @@ func TestParseBrakemanOutput(t *testing.T) { dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("invalid output", nil) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config.New()) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, newTestConfig(t, analysis)) formatter := NewFormatter(service) formatter.StartAnalysis("") @@ -116,7 +113,7 @@ func TestParseBrakemanOutput(t *testing.T) { dockerAPIControllerMock.On("SetAnalysisID") dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", errors.New("test")) - service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, config.New()) + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, newTestConfig(t, analysis)) formatter := NewFormatter(service) formatter.StartAnalysis("") @@ -143,6 +140,12 @@ func TestParseBrakemanOutput(t *testing.T) { }) } +func newTestConfig(t *testing.T, analysis *analysis.Analysis) *config.Config { + cfg := config.New() + cfg.ProjectPath = testutil.CreateHorusecAnalysisDirectory(t, analysis, testutil.RubyExample) + return cfg +} + const outputMock = ` { "warnings": [ diff --git a/internal/services/formatters/service.go b/internal/services/formatters/service.go index d1f3a2c4a..5c54fed1c 100644 --- a/internal/services/formatters/service.go +++ b/internal/services/formatters/service.go @@ -99,7 +99,7 @@ func (s *Service) AddWorkDirInCmd(cmd, projectSubPath string, tool tools.Tool) s // Since the command will run inside a Docker container we need // to convert any Windows slash (\) to Unix slash (/). projectSubPath = filepath.ToSlash(projectSubPath) - logger.LogDebugWithLevel(messages.MsgDebugShowWorkdir, tool.ToString(), projectSubPath) + logger.LogDebugWithLevel(fmt.Sprintf(messages.MsgDebugShowWorkdir, projectSubPath, tool.ToString())) return strings.ReplaceAll(cmd, "{{WORK_DIR}}", fmt.Sprintf("cd %s", projectSubPath)) } diff --git a/internal/utils/file/file.go b/internal/utils/file/file.go index 33fb13f42..df7c87bee 100644 --- a/internal/utils/file/file.go +++ b/internal/utils/file/file.go @@ -63,10 +63,20 @@ func GetPathFromFilename(filename, basePath string) string { return filePath } -func isSameExtensions(filename, path string) bool { - filenameExt := filepath.Ext(filename) - basePathExt := filepath.Ext(path) - return filenameExt == basePathExt +// GetSubPathByFilename works like GetSubPathByExtension but for filenames. +// +// The value returned will be the first path that contains a file with a given +// filename, otherwise will return an empty string. +func GetSubPathByFilename(projectPath, subPath, filename string) string { + pathToWalk := joinProjectPathWithSubPath(projectPath, subPath) + logger.LogDebugWithLevel(fmt.Sprintf("Seaching for files with %s name on %s", filename, pathToWalk)) + + if path := GetPathFromFilename(filename, pathToWalk); path != "" { + logger.LogDebugWithLevel(fmt.Sprintf("Found file %s on %s", filename, path)) + return filepath.Dir(path) + } + + return "" } // ReplacePathSeparator replace slashes from path to OS specific. @@ -115,10 +125,6 @@ func GetSubPathByExtension(projectPath, subPath, ext string) (extensionPath stri return "" } -func buildPattern(ext string) string { - return "*" + ext -} - // relativeDirIfPathMatch return relative directory of path based on projectPath // if path extension match ext. func relativeDirIfPathMatch(projectPath, path, ext string) string { @@ -316,3 +322,13 @@ func CreateAndWriteFile(input, filename string) error { _, err = file.WriteString(input) return err } + +func isSameExtensions(filename, path string) bool { + filenameExt := filepath.Ext(filename) + basePathExt := filepath.Ext(path) + return filenameExt == basePathExt +} + +func buildPattern(ext string) string { + return "*" + ext +}