diff --git a/horusec-cli/README.md b/horusec-cli/README.md index 3ae62f0f0..ef47d8dfc 100644 --- a/horusec-cli/README.md +++ b/horusec-cli/README.md @@ -240,6 +240,7 @@ All available flags are: | HORUSEC_CLI_FALSE_POSITIVE_HASHES | horusecCliFalsePositiveHashes | false-positive | F | | Used to ignore vulnerability on analysis and setup with type `False positive`. ATTENTION when you add this configuration directly to the CLI, the configuration performed via the Horusec graphical interface will be overwritten. | | HORUSEC_CLI_RISK_ACCEPT_HASHES | horusecCliRiskAcceptHashes | risk-accept | R | | Used to ignore vulnerability on analysis and setup with type `Risk accept`. ATTENTION when you add this configuration directly to the CLI, the configuration performed via the Horusec graphical interface will be overwritten. | | HORUSEC_CLI_TOOLS_TO_IGNORE | horusecCliToolsToIgnore | tools-ignore | T | | Used to ignore tool on run horusec analysis. Available are: GoSec,SecurityCodeScan,Brakeman,Safety,Bandit,NpmAudit,YarnAudit,SpotBugs,HorusecKotlin,HorusecJava,HorusecLeaks,GitLeaks,TfSec,Semgrep,HorusecCsharp,HorusecNodeJS, HorusecKubernetes. Ex.: `T="GoSec, HorusecLeaks"` | +| HORUSEC_CLI_HEADERS | horusecCliHeaders | headers | | | Used to send dynamic headers on dispatch http request to horusec api service | | | horusecCliWorkDir | | | | This setting tells to horusec the right directory to run a specific language. | #### Authorization diff --git a/horusec-cli/cmd/horusec/start/start.go b/horusec-cli/cmd/horusec/start/start.go index 48e09aa70..f455ddefd 100644 --- a/horusec-cli/cmd/horusec/start/start.go +++ b/horusec-cli/cmd/horusec/start/start.go @@ -178,6 +178,9 @@ func (s *Start) loadFlags(cmd *cobra.Command) { cmd.PersistentFlags(). StringVarP(&s.configs.RepositoryAuthorization, "authorization", "a", s.configs.GetRepositoryAuthorization(), "The authorization token for the Horusec API") + cmd.PersistentFlags(). + StringVar(&s.configs.Headers, "headers", s.configs.Headers, + "The headers dynamic to send on request in Horusec API. Example --headers=\"{\"X-Auth-Service\": \"my-value\"}\"") cmd.PersistentFlags(). BoolVarP(&s.configs.ReturnErrorIfFoundVulnerability, "return-error", "e", s.configs.GetReturnErrorIfFoundVulnerability(), "The return-error is the option to check if you can return \"exit(1)\" if found vulnerabilities. Example -e=\"true\"") diff --git a/horusec-cli/config/.example-horusec-cli.json b/horusec-cli/config/.example-horusec-cli.json index 3a898de51..f24de6475 100644 --- a/horusec-cli/config/.example-horusec-cli.json +++ b/horusec-cli/config/.example-horusec-cli.json @@ -17,5 +17,8 @@ "horusecCliRepositoryName": "horus", "horusecCliFalsePositiveHashes": "hash1, hash2", "horusecCliRiskAcceptHashes": "hash3, hash4", - "horusecCliToolsToIgnore": "GoSec" + "horusecCliToolsToIgnore": "GoSec", + "horusecCliHeaders": { + "X-Headers": "some-other-value" + } } \ No newline at end of file diff --git a/horusec-cli/config/config.go b/horusec-cli/config/config.go index 489ece728..bf237e585 100644 --- a/horusec-cli/config/config.go +++ b/horusec-cli/config/config.go @@ -152,6 +152,9 @@ const ( // Used to ignore tools for run // By default is empty EnvToolsToIgnore = "HORUSEC_CLI_TOOLS_TO_IGNORE" + // Used send others headers on request to send in horusec-api + // By default is empty + EnvHeaders = "HORUSEC_CLI_HEADERS" ) type Config struct { @@ -162,6 +165,7 @@ type Config struct { TimeoutInSecondsAnalysis int64 MonitorRetryInSeconds int64 RepositoryAuthorization string + Headers string PrintOutputType string JSONOutputFilePath string TypesOfVulnerabilitiesToIgnore string @@ -206,6 +210,7 @@ func (c *Config) SetConfigsFromViper() { c.SetFalsePositiveHashes(viper.GetString(c.toLowerCamel(EnvFalsePositiveHashes))) c.SetRiskAcceptHashes(viper.GetString(c.toLowerCamel(EnvRiskAcceptHashes))) c.SetToolsToIgnore(viper.GetString(c.toLowerCamel(EnvToolsToIgnore))) + c.SetHeaders(viper.GetStringMapString(c.toLowerCamel(EnvHeaders))) } //nolint @@ -232,6 +237,7 @@ func (c *Config) SetConfigsFromEnvironments() { c.SetFalsePositiveHashes(env.GetEnvOrDefault(EnvFalsePositiveHashes, c.FalsePositiveHashes)) c.SetRiskAcceptHashes(env.GetEnvOrDefault(EnvRiskAcceptHashes, c.RiskAcceptHashes)) c.SetToolsToIgnore(env.GetEnvOrDefault(EnvToolsToIgnore, c.ToolsToIgnore)) + c.SetHeaders(env.GetEnvOrDefault(EnvHeaders, c.Headers)) } func (c *Config) GetHorusecAPIUri() string { @@ -460,3 +466,22 @@ func (c *Config) GetToolsToIgnore() string { func (c *Config) SetToolsToIgnore(toolsToIgnore string) { c.ToolsToIgnore = toolsToIgnore } + +func (c *Config) GetHeaders() (headers map[string]string) { + err := json.Unmarshal([]byte(c.Headers), &headers) + logger.LogErrorWithLevel("Error on unmarshal headers to map", err, logger.ErrorLevel) + return headers +} + +func (c *Config) SetHeaders(headers interface{}) { + if headers != nil && headers != "" { + headersString, ok := headers.(string) + if ok { + c.Headers = headersString + } else { + bytes, err := json.Marshal(headers) + logger.LogErrorWithLevel("Error on marshal headers to bytes", err, logger.ErrorLevel) + c.Headers = string(bytes) + } + } +} diff --git a/horusec-cli/config/config_test.go b/horusec-cli/config/config_test.go index 4a5ef1d53..e80e9b084 100644 --- a/horusec-cli/config/config_test.go +++ b/horusec-cli/config/config_test.go @@ -15,6 +15,7 @@ package config import ( + "encoding/json" "os" "testing" @@ -41,6 +42,7 @@ func TestNewHorusecConfig(t *testing.T) { assert.Equal(t, configs.CertPath, "") assert.Equal(t, configs.CertInsecureSkipVerify, false) assert.Equal(t, configs.ReturnErrorIfFoundVulnerability, false) + assert.Equal(t, configs.Headers, "") path, _ := os.Getwd() assert.Equal(t, configs.ProjectPath, path) assert.Equal(t, configs.WorkDir, Config{}.WorkDir) @@ -53,6 +55,7 @@ func TestNewHorusecConfig(t *testing.T) { assert.Equal(t, configs.ToolsToIgnore, "") assert.Equal(t, 0, len(configs.GetFalsePositiveHashesList())) assert.Equal(t, 0, len(configs.GetRiskAcceptHashesList())) + assert.Equal(t, "", configs.Headers) }) t.Run("Should change horusec config and return your new values", func(t *testing.T) { configs := &Config{} @@ -77,6 +80,7 @@ func TestNewHorusecConfig(t *testing.T) { configs.SetFalsePositiveHashes("123456789") configs.SetRiskAcceptHashes("987654321") configs.SetToolsToIgnore("HorusecLeaks") + configs.SetHeaders(map[string]string{"header1": "value1"}) assert.NotEqual(t, configs.GetHorusecAPIUri(), "http://0.0.0.0:8000") assert.NotEqual(t, configs.GetTimeoutInSecondsRequest(), int64(300)) assert.NotEqual(t, configs.GetTimeoutInSecondsAnalysis(), int64(600)) @@ -98,6 +102,7 @@ func TestNewHorusecConfig(t *testing.T) { assert.NotEqual(t, configs.GetRiskAcceptHashes(), "") assert.NotEqual(t, configs.GetToolsToIgnore(), "") assert.NotNil(t, configs.GetWorkDir()) + assert.NotEmpty(t, configs.GetHeaders()) }) t.Run("Should return horusec config using viper file", func(t *testing.T) { path, err := os.Getwd() @@ -120,6 +125,7 @@ func TestNewHorusecConfig(t *testing.T) { assert.Equal(t, "./assets", configs.FilesOrPathsToIgnore) assert.Equal(t, true, configs.ReturnErrorIfFoundVulnerability) assert.Equal(t, "./", configs.ProjectPath) + assert.Equal(t, `{"x-headers":"some-other-value"}`, configs.Headers) assert.Equal(t, wd, configs.WorkDir) assert.Equal(t, configs.FilterPath, "./tmp") assert.Equal(t, configs.EnableGitHistoryAnalysis, true) @@ -158,6 +164,9 @@ func TestNewHorusecConfig(t *testing.T) { assert.NoError(t, os.Setenv(EnvFalsePositiveHashes, "hash1, hash2")) assert.NoError(t, os.Setenv(EnvRiskAcceptHashes, "hash3, hash4")) assert.NoError(t, os.Setenv(EnvToolsToIgnore, "TfSec")) + headersBytes, err := json.Marshal(map[string]string{"X-other-header": "some-value"}) + assert.NoError(t, err) + assert.NoError(t, os.Setenv(EnvHeaders, string(headersBytes))) configs.SetConfigsFromEnvironments() assert.Equal(t, "http://horusec.com", configs.HorusecAPIUri) assert.Equal(t, int64(50), configs.TimeoutInSecondsRequest) @@ -180,6 +189,7 @@ func TestNewHorusecConfig(t *testing.T) { assert.Equal(t, "hash3, hash4", configs.RiskAcceptHashes) assert.Equal(t, 2, len(configs.GetRiskAcceptHashesList())) assert.Equal(t, "TfSec", configs.GetToolsToIgnore()) + assert.Equal(t, map[string]string{"X-other-header": "some-value"}, configs.GetHeaders()) }) } @@ -206,5 +216,6 @@ func TestToLowerCamel(t *testing.T) { assert.Equal(t, "horusecCliFalsePositiveHashes", configs.toLowerCamel(EnvFalsePositiveHashes)) assert.Equal(t, "horusecCliRiskAcceptHashes", configs.toLowerCamel(EnvRiskAcceptHashes)) assert.Equal(t, "horusecCliToolsToIgnore", configs.toLowerCamel(EnvToolsToIgnore)) + assert.Equal(t, "horusecCliHeaders", configs.toLowerCamel(EnvHeaders)) }) } diff --git a/horusec-cli/internal/services/horusapi/horus_api.go b/horusec-cli/internal/services/horusapi/horus_api.go index db13a4372..563dddb84 100644 --- a/horusec-cli/internal/services/horusapi/horus_api.go +++ b/horusec-cli/internal/services/horusapi/horus_api.go @@ -90,7 +90,7 @@ func (s *Service) sendFindAnalysisRequest(analysisID uuid.UUID) (httpResponse.In return nil, err } - req.Header.Add("Authorization", s.config.GetRepositoryAuthorization()) + s.addHeaders(req) return s.httpUtil.DoRequest(req, tlsConfig) } @@ -105,7 +105,7 @@ func (s *Service) sendCreateAnalysisRequest(analysis *horusec.Analysis) (httpRes return nil, err } - req.Header.Add("Authorization", s.config.GetRepositoryAuthorization()) + s.addHeaders(req) return s.httpUtil.DoRequest(req, tlsConfig) } @@ -171,3 +171,10 @@ func (s *Service) newRequestData(analysis *horusec.Analysis) []byte { return analysisData.ToBytes() } + +func (s *Service) addHeaders(req *http.Request) { + req.Header.Add("Authorization", s.config.GetRepositoryAuthorization()) + for key, value := range s.config.GetHeaders() { + req.Header.Add(key, value) + } +} diff --git a/horusec-cli/internal/services/horusapi/horus_api_test.go b/horusec-cli/internal/services/horusapi/horus_api_test.go index 54b65f816..309ed48d8 100644 --- a/horusec-cli/internal/services/horusapi/horus_api_test.go +++ b/horusec-cli/internal/services/horusapi/horus_api_test.go @@ -47,7 +47,7 @@ func TestSendAnalysis(t *testing.T) { service := Service{ httpUtil: httpMock, - config: &cliConfig.Config{RepositoryAuthorization: "test"}, + config: &cliConfig.Config{RepositoryAuthorization: "test", Headers: `{"some-header": "some-value"}`}, } assert.NotPanics(t, func() {