Skip to content

Commit

Permalink
CxOne: Add param to tag scan and project (#4944)
Browse files Browse the repository at this point in the history
* Initial in progress

* compiling but not yet functional

* Missed file

* updated checkmarxone step

* Working up to fetching a project then breaks

* Missed file

* Breaks when retrieving projects+proxy set

* Create project & run scan working, now polling

* Fixed polling

* added back the zipfile remove command

* Fixed polling again

* Generates and downloads PDF report

* Updated and working, prep for refactor

* Added compliance steps

* Cleanup, reporting, added groovy connector

* fixed groovy file

* checkmarxone to checkmarxOne

* checkmarxone to checkmarxOne

* split credentials (id+secret, apikey), renamed pullrequestname to branch, groovy fix

* Fixed filenames & yaml

* missed the metadata_generated.go

* added json to sarif conversion

* fix:type in new checkmarxone package

* fix:type in new checkmarxone package

* removed test logs, added temp error log for creds

* extra debugging to fix crash

* improved auth logging, fixed query parse issue

* fixed bug with group fetch when using oauth user

* CWE can be -1 if not defined, can't be uint

* Query also had CweID

* Disabled predicates-fetch in sarif generation

* Removing leftover info log message

* Better error handling

* fixed default preset configuration

* removing .bat files - sorry

* Cleanup per initial review

* refactoring per Gist, fixed project find, add apps

* small fix - sorry for commit noise while testing

* Fixing issues with incremental scans.

* removing maxretries

* Updated per PR feedback, further changes todo toda

* JSON Report changes and reporting cleanup

* removing .bat (again?)

* adding docs, groovy unit test, linter fixes

* Started adding tests maybe 15% covered

* fix(checkmarxOne): test cases for pkg and reporting

* fix(checkmarxOne):fix formatting

* feat(checkmarxone): update interface with missing method

* feat(checkmarxone):change runStep signature to be able to inject dependency

* feat(checkmarxone): add tests for step (wip)

* Adding a bit more coverage

* feat(checkmarxOne): fix code review

* feat(checkmarxOne): fix code review

* feat(checkmarxOne): fix code review

* feat(checkmarxOne): fix integration test PR

* adding scan-summary bug workaround, reportgen fail

* enforceThresholds fix when no results passed in

* fixed gap when preset empty in yaml & project conf

* fixed another gap in preset selection

* fix 0-result panic

* fail when no preset is set anywhere

* removed comment

* initial project-under-app support

* fixing sarif reportgen

* some cleanup of error messages

* post-merge test fixes

* revert previous upstream merge

* adding "incremental" to "full" triggers

* wrong boolean

* project-in-application api change prep

* Fixing SARIF report without preset access

* fix sarif deeplink

* removing comments

* fix(cxone):formatting

* fix(cxone):formatting

* small sarif fixes

* fixed merge

* attempt at pulling git source repo branch

* fix(cxone):new endpoint for project creation

* fix(cxOne): taxa is an array

* fix(cxOne): get Git branch from commonPipelineEnvironment

* fix(cxOne): add params to tag a scan and a project

* fix(cxOne): unit test - update project

* fix(cxOne): unit test - update project tags

* fix(cxOne): improve logs

* fix(cxOne): improve logs

---------

Co-authored-by: michael kubiaczyk <[email protected]>
Co-authored-by: michaelkubiaczyk <[email protected]>
Co-authored-by: sumeet patil <[email protected]>
  • Loading branch information
4 people authored Jun 3, 2024
1 parent 22ff571 commit 683ca35
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 18 deletions.
48 changes: 43 additions & 5 deletions cmd/checkmarxOneExecuteScan.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package cmd
import (
"archive/zip"
"context"
"encoding/json"
"fmt"
"io"
"maps"
"math"
"os"
"path/filepath"
Expand Down Expand Up @@ -78,9 +80,11 @@ func runStep(config checkmarxOneExecuteScanOptions, influx *checkmarxOneExecuteS
return fmt.Errorf("failed to get project: %s", err)
}

cx1sh.Group, err = cx1sh.GetGroup() // used when creating a project and when generating a SARIF report
if err != nil {
log.Entry().WithError(err).Warnf("failed to get group")
if len(config.GroupName) > 0 {
cx1sh.Group, err = cx1sh.GetGroup() // used when creating a project and when generating a SARIF report
if err != nil {
log.Entry().WithError(err).Warnf("failed to get group")
}
}

if cx1sh.Project == nil {
Expand Down Expand Up @@ -112,6 +116,14 @@ func runStep(config checkmarxOneExecuteScanOptions, influx *checkmarxOneExecuteS
return fmt.Errorf("failed to set preset: %s", err)
}

// update project's tags
if (len(config.ProjectTags)) > 0 {
err = cx1sh.UpdateProjectTags()
if err != nil {
log.Entry().WithError(err).Warnf("failed to tags the project: %s", err)
}
}

scans, err := cx1sh.GetLastScans(10)
if err != nil {
log.Entry().WithError(err).Warnf("failed to get last 10 scans")
Expand Down Expand Up @@ -298,6 +310,23 @@ func (c *checkmarxOneExecuteScanHelper) CreateProject() (*checkmarxOne.Project,
return &project, nil
}

func (c *checkmarxOneExecuteScanHelper) UpdateProjectTags() error {
if len(c.config.ProjectTags) > 0 {
tags := make(map[string]string, 0)
err := json.Unmarshal([]byte(c.config.ProjectTags), &tags)
if err != nil {
log.Entry().Infof("Failed to parse the project tags: %v", c.config.ProjectTags)
return err
}
// merge new tags to the existing ones
maps.Copy(c.Project.Tags, tags)

return c.sys.UpdateProject(c.Project)
}

return nil
}

func (c *checkmarxOneExecuteScanHelper) SetProjectPreset() error {
projectConf, err := c.sys.GetProjectConfiguration(c.Project.ProjectID)

Expand Down Expand Up @@ -431,9 +460,18 @@ func (c *checkmarxOneExecuteScanHelper) CreateScanRequest(incremental bool, uplo
log.Entry().Infof("Will run a scan with the following configuration: %v", sastConfigString)

configs := []checkmarxOne.ScanConfiguration{sastConfig}
// add more engines

scan, err := c.sys.ScanProjectZip(c.Project.ProjectID, uploadLink, branch, configs)
// add scan's tags
tags := make(map[string]string, 0)
if len(c.config.ScanTags) > 0 {
err := json.Unmarshal([]byte(c.config.ScanTags), &tags)
if err != nil {
log.Entry().WithError(err).Warnf("Failed to parse the scan tags: %v", c.config.ScanTags)
}
}

// add more engines
scan, err := c.sys.ScanProjectZip(c.Project.ProjectID, uploadLink, branch, configs, tags)

if err != nil {
return nil, fmt.Errorf("Failed to run scan on project %v: %s", c.Project.Name, err)
Expand Down
24 changes: 23 additions & 1 deletion cmd/checkmarxOneExecuteScan_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 66 additions & 3 deletions cmd/checkmarxOneExecuteScan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"maps"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -77,15 +78,15 @@ func (sys *checkmarxOneSystemMock) GetLastScansByStatus(projectID string, limit
return []checkmarxOne.Scan{}, nil
}

func (sys *checkmarxOneSystemMock) ScanProject(projectID, sourceUrl, branch, scanType string, settings []checkmarxOne.ScanConfiguration) (checkmarxOne.Scan, error) {
func (sys *checkmarxOneSystemMock) ScanProject(projectID, sourceUrl, branch, scanType string, settings []checkmarxOne.ScanConfiguration, tags map[string]string) (checkmarxOne.Scan, error) {
return checkmarxOne.Scan{}, nil
}

func (sys *checkmarxOneSystemMock) ScanProjectZip(projectID, sourceUrl, branch string, settings []checkmarxOne.ScanConfiguration) (checkmarxOne.Scan, error) {
func (sys *checkmarxOneSystemMock) ScanProjectZip(projectID, sourceUrl, branch string, settings []checkmarxOne.ScanConfiguration, tags map[string]string) (checkmarxOne.Scan, error) {
return checkmarxOne.Scan{}, nil
}

func (sys *checkmarxOneSystemMock) ScanProjectGit(projectID, repoUrl, branch string, settings []checkmarxOne.ScanConfiguration) (checkmarxOne.Scan, error) {
func (sys *checkmarxOneSystemMock) ScanProjectGit(projectID, repoUrl, branch string, settings []checkmarxOne.ScanConfiguration, tags map[string]string) (checkmarxOne.Scan, error) {
return checkmarxOne.Scan{}, nil
}

Expand Down Expand Up @@ -240,6 +241,10 @@ func (sys *checkmarxOneSystemMock) UpdateProjectConfiguration(projectID string,
return nil
}

func (sys *checkmarxOneSystemMock) UpdateProject(project *checkmarxOne.Project) error {
return nil
}

func (sys *checkmarxOneSystemMock) GetVersion() (checkmarxOne.VersionInfo, error) {
return checkmarxOne.VersionInfo{}, nil
}
Expand Down Expand Up @@ -324,3 +329,61 @@ func TestGetGroup(t *testing.T) {
assert.Equal(t, group.Name, "Group2")
})
}

func TestUpdateProjectTags(t *testing.T) {
t.Parallel()

sys := &checkmarxOneSystemMock{}

t.Run("project tags are not provided", func(t *testing.T) {
t.Parallel()

options := checkmarxOneExecuteScanOptions{ProjectName: "ssba", VulnerabilityThresholdUnit: "absolute", FullScanCycle: "2", Incremental: true, FullScansScheduled: true, Preset: "CheckmarxDefault" /*GroupName: "NotProvided",*/, VulnerabilityThresholdEnabled: true, GeneratePdfReport: true, APIKey: "testAPIKey", ServerURL: "testURL", IamURL: "testIamURL", Tenant: "testTenant"}

cx1sh := checkmarxOneExecuteScanHelper{nil, options, sys, nil, nil, nil, nil, nil, nil}
err := cx1sh.UpdateProjectTags()
assert.NoError(t, err, "Error occurred but none expected")
})

t.Run("project tags are provided correctly", func(t *testing.T) {
t.Parallel()

projectJson := `{ "id": "702ba12b-ae61-48c0-9b6a-09b17666be32",
"name": "test-apr24-piper",
"tags": {
"key1": "value1",
"key2": "value2",
"keywithoutvalue1": ""
},
"groups": [],
"criticality": 3,
"mainBranch": "",
"privatePackage": false
}`
var project checkmarxOne.Project
_ = json.Unmarshal([]byte(projectJson), &project)

options := checkmarxOneExecuteScanOptions{ProjectName: "ssba", VulnerabilityThresholdUnit: "absolute", FullScanCycle: "2", Incremental: true, FullScansScheduled: true, Preset: "CheckmarxDefault" /*GroupName: "NotProvided",*/, VulnerabilityThresholdEnabled: true, GeneratePdfReport: true, APIKey: "testAPIKey", ServerURL: "testURL", IamURL: "testIamURL", Tenant: "testTenant", ProjectTags: `{"key3":"value3", "key2":"value5", "keywithoutvalue2":""}`}

cx1sh := checkmarxOneExecuteScanHelper{nil, options, sys, nil, nil, &project, nil, nil, nil}
err := cx1sh.UpdateProjectTags()
assert.NoError(t, err, "Error occurred but none expected")

oldTagsJson := `{
"key1": "value1",
"key2": "value2",
"keywithoutvalue1": ""
}`
oldTags := make(map[string]string, 0)
_ = json.Unmarshal([]byte(oldTagsJson), &oldTags)

newTagsJson := `{"key3":"value3", "key2":"value5", "keywithoutvalue2":""}`
newTags := make(map[string]string, 0)
_ = json.Unmarshal([]byte(newTagsJson), &newTags)

// merge new tags to the existing ones
maps.Copy(oldTags, newTags)

assert.Equal(t, project.Tags, oldTags) // project's tags must be merged
})
}
35 changes: 27 additions & 8 deletions pkg/checkmarxone/checkmarxone.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,10 @@ type System interface {
GetLastScans(projectID string, limit int) ([]Scan, error)
GetLastScansByStatus(projectID string, limit int, status []string) ([]Scan, error)

ScanProject(projectID, sourceUrl, branch, scanType string, settings []ScanConfiguration) (Scan, error)
ScanProjectZip(projectID, sourceUrl, branch string, settings []ScanConfiguration) (Scan, error)
ScanProjectGit(projectID, repoUrl, branch string, settings []ScanConfiguration) (Scan, error)
ScanProject(projectID, sourceUrl, branch, scanType string, settings []ScanConfiguration, tags map[string]string) (Scan, error)
ScanProjectZip(projectID, sourceUrl, branch string, settings []ScanConfiguration, tags map[string]string) (Scan, error)
ScanProjectGit(projectID, repoUrl, branch string, settings []ScanConfiguration, tags map[string]string) (Scan, error)
UpdateProject(project *Project) error

UploadProjectSourceCode(projectID string, zipFile string) (string, error)
CreateProject(projectName string, groupIDs []string) (Project, error)
Expand Down Expand Up @@ -651,6 +652,22 @@ func (sys *SystemInstance) UpdateApplication(app *Application) error {
return nil
}

func (sys *SystemInstance) UpdateProject(project *Project) error {
sys.logger.Debugf("Updating project: %v", project.Name)
jsonBody, err := json.Marshal(*project)
if err != nil {
return err
}

_, err = sendRequest(sys, http.MethodPut, fmt.Sprintf("/projects/%v", project.ProjectID), bytes.NewReader(jsonBody), nil, []int{})
if err != nil {
sys.logger.Errorf("Error while updating project: %s", err)
return err
}

return nil
}

// Updated for Cx1
func (sys *SystemInstance) GetGroups() ([]Group, error) {
sys.logger.Debug("Getting Groups...")
Expand Down Expand Up @@ -936,7 +953,7 @@ func (sys *SystemInstance) scanProject(scanConfig map[string]interface{}) (Scan,
return scan, err
}

func (sys *SystemInstance) ScanProjectZip(projectID, sourceUrl, branch string, settings []ScanConfiguration) (Scan, error) {
func (sys *SystemInstance) ScanProjectZip(projectID, sourceUrl, branch string, settings []ScanConfiguration, tags map[string]string) (Scan, error) {
jsonBody := map[string]interface{}{
"project": map[string]interface{}{"id": projectID},
"type": "upload",
Expand All @@ -945,6 +962,7 @@ func (sys *SystemInstance) ScanProjectZip(projectID, sourceUrl, branch string, s
"branch": branch,
},
"config": settings,
"tags": tags,
}

scan, err := sys.scanProject(jsonBody)
Expand All @@ -954,7 +972,7 @@ func (sys *SystemInstance) ScanProjectZip(projectID, sourceUrl, branch string, s
return scan, err
}

func (sys *SystemInstance) ScanProjectGit(projectID, repoUrl, branch string, settings []ScanConfiguration) (Scan, error) {
func (sys *SystemInstance) ScanProjectGit(projectID, repoUrl, branch string, settings []ScanConfiguration, tags map[string]string) (Scan, error) {
jsonBody := map[string]interface{}{
"project": map[string]interface{}{"id": projectID},
"type": "git",
Expand All @@ -963,6 +981,7 @@ func (sys *SystemInstance) ScanProjectGit(projectID, repoUrl, branch string, set
"branch": branch,
},
"config": settings,
"tags": tags,
}

scan, err := sys.scanProject(jsonBody)
Expand All @@ -972,11 +991,11 @@ func (sys *SystemInstance) ScanProjectGit(projectID, repoUrl, branch string, set
return scan, err
}

func (sys *SystemInstance) ScanProject(projectID, sourceUrl, branch, scanType string, settings []ScanConfiguration) (Scan, error) {
func (sys *SystemInstance) ScanProject(projectID, sourceUrl, branch, scanType string, settings []ScanConfiguration, tags map[string]string) (Scan, error) {
if scanType == "upload" {
return sys.ScanProjectZip(projectID, sourceUrl, branch, settings)
return sys.ScanProjectZip(projectID, sourceUrl, branch, settings, tags)
} else if scanType == "git" {
return sys.ScanProjectGit(projectID, sourceUrl, branch, settings)
return sys.ScanProjectGit(projectID, sourceUrl, branch, settings, tags)
}

return Scan{}, errors.New("Invalid scanType provided, must be 'upload' or 'git'")
Expand Down
46 changes: 46 additions & 0 deletions pkg/checkmarxone/checkmarxone_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package checkmarxOne

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -294,3 +295,48 @@ func TestGetApplicationByName(t *testing.T) {
assert.Contains(t, fmt.Sprint(err), "Provoked technical error")
})
}

func TestUpdateProject(t *testing.T) {
logger := log.Entry().WithField("package", "SAP/jenkins-library/pkg/checkmarxOne_test")
opts := piperHttp.ClientOptions{}

requestJson := `{ "id": "702ba12b-ae61-48c0-9b6a-09b17666be32",
"name": "test-apr24-piper",
"tags": {
"\"key1\"": "\"value1\"",
"\"keywithoutvalue\"": "\"\""
},
"groups": [],
"criticality": 3,
"mainBranch": "",
"privatePackage": false
}`
var project Project
_ = json.Unmarshal([]byte(requestJson), &project)

t.Run("test success", func(t *testing.T) {
myTestClient := senderMock{responseBody: ``, httpStatusCode: 204}
serverURL := "https://cx1.server.com"
sys := SystemInstance{serverURL: serverURL, iamURL: "https://cx1iam.server.com", tenant: "tenant", client: &myTestClient, logger: logger}
myTestClient.SetOptions(opts)

err := sys.UpdateProject(&project)
assert.NoError(t, err, "Error occurred but none expected")
assert.Equal(t, serverURL+"/api/projects/"+project.ProjectID, myTestClient.urlCalled, "Called url incorrect")
assert.Equal(t, "PUT", myTestClient.httpMethod, "HTTP method incorrect")
var body Project
_ = json.Unmarshal([]byte(myTestClient.requestBody), &body)
assert.Equal(t, project, body, "Request body incorrect")

})

t.Run("test technical error", func(t *testing.T) {
myTestClient := senderMock{httpStatusCode: 403}
sys := SystemInstance{serverURL: "https://cx1.server.com", iamURL: "https://cx1iam.server.com", tenant: "tenant", client: &myTestClient, logger: logger}
myTestClient.SetOptions(opts)
myTestClient.errorExp = true

err := sys.UpdateProject(&project)
assert.Contains(t, fmt.Sprint(err), "Provoked technical error")
})
}
Loading

0 comments on commit 683ca35

Please sign in to comment.