From 2305f93eec2166db8a6fbb94b3d8722efeb023f3 Mon Sep 17 00:00:00 2001 From: Maxime Desrosiers Date: Thu, 28 Jul 2022 15:25:16 -0400 Subject: [PATCH] add application name parameter to protecode scan --- cmd/protecodeExecuteScan.go | 11 ++- cmd/protecodeExecuteScan_generated.go | 11 +++ cmd/protecodeExecuteScan_test.go | 86 ++++++++++++++++++++ pkg/protecode/protecode.go | 30 ++++--- pkg/protecode/protecode_test.go | 35 ++++---- resources/metadata/protecodeExecuteScan.yaml | 7 ++ 6 files changed, 148 insertions(+), 32 deletions(-) diff --git a/cmd/protecodeExecuteScan.go b/cmd/protecodeExecuteScan.go index bc232dd86e..afa0b78a7f 100644 --- a/cmd/protecodeExecuteScan.go +++ b/cmd/protecodeExecuteScan.go @@ -363,7 +363,7 @@ func uploadFile(utils protecodeUtils, config protecodeExecuteScanOptions, produc if len(config.FetchURL) > 0 { log.Entry().Debugf("Declare fetch url %v", config.FetchURL) - resultData := client.DeclareFetchURL(config.CleanupMode, config.Group, config.FetchURL, version, productID, replaceBinary) + resultData := client.DeclareFetchURL(config.CleanupMode, config.Group, config.FetchURL, version, productID, replaceBinary, config.ApplicationName) productID = resultData.Result.ProductID } else { log.Entry().Debugf("Upload file path: %v", config.FilePath) @@ -376,10 +376,17 @@ func uploadFile(utils protecodeUtils, config protecodeExecuteScanOptions, produc } combinedFileName := fileName + + // set the application name instead of the filename + if len(config.ApplicationName) > 0 { + combinedFileName = config.ApplicationName + } + if len(config.PullRequestName) > 0 { - combinedFileName = fmt.Sprintf("%v_%v", config.PullRequestName, fileName) + combinedFileName = fmt.Sprintf("%v_%v", config.PullRequestName, combinedFileName) } + resultData := client.UploadScanFile(config.CleanupMode, config.Group, pathToFile, combinedFileName, version, productID, replaceBinary) productID = resultData.Result.ProductID log.Entry().Debugf("[DEBUG] ===> uploadFile return FINAL product id: %v", productID) diff --git a/cmd/protecodeExecuteScan_generated.go b/cmd/protecodeExecuteScan_generated.go index df2df3323f..d66c5fb1fc 100644 --- a/cmd/protecodeExecuteScan_generated.go +++ b/cmd/protecodeExecuteScan_generated.go @@ -29,6 +29,7 @@ type protecodeExecuteScanOptions struct { DockerConfigJSON string `json:"dockerConfigJSON,omitempty"` CleanupMode string `json:"cleanupMode,omitempty" validate:"possible-values=none binary complete"` FilePath string `json:"filePath,omitempty"` + ApplicationName string `json:"applicationName,omitempty"` TimeoutMinutes string `json:"timeoutMinutes,omitempty"` ServerURL string `json:"serverUrl,omitempty"` ReportFileName string `json:"reportFileName,omitempty"` @@ -245,6 +246,7 @@ func addProtecodeExecuteScanFlags(cmd *cobra.Command, stepConfig *protecodeExecu cmd.Flags().StringVar(&stepConfig.DockerConfigJSON, "dockerConfigJSON", os.Getenv("PIPER_dockerConfigJSON"), "Path to the file `.docker/config.json` - this is typically provided by your CI/CD system. You can find more details about the Docker credentials in the [Docker documentation](https://docs.docker.com/engine/reference/commandline/login/).") cmd.Flags().StringVar(&stepConfig.CleanupMode, "cleanupMode", `binary`, "Decides which parts are removed from the Protecode backend after the scan") cmd.Flags().StringVar(&stepConfig.FilePath, "filePath", os.Getenv("PIPER_filePath"), "The path to the file from local workspace to scan with Protecode") + cmd.Flags().StringVar(&stepConfig.ApplicationName, "applicationName", os.Getenv("PIPER_applicationName"), "The Protecode application name, by default the uploaded filename will be use") cmd.Flags().StringVar(&stepConfig.TimeoutMinutes, "timeoutMinutes", `60`, "The timeout to wait for the scan to finish") cmd.Flags().StringVar(&stepConfig.ServerURL, "serverUrl", os.Getenv("PIPER_serverUrl"), "The URL to the Protecode backend") cmd.Flags().StringVar(&stepConfig.ReportFileName, "reportFileName", `protecode_report.pdf`, "The file name of the report to be created") @@ -369,6 +371,15 @@ func protecodeExecuteScanMetadata() config.StepData { Aliases: []config.Alias{}, Default: os.Getenv("PIPER_filePath"), }, + { + Name: "applicationName", + ResourceRef: []config.ResourceReference{}, + Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, + Type: "string", + Mandatory: false, + Aliases: []config.Alias{}, + Default: os.Getenv("PIPER_applicationName"), + }, { Name: "timeoutMinutes", ResourceRef: []config.ResourceReference{}, diff --git a/cmd/protecodeExecuteScan_test.go b/cmd/protecodeExecuteScan_test.go index 1da9161da1..2739a2c976 100644 --- a/cmd/protecodeExecuteScan_test.go +++ b/cmd/protecodeExecuteScan_test.go @@ -273,6 +273,92 @@ func TestUploadScanOrDeclareFetch(t *testing.T) { } } +func TestApplicationName(t *testing.T) { + // init + testFile, err := ioutil.TempFile("", "testFileUpload") + require.NoError(t, err) + defer os.RemoveAll(testFile.Name()) // clean up + fileName := filepath.Base(testFile.Name()) + path := strings.ReplaceAll(testFile.Name(), fileName, "") + + var passedHeaders = map[string][]string{} + + requestURI := "" + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + requestURI = req.RequestURI + + passedHeaders = map[string][]string{} + if req.Header != nil { + for name, headers := range req.Header { + passedHeaders[name] = headers + } + } + + response := protecode.ResultData{Result: protecode.Result{ProductID: 4711, ReportURL: requestURI}} + + var b bytes.Buffer + json.NewEncoder(&b).Encode(&response) + rw.Write([]byte(b.Bytes())) + })) + + // Close the server when test finishes + defer server.Close() + + po := protecode.Options{ServerURL: server.URL} + pc := protecode.Protecode{} + pc.SetOptions(po) + + utils := protecodeTestUtilsBundle{ + FilesMock: &mock.FilesMock{}, + DownloadMock: &mock.DownloadMock{}, + } + + cases := []struct { + group string + fetchURL string + filePath string + appName string + prName string + productID int + want string + }{ + {"group1", "http://local/binary.tar", "", "", "", 4711, ""}, + {"group1", "http://local/binary.tar", "", "", "PR_4444", 4711, ""}, + {"group1", "http://local/binary.tar", "", "appName", "", 4711, "appName"}, + {"group1", "http://local/binary.tar", "", "appName", "PR_4444", 4711, "appName"}, + + {"group1", "", path, "", "", 4711, fmt.Sprintf("/api/upload/%v", fileName)}, + {"group1", "", path, "", "PR_4444", 4711, fmt.Sprintf("/api/upload/PR_4444_%v", fileName)}, + {"group1", "", path, "appName", "", 4711, "/api/upload/appName"}, + {"group1", "", path, "appName", "PR_4444", 4711, "/api/upload/PR_4444_appName"}, + } + + for i, c := range cases { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + // test + config := protecodeExecuteScanOptions{Group: c.group, FetchURL: c.fetchURL, FilePath: c.filePath, ApplicationName: c.appName, PullRequestName: c.prName} + _ = uploadFile(utils, config, c.productID, pc, fileName, false) + + // assert + if (len(c.fetchURL) > 0) { + assert.Equal(t, requestURI, "/api/fetch/") + assert.Equal(t, passedHeaders["Url"], []string{c.fetchURL}) + if (len(c.appName) > 0) { + assert.Contains(t, passedHeaders, "Name") + assert.Equal(t, []string{c.want}, passedHeaders["Name"]) + } else { + assert.NotContains(t, passedHeaders, "Name") + } + } else { + assert.Equal(t, c.want, requestURI) + } + assert.Contains(t, passedHeaders, "Group") + assert.Contains(t, passedHeaders, "Delete-Binary") + + }) + } +} + func TestExecuteProtecodeScan(t *testing.T) { testDataFile := filepath.Join("testdata", "TestProtecode", "protecode_result_violations.json") violationsAbsPath, err := filepath.Abs(testDataFile) diff --git a/pkg/protecode/protecode.go b/pkg/protecode/protecode.go index 1dadc5c8b3..3cb77b0091 100644 --- a/pkg/protecode/protecode.go +++ b/pkg/protecode/protecode.go @@ -371,27 +371,25 @@ func (pc *Protecode) UploadScanFile(cleanupMode, group, filePath, fileName, vers } // DeclareFetchURL configures the fetch url for the protecode scan -func (pc *Protecode) DeclareFetchURL(cleanupMode, group, fetchURL, version string, productID int, replaceBinary bool) *ResultData { +func (pc *Protecode) DeclareFetchURL(cleanupMode, group, fetchURL, version string, productID int, replaceBinary bool, applicationName string) *ResultData { + log.Entry().Debugf("[DEBUG] ===> DeclareFetchURL started.....") deleteBinary := (cleanupMode == "binary" || cleanupMode == "complete") - var headers = make(map[string][]string) + headers := map[string][]string{"Group": {group}, "Delete-Binary": {fmt.Sprintf("%v", deleteBinary)}, "Url": {fetchURL}, "Content-Type": {"application/json"}} - if (replaceBinary) && (version != "") { - log.Entry().Debugf("[DEBUG][FETCH_URL] ===> replaceBinary && version != empty ") - headers = map[string][]string{"Group": {group}, "Delete-Binary": {fmt.Sprintf("%v", deleteBinary)}, "Replace": {fmt.Sprintf("%v", productID)}, "Version": {version}, "Url": {fetchURL}, "Content-Type": {"application/json"}} - } else if replaceBinary { - log.Entry().Debugf("[DEBUG][FETCH_URL] ===> replaceBinary") - headers = map[string][]string{"Group": {group}, "Delete-Binary": {fmt.Sprintf("%v", deleteBinary)}, "Replace": {fmt.Sprintf("%v", productID)}, "Url": {fetchURL}, "Content-Type": {"application/json"}} - } else if version != "" { - log.Entry().Debugf("[DEBUG][FETCH_URL] ===> version != empty ") - headers = map[string][]string{"Group": {group}, "Delete-Binary": {fmt.Sprintf("%v", deleteBinary)}, "Version": {version}, "Url": {fetchURL}, "Content-Type": {"application/json"}} - } else { - log.Entry().Debugf("[DEBUG][FETCH_URL] ===> replaceBinary is false and version == empty") - headers = map[string][]string{"Group": {group}, "Delete-Binary": {fmt.Sprintf("%v", deleteBinary)}, "Url": {fetchURL}, "Content-Type": {"application/json"}} + if (replaceBinary) { + headers["Replace"] = []string{fmt.Sprintf("%v", productID)} + } + + if version != "" { + headers["Version"] = []string{version} + } + + if ( len(applicationName) > 0) { + headers["Name"] = []string{applicationName} } - // log.Entry().Debugf("[DEBUG] ===> Headers for fetch upload: %v", headers) - //headers := map[string][]string{"Group": {group}, "Delete-Binary": {fmt.Sprintf("%v", deleteBinary)}, "Url": {fetchURL}, "Content-Type": {"application/json"}} + log.Entry().Debugf("[DEBUG] ===> Headers for DeclareFetchURL: %v", headers) protecodeURL := fmt.Sprintf("%v/api/fetch/", pc.serverURL) r, statusCode, err := pc.sendAPIRequest(http.MethodPost, protecodeURL, headers) diff --git a/pkg/protecode/protecode_test.go b/pkg/protecode/protecode_test.go index 7ac489bde7..1dbdf68ab4 100644 --- a/pkg/protecode/protecode_test.go +++ b/pkg/protecode/protecode_test.go @@ -310,26 +310,30 @@ func TestDeclareFetchURLSuccess(t *testing.T) { pc := makeProtecode(Options{ServerURL: server.URL}) cases := []struct { - cleanupMode string - protecodeGroup string - fetchURL string - version string - productID int - replaceBinary bool - want int + cleanupMode string + protecodeGroup string + fetchURL string + version string + productID int + replaceBinary bool + applicationName string + want int }{ - {"binary", "group1", "/api/fetch/", "", 1, true, 111}, - {"binary", "group1", "/api/fetch/", "custom-test-version", -1, true, 111}, - {"binary", "group1", "/api/fetch/", "1.2.3", 0, true, 111}, + {"binary", "group1", "/api/fetch/", "", 1, true, "", 111}, + {"binary", "group1", "/api/fetch/", "custom-test-version", -1, true, "", 111}, + {"binary", "group1", "/api/fetch/", "1.2.3", 0, true, "", 111}, - {"binary", "group1", "/api/fetch/", "", 1, false, 111}, - {"binary", "group1", "/api/fetch/", "custom-test-version", -1, false, 111}, - {"binary", "group1", "/api/fetch/", "1.2.3", 0, false, 111}, + {"binary", "group1", "/api/fetch/", "", 1, false, "", 111}, + {"binary", "group1", "/api/fetch/", "custom-test-version", -1, false, "", 111}, + {"binary", "group1", "/api/fetch/", "1.2.3", 0, false, "", 111}, + + {"binary", "group1", "/api/fetch/", "", 0, true, "customAppName", 111}, + {"binary", "group1", "/api/fetch/", "1.2.3", 0, true, "customAppName", 111}, } for _, c := range cases { // pc.DeclareFetchURL(c.cleanupMode, c.protecodeGroup, c.fetchURL) - got := pc.DeclareFetchURL(c.cleanupMode, c.protecodeGroup, c.fetchURL, c.version, c.productID, c.replaceBinary) + got := pc.DeclareFetchURL(c.cleanupMode, c.protecodeGroup, c.fetchURL, c.version, c.productID, c.replaceBinary, c.applicationName) assert.Equal(t, requestURI, "/api/fetch/") assert.Equal(t, got.Result.ProductID, c.want) @@ -343,6 +347,9 @@ func TestDeclareFetchURLSuccess(t *testing.T) { assert.Contains(t, passedHeaders, "Replace") } + if len(c.applicationName) > 0 { + assert.Contains(t, passedHeaders, "Name") + } } } diff --git a/resources/metadata/protecodeExecuteScan.yaml b/resources/metadata/protecodeExecuteScan.yaml index 064576c879..f0d9aa5f1b 100644 --- a/resources/metadata/protecodeExecuteScan.yaml +++ b/resources/metadata/protecodeExecuteScan.yaml @@ -99,6 +99,13 @@ spec: - PARAMETERS - STAGES - STEPS + - name: applicationName + type: string + description: The Protecode application name, by default the uploaded filename will be use + scope: + - PARAMETERS + - STAGES + - STEPS - name: timeoutMinutes aliases: - name: protecodeTimeoutMinutes