From eb80dca126a530a3769c2c93ebc971f8fcbcdb23 Mon Sep 17 00:00:00 2001 From: Sven Dolderer Date: Thu, 26 Sep 2024 17:37:48 +0200 Subject: [PATCH 1/7] client: label functionality added #3459 For these actions: - listJobs - getReport - getStatus --- .../mercedes-benz.com/sechub/cli/config.go | 2 +- .../sechub/cli/context-initializer.go | 12 ++++--- .../src/mercedes-benz.com/sechub/cli/job.go | 8 ++++- .../mercedes-benz.com/sechub/cli/labels.go | 4 --- .../sechub/cli/labels_test.go | 34 +++++++++++++++++++ .../sechub/cli/urlbuilder.go | 12 +++++++ .../sechub/cli/urlbuilder_test.go | 25 ++++++++++++++ 7 files changed, 87 insertions(+), 10 deletions(-) diff --git a/sechub-cli/src/mercedes-benz.com/sechub/cli/config.go b/sechub-cli/src/mercedes-benz.com/sechub/cli/config.go index 2849f1da65..6a5e0c60cd 100644 --- a/sechub-cli/src/mercedes-benz.com/sechub/cli/config.go +++ b/sechub-cli/src/mercedes-benz.com/sechub/cli/config.go @@ -85,7 +85,7 @@ func prepareOptionsFromCommandline(config *Config) { fileOption, "", "Defines file to read from for actions '"+defineFalsePositivesAction+"', '"+markFalsePositivesAction+"', '"+interactiveMarkFalsePositivesAction+"', '"+unmarkFalsePositivesAction+"'") flag.StringVar(&config.secHubJobUUID, jobUUIDOption, "", "SecHub job uuid - Optional for actions '"+getStatusAction+"' or '"+getReportAction+"'") - flag.Func(labelOption, "Define a `SecHub label` for the scan job. (Example: \"key1=value1\") Repeat to define multiple labels.", func(s string) error { + flag.Func(labelOption, "Define a `SecHub label` for scan or for list filtering. (Example: \"key1=value1\") Repeat to define multiple labels.", func(s string) error { var err error config.labels, err = addLabelToList(config.labels, s, true) if err != nil { diff --git a/sechub-cli/src/mercedes-benz.com/sechub/cli/context-initializer.go b/sechub-cli/src/mercedes-benz.com/sechub/cli/context-initializer.go index 965c608672..7891d7a281 100644 --- a/sechub-cli/src/mercedes-benz.com/sechub/cli/context-initializer.go +++ b/sechub-cli/src/mercedes-benz.com/sechub/cli/context-initializer.go @@ -20,13 +20,17 @@ func InitializeContext() *Context { printLogoWithVersion(context) /* load configuration file - maybe there are some settings normally done by cli arguments too */ - if context.config.action != showHelpAction { + if context.config.action != showHelpAction && context.config.action != showVersionAction { loadConfigFile(context) } - // Add labels defined via cmdline args or env var to config JSON (only for scan jobs) - if len(context.config.labels) > 0 && (context.config.action == scanAction || context.config.action == scanAsynchronAction) { - err := applyLabelsToConfigJson(context) + // Add labels defined via cmdline args or env var to config JSON (only for job related client actions) + if context.config.action == scanAction || + context.config.action == scanAsynchronAction || + context.config.action == listJobsAction || + context.config.action == getReportAction || + context.config.action == getStatusAction { + err := applyLabelsToConfigJson(context) if err != nil { sechubUtil.LogError("Error while processing labels: " + err.Error()) } diff --git a/sechub-cli/src/mercedes-benz.com/sechub/cli/job.go b/sechub-cli/src/mercedes-benz.com/sechub/cli/job.go index e30e874d13..86542f90d3 100644 --- a/sechub-cli/src/mercedes-benz.com/sechub/cli/job.go +++ b/sechub-cli/src/mercedes-benz.com/sechub/cli/job.go @@ -164,7 +164,12 @@ func printSecHubJobSummaryAndFailOnTrafficLight(context *Context) { } func getSecHubJobList(context *Context, size int) { - // request SecHub job state from server + // Print filtering labels if defined + for key, value := range context.config.labels { + sechubUtil.LogNotice("Label match "+key+"="+value) + } + + // Request SecHub job list from server response := sendWithDefaultHeader("GET", buildGetSecHubJobListAPICall(context, size), context) data, err := io.ReadAll(response.Body) @@ -213,6 +218,7 @@ func printLatestJobsOfProject(context *Context) { // get latest jobs into context.jobList getSecHubJobList(context, SizeOfJobList) + // Print result table printFormat := "%-36s | %-6s | %-8s | %-6s | %-19s | %-19s\n" fmt.Printf(printFormat, "SecHub JobUUID", "Status", "Stage", "Result", "Created", "Ended") fmt.Println("-------------------------------------+--------+----------+--------+---------------------+--------------------") diff --git a/sechub-cli/src/mercedes-benz.com/sechub/cli/labels.go b/sechub-cli/src/mercedes-benz.com/sechub/cli/labels.go index c87bc61c3e..7afee5aedd 100644 --- a/sechub-cli/src/mercedes-benz.com/sechub/cli/labels.go +++ b/sechub-cli/src/mercedes-benz.com/sechub/cli/labels.go @@ -38,10 +38,6 @@ func addLabelToList(list map[string]string, labelDefinition string, overrideIfEx // in the sechub config JSON (context.contentToSend) by context.config.labels func applyLabelsToConfigJson(context *Context) error { var err error - if len(context.config.labels) == 0 { - // Nothing to do when context.config.labels is empty - return err - } // Unmarshal the JSON into a map structure // because when using SecHubConfig struct then all new serverside json options have to be added to the client struct diff --git a/sechub-cli/src/mercedes-benz.com/sechub/cli/labels_test.go b/sechub-cli/src/mercedes-benz.com/sechub/cli/labels_test.go index 0b619f5cdb..0014c3ed7a 100644 --- a/sechub-cli/src/mercedes-benz.com/sechub/cli/labels_test.go +++ b/sechub-cli/src/mercedes-benz.com/sechub/cli/labels_test.go @@ -145,6 +145,40 @@ func Example_applyLabelsToConfigJson_with_labels_section_in_json() { // context.contentToSend: {"apiVersion":"1.0","metaData":{"labels":{"key1":"value1x","key2":"value2"}},"project":"myproject"} } +func Example_applyLabelsToConfigJson_with_labels_section_only_in_json() { + // PREPARE + var context Context + var config Config + context.config = &config + + sechubJSON := ` + { + "apiVersion": "1.0", + "project": "myproject", + "metaData": { + "labels": { + "key1": "value1", + "key2": "value2" + } + } + } + ` + context.contentToSend = []byte(sechubJSON) + + // Lables are empty + labels := map[string]string{} + context.config.labels = labels + + // EXECUTE + applyLabelsToConfigJson(&context) + + // TEST + fmt.Printf("labels: %+v\n", context.config.labels) + + // Output: + // labels: map[key1:value1 key2:value2] +} + func Example_applyLabelsToConfigJson_without_labels_section_in_json() { // PREPARE var context Context diff --git a/sechub-cli/src/mercedes-benz.com/sechub/cli/urlbuilder.go b/sechub-cli/src/mercedes-benz.com/sechub/cli/urlbuilder.go index 07ee3877a0..d577edb1d3 100644 --- a/sechub-cli/src/mercedes-benz.com/sechub/cli/urlbuilder.go +++ b/sechub-cli/src/mercedes-benz.com/sechub/cli/urlbuilder.go @@ -4,6 +4,7 @@ package cli import ( "fmt" + "net/url" ) // https://localhost:8443/api/project/testproject/job/ @@ -60,6 +61,17 @@ func buildGetSecHubJobListAPICall(context *Context, size int) string { context.contentToSend = nil // Do not send content context.inputForContentProcessing = nil apiPart := fmt.Sprintf("project/%s/jobs?size=%d&page=0", context.config.projectID, size) + + // Add filtering by labels if defined + metadata_set:=false + for key, value := range context.config.labels { + if ! metadata_set { + apiPart += "&withMetaData=true" + metadata_set = true + } + apiPart += "&metadata.labels."+key+"="+url.QueryEscape(value) + } + return buildAPIUrl(&context.config.server, &apiPart) } diff --git a/sechub-cli/src/mercedes-benz.com/sechub/cli/urlbuilder_test.go b/sechub-cli/src/mercedes-benz.com/sechub/cli/urlbuilder_test.go index 0edbcd3ba4..e3e3a155f3 100644 --- a/sechub-cli/src/mercedes-benz.com/sechub/cli/urlbuilder_test.go +++ b/sechub-cli/src/mercedes-benz.com/sechub/cli/urlbuilder_test.go @@ -184,3 +184,28 @@ func Example_buildGetSecHubJobListAPICall() { // Output: // https://localhost:8443/api/project/testproject/jobs?size=10&page=0 } + +func Example_buildGetSecHubJobListAPICallWithLabels() { + /* prepare */ + context := new(Context) + config := new(Config) + + context.config = config + config.projectID = "testproject" + config.server = "https://localhost:8443" + + labels := map[string]string{ + "key1": "value1", + "key2": "Non alphnumeric character$ !", + } + context.config.labels = labels + + + /* execute */ + result := buildGetSecHubJobListAPICall(context, 10) + + /* test*/ + fmt.Println(result) + // Output: + // https://localhost:8443/api/project/testproject/jobs?size=10&page=0&withMetaData=true&metadata.labels.key1=value1&metadata.labels.key2=Non+alphnumeric+character%24+%21 +} From 40479ec7d0004f400d3711c8785a97f48b5e6e67 Mon Sep 17 00:00:00 2001 From: Sven Dolderer Date: Thu, 26 Sep 2024 17:55:09 +0200 Subject: [PATCH 2/7] client: label functionality docs updated #3459 --- .../src/mercedes-benz.com/sechub/cli/config.go | 2 +- .../documents/client/02_sechub_client.adoc | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/sechub-cli/src/mercedes-benz.com/sechub/cli/config.go b/sechub-cli/src/mercedes-benz.com/sechub/cli/config.go index 6a5e0c60cd..8483ced80b 100644 --- a/sechub-cli/src/mercedes-benz.com/sechub/cli/config.go +++ b/sechub-cli/src/mercedes-benz.com/sechub/cli/config.go @@ -85,7 +85,7 @@ func prepareOptionsFromCommandline(config *Config) { fileOption, "", "Defines file to read from for actions '"+defineFalsePositivesAction+"', '"+markFalsePositivesAction+"', '"+interactiveMarkFalsePositivesAction+"', '"+unmarkFalsePositivesAction+"'") flag.StringVar(&config.secHubJobUUID, jobUUIDOption, "", "SecHub job uuid - Optional for actions '"+getStatusAction+"' or '"+getReportAction+"'") - flag.Func(labelOption, "Define a `SecHub label` for scan or for list filtering. (Example: \"key1=value1\") Repeat to define multiple labels.", func(s string) error { + flag.Func(labelOption, "Define a `SecHub label` for scan or filtering. (Example: \"key1=value1\") Repeat to define multiple labels.", func(s string) error { var err error config.labels, err = addLabelToList(config.labels, s, true) if err != nil { diff --git a/sechub-doc/src/docs/asciidoc/documents/client/02_sechub_client.adoc b/sechub-doc/src/docs/asciidoc/documents/client/02_sechub_client.adoc index 7721c524a8..37d84c3ac0 100644 --- a/sechub-doc/src/docs/asciidoc/documents/client/02_sechub_client.adoc +++ b/sechub-doc/src/docs/asciidoc/documents/client/02_sechub_client.adoc @@ -155,7 +155,8 @@ sechub getStatus ---- TIP: Defaults to latest job. + - You can select the job by providing the wanted job UUID by adding `-jobUUID ${jobUUID}` + You can select the job by providing the wanted job UUID by adding `-jobUUID ${jobUUID}` + + This action respects defined <> as filter criteria. ===== getReport Will fetch the report as json. (result will only exist when job is done) @@ -166,7 +167,8 @@ sechub getReport ---- TIP: Defaults to latest finished job. + - You can select the job by providing the wanted job UUID by adding `-jobUUID ${jobUUID}` + You can select the job by providing the wanted job UUID by adding `-jobUUID ${jobUUID}` + + This action respects defined <> as filter criteria. TIP: With `-output ${file-or-directory}` you can define where to place the report. + Can be a directory, a file name or a file path. + @@ -182,7 +184,8 @@ sechub listJobs TIP: This might be useful for an overview or + when you want to see your project's scan job queue or + - to find out the job UUID of a certain job. + to find out the job UUID of a certain job. + + This action respects defined <> as filter criteria. ===== defineFalsePositives @@ -296,6 +299,7 @@ include::sechub_client_falsepositive_list_example_unmark_jobData+projectData.jso TIP: <> might be easier to use +[[client-configuration-overview]] ==== Configuration overview Basically parameters can be passed to the `{sechub}` client in three ways: @@ -349,7 +353,7 @@ In below table, there is an overview of what can be defined where. - `-jobUUID ` + `{sechub}` job uuid. Optional for actions <> or <> - `-label