github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/checkmarx/checkmarx.go (about) 1 package checkmarx 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "net/http" 9 "net/url" 10 "strconv" 11 "strings" 12 "time" 13 14 "encoding/xml" 15 16 piperHttp "github.com/SAP/jenkins-library/pkg/http" 17 "github.com/SAP/jenkins-library/pkg/log" 18 "github.com/SAP/jenkins-library/pkg/piperutils" 19 "github.com/pkg/errors" 20 "github.com/sirupsen/logrus" 21 ) 22 23 // ReportsDirectory defines the subfolder for the Checkmarx reports which are generated 24 const ReportsDirectory = "checkmarx" 25 26 // AuthToken - Structure to store OAuth2 token 27 type AuthToken struct { 28 TokenType string `json:"token_type"` 29 AccessToken string `json:"access_token"` 30 ExpiresIn int `json:"expires_in"` 31 } 32 33 // Preset - Project's Preset 34 type Preset struct { 35 ID int `json:"id"` 36 Name string `json:"name"` 37 OwnerName string `json:"ownerName"` 38 Link Link `json:"link"` 39 } 40 41 // Scan - Scan Structure 42 type Scan struct { 43 ID int `json:"id"` 44 Link Link `json:"link"` 45 } 46 47 // ProjectCreateResult - ProjectCreateResult Structure 48 type ProjectCreateResult struct { 49 ID int `json:"id"` 50 Link Link `json:"link"` 51 } 52 53 // ProjectBranchingResponse - ProjectBranchingResponse Structure 54 type ProjectBranchingResponse struct { 55 ID int `json:"id"` 56 Link Link `json:"link"` 57 } 58 59 type BranchingStatus struct { 60 ID int `json:"id"` 61 Value string `json:"value"` 62 } 63 64 // ProjectBranchingStatusResponse - ProjectBranchingStatusResponse Structure 65 type ProjectBranchingStatusResponse struct { 66 ID int `json:"id"` 67 OriginalProjectId int `json:"originalProjectId"` 68 BranchedProjectId int `json:"branchedProjectId"` 69 Status BranchingStatus `json:"status"` 70 ErrorMessage string `json:"errorMessage"` 71 } 72 73 // Report - Report Structure 74 type Report struct { 75 ReportID int `json:"reportId"` 76 Links Links `json:"links"` 77 } 78 79 // ResultsStatistics - ResultsStatistics Structure 80 type ResultsStatistics struct { 81 High int `json:"highSeverity"` 82 Medium int `json:"mediumSeverity"` 83 Low int `json:"lowSeverity"` 84 Info int `json:"infoSeverity"` 85 } 86 87 // ScanStatus - ScanStatus Structure 88 type ScanStatus struct { 89 ID int `json:"id"` 90 Link Link `json:"link"` 91 Status Status `json:"status"` 92 ScanType string `json:"scanType"` 93 Comment string `json:"comment"` 94 IsIncremental bool `json:"isIncremental"` 95 } 96 97 // Status - Status Structure 98 type Status struct { 99 ID int `json:"id"` 100 Name string `json:"name"` 101 Details ScanStatusDetail `json:"details"` 102 } 103 104 // ScanStatusDetail - ScanStatusDetail Structure 105 type ScanStatusDetail struct { 106 Stage string `json:"stage"` 107 Step string `json:"step"` 108 } 109 110 // ReportStatusResponse - ReportStatusResponse Structure 111 type ReportStatusResponse struct { 112 Location string `json:"location"` 113 ContentType string `json:"contentType"` 114 Status ReportStatus `json:"status"` 115 } 116 117 // ReportStatus - ReportStatus Structure 118 type ReportStatus struct { 119 ID int `json:"id"` 120 Value string `json:"value"` 121 } 122 123 // Project - Project Structure 124 type Project struct { 125 ID int `json:"id"` 126 TeamID string `json:"teamId"` 127 Name string `json:"name"` 128 IsPublic bool `json:"isPublic"` 129 SourceSettingsLink SourceSettingsLink `json:"sourceSettingsLink"` 130 Link Link `json:"link"` 131 } 132 133 // ScanSettings - scan settings at project level 134 type ScanSettings struct { 135 Project ProjectLink `json:"project"` 136 Preset PresetLink `json:"preset"` 137 EngineConfiguration EngineConfigurationLink `json:"engineConfiguration" ` 138 } 139 140 // ProjectLink - project link found in ScanSettings response 141 type ProjectLink struct { 142 ProjectID int `json:"id"` 143 Link Link `json:"link"` 144 } 145 146 // PresetLink - preset link found in ScanSettings response 147 type PresetLink struct { 148 PresetID int `json:"id"` 149 Link Link `json:"link"` 150 } 151 152 // EngineConfigurationLink - engine configuration link found in ScanSettings response 153 type EngineConfigurationLink struct { 154 EngineConfigurationID int `json:"id"` 155 Link Link `json:"link"` 156 } 157 158 // Team - Team Structure 159 type Team struct { 160 ID json.RawMessage `json:"id"` 161 FullName string `json:"fullName"` 162 } 163 164 // Links - Links Structure 165 type Links struct { 166 Report Link `json:"report"` 167 Status Link `json:"status"` 168 } 169 170 // Link - Link Structure 171 type Link struct { 172 Rel string `json:"rel"` 173 URI string `json:"uri"` 174 } 175 176 // SourceSettingsLink - SourceSettingsLink Structure 177 type SourceSettingsLink struct { 178 Type string `json:"type"` 179 Rel string `json:"rel"` 180 URI string `json:"uri"` 181 } 182 183 type ShortDescription struct { 184 Text string `json:"shortDescription"` 185 } 186 187 // DetailedResult - DetailedResult Structure 188 type DetailedResult struct { 189 XMLName xml.Name `xml:"CxXMLResults"` 190 InitiatorName string `xml:"InitiatorName,attr"` 191 ScanID string `xml:"ScanId,attr"` 192 Owner string `xml:"Owner,attr"` 193 ProjectID string `xml:"ProjectId,attr"` 194 ProjectName string `xml:"ProjectName,attr"` 195 TeamFullPathOnReportDate string `xml:"TeamFullPathOnReportDate,attr"` 196 DeepLink string `xml:"DeepLink,attr"` 197 ScanStart string `xml:"ScanStart,attr"` 198 Preset string `xml:"Preset,attr"` 199 ScanTime string `xml:"ScanTime,attr"` 200 LinesOfCodeScanned int `xml:"LinesOfCodeScanned,attr"` 201 FilesScanned int `xml:"FilesScanned,attr"` 202 ReportCreationTime string `xml:"ReportCreationTime,attr"` 203 Team string `xml:"Team,attr"` 204 CheckmarxVersion string `xml:"CheckmarxVersion,attr"` 205 ScanType string `xml:"ScanType,attr"` 206 SourceOrigin string `xml:"SourceOrigin,attr"` 207 Visibility string `xml:"Visibility,attr"` 208 Queries []Query `xml:"Query"` 209 } 210 211 // Query - Query Structure 212 type Query struct { 213 XMLName xml.Name `xml:"Query"` 214 Name string `xml:"name,attr"` 215 Results []Result `xml:"Result"` 216 } 217 218 // Result - Result Structure 219 type Result struct { 220 XMLName xml.Name `xml:"Result"` 221 State string `xml:"state,attr"` 222 Severity string `xml:"Severity,attr"` 223 FalsePositive string `xml:"FalsePositive,attr"` 224 } 225 226 // SystemInstance is the client communicating with the Checkmarx backend 227 type SystemInstance struct { 228 serverURL string 229 username string 230 password string 231 client piperHttp.Uploader 232 logger *logrus.Entry 233 } 234 235 // System is the interface abstraction of a specific SystemIns 236 type System interface { 237 FilterPresetByName(presets []Preset, presetName string) Preset 238 FilterPresetByID(presets []Preset, presetID int) Preset 239 FilterProjectByName(projects []Project, projectName string) Project 240 FilterTeamByName(teams []Team, teamName string) (Team, error) 241 FilterTeamByID(teams []Team, teamID json.RawMessage) Team 242 DownloadReport(reportID int) ([]byte, error) 243 GetReportStatus(reportID int) (ReportStatusResponse, error) 244 RequestNewReport(scanID int, reportType string) (Report, error) 245 GetResults(scanID int) ResultsStatistics 246 GetScanStatusAndDetail(scanID int) (string, ScanStatusDetail) 247 GetScans(projectID int) ([]ScanStatus, error) 248 ScanProject(projectID int, isIncremental, isPublic, forceScan bool) (Scan, error) 249 UpdateProjectConfiguration(projectID int, presetID int, engineConfigurationID string) error 250 UpdateProjectExcludeSettings(projectID int, excludeFolders string, excludeFiles string) error 251 UploadProjectSourceCode(projectID int, zipFile string) error 252 CreateProject(projectName, teamID string) (ProjectCreateResult, error) 253 CreateBranch(projectID int, branchName string) int 254 GetPresets() []Preset 255 GetProjectByID(projectID int) (Project, error) 256 GetProjectsByNameAndTeam(projectName, teamID string) ([]Project, error) 257 GetProjects() ([]Project, error) 258 GetShortDescription(scanID int, pathID int) (ShortDescription, error) 259 GetTeams() []Team 260 } 261 262 // NewSystemInstance returns a new Checkmarx client for communicating with the backend 263 func NewSystemInstance(client piperHttp.Uploader, serverURL, username, password string) (*SystemInstance, error) { 264 loggerInstance := log.Entry().WithField("package", "SAP/jenkins-library/pkg/checkmarx") 265 sys := &SystemInstance{ 266 serverURL: serverURL, 267 username: username, 268 password: password, 269 client: client, 270 logger: loggerInstance, 271 } 272 273 token, err := sys.getOAuth2Token() 274 if err != nil { 275 return sys, errors.Wrap(err, "Error fetching oAuth token") 276 } 277 278 log.RegisterSecret(token) 279 280 options := piperHttp.ClientOptions{ 281 Token: token, 282 TransportTimeout: time.Minute * 15, 283 } 284 sys.client.SetOptions(options) 285 286 return sys, nil 287 } 288 289 func sendRequest(sys *SystemInstance, method, url string, body io.Reader, header http.Header) ([]byte, error) { 290 return sendRequestInternal(sys, method, url, body, header, []int{}) 291 } 292 293 func sendRequestInternal(sys *SystemInstance, method, url string, body io.Reader, header http.Header, acceptedErrorCodes []int) ([]byte, error) { 294 var requestBody io.Reader 295 var requestBodyCopy io.Reader 296 if body != nil { 297 closer := io.NopCloser(body) 298 bodyBytes, _ := io.ReadAll(closer) 299 requestBody = bytes.NewBuffer(bodyBytes) 300 requestBodyCopy = bytes.NewBuffer(bodyBytes) 301 defer closer.Close() 302 } 303 response, err := sys.client.SendRequest(method, fmt.Sprintf("%v/cxrestapi%v", sys.serverURL, url), requestBody, header, nil) 304 if err != nil && (response == nil || !piperutils.ContainsInt(acceptedErrorCodes, response.StatusCode)) { 305 sys.recordRequestDetailsInErrorCase(requestBodyCopy, response) 306 sys.logger.Errorf("HTTP request failed with error: %s", err) 307 return nil, err 308 } 309 310 data, _ := io.ReadAll(response.Body) 311 sys.logger.Debugf("Valid response body: %v", string(data)) 312 defer response.Body.Close() 313 return data, nil 314 } 315 316 func (sys *SystemInstance) recordRequestDetailsInErrorCase(requestBody io.Reader, response *http.Response) { 317 if requestBody != nil { 318 data, _ := io.ReadAll(io.NopCloser(requestBody)) 319 sys.logger.Errorf("Request body: %s", data) 320 } 321 if response != nil && response.Body != nil { 322 data, _ := io.ReadAll(response.Body) 323 sys.logger.Errorf("Response body: %s", data) 324 response.Body.Close() 325 } 326 } 327 328 func (sys *SystemInstance) getOAuth2Token() (string, error) { 329 body := url.Values{ 330 "username": {sys.username}, 331 "password": {sys.password}, 332 "grant_type": {"password"}, 333 "scope": {"sast_rest_api"}, 334 "client_id": {"resource_owner_client"}, 335 "client_secret": {"014DF517-39D1-4453-B7B3-9930C563627C"}, 336 } 337 header := http.Header{} 338 header.Add("Content-type", "application/x-www-form-urlencoded") 339 data, err := sendRequest(sys, http.MethodPost, "/auth/identity/connect/token", strings.NewReader(body.Encode()), header) 340 if err != nil { 341 return "", err 342 } 343 344 var token AuthToken 345 json.Unmarshal(data, &token) 346 return token.TokenType + " " + token.AccessToken, nil 347 } 348 349 // GetTeams returns the teams the user is assigned to 350 func (sys *SystemInstance) GetTeams() []Team { 351 sys.logger.Debug("Getting Teams...") 352 var teams []Team 353 354 data, err := sendRequest(sys, http.MethodGet, "/auth/teams", nil, nil) 355 if err != nil { 356 sys.logger.Errorf("Fetching teams failed: %s", err) 357 return teams 358 } 359 360 json.Unmarshal(data, &teams) 361 return teams 362 } 363 364 // GetProjects returns the projects defined in the Checkmarx backend which the user has access to 365 func (sys *SystemInstance) GetProjects() ([]Project, error) { 366 return sys.GetProjectsByNameAndTeam("", "") 367 } 368 369 // GetProjectByID returns the project addressed by projectID from the Checkmarx backend which the user has access to 370 func (sys *SystemInstance) GetProjectByID(projectID int) (Project, error) { 371 sys.logger.Debugf("Getting Project with ID %v...", projectID) 372 var project Project 373 374 data, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("/projects/%v", projectID), nil, nil) 375 if err != nil { 376 return project, errors.Wrapf(err, "fetching project %v failed", projectID) 377 } 378 379 json.Unmarshal(data, &project) 380 return project, nil 381 } 382 383 // GetProjectsByNameAndTeam returns the project addressed by projectID from the Checkmarx backend which the user has access to 384 func (sys *SystemInstance) GetProjectsByNameAndTeam(projectName, teamID string) ([]Project, error) { 385 sys.logger.Debugf("Getting projects with name %v of team %v...", projectName, teamID) 386 var projects []Project 387 header := http.Header{} 388 header.Set("Accept-Type", "application/json") 389 var data []byte 390 var err error 391 if len(teamID) > 0 && len(projectName) > 0 { 392 body := url.Values{ 393 "projectName": {projectName}, 394 "teamId": {teamID}, 395 } 396 data, err = sendRequestInternal(sys, http.MethodGet, fmt.Sprintf("/projects?%v", body.Encode()), nil, header, []int{404}) 397 } else { 398 data, err = sendRequestInternal(sys, http.MethodGet, "/projects", nil, header, []int{404}) 399 } 400 if err != nil { 401 return projects, errors.Wrapf(err, "fetching project %v failed", projectName) 402 } 403 404 json.Unmarshal(data, &projects) 405 return projects, nil 406 } 407 408 // CreateProject creates a new project in the Checkmarx backend 409 func (sys *SystemInstance) CreateProject(projectName, teamID string) (ProjectCreateResult, error) { 410 var result ProjectCreateResult 411 jsonData := map[string]interface{}{ 412 "name": projectName, 413 "owningTeam": teamID, 414 "isPublic": true, 415 } 416 417 jsonValue, err := json.Marshal(jsonData) 418 if err != nil { 419 return result, errors.Wrapf(err, "failed to marshal project data") 420 } 421 422 header := http.Header{} 423 header.Set("Content-Type", "application/json") 424 425 data, err := sendRequest(sys, http.MethodPost, "/projects", bytes.NewBuffer(jsonValue), header) 426 if err != nil { 427 return result, errors.Wrapf(err, "failed to create project %v", projectName) 428 } 429 430 json.Unmarshal(data, &result) 431 return result, nil 432 } 433 434 // CreateBranch creates a branch of an existing project in the Checkmarx backend 435 func (sys *SystemInstance) CreateBranch(projectID int, branchName string) int { 436 jsonData := map[string]interface{}{ 437 "name": branchName, 438 } 439 440 jsonValue, err := json.Marshal(jsonData) 441 if err != nil { 442 sys.logger.Errorf("Error Marshal: %s", err) 443 return 0 444 } 445 446 header := http.Header{} 447 header.Set("Content-Type", "application/json") 448 data, err := sendRequest(sys, http.MethodPost, fmt.Sprintf("/projects/%v/branch", projectID), bytes.NewBuffer(jsonValue), header) 449 if err != nil { 450 sys.logger.Errorf("Failed to create project: %s", err) 451 return 0 452 } 453 454 var branchingResponse ProjectBranchingResponse 455 json.Unmarshal(data, &branchingResponse) 456 branchedProjectId := branchingResponse.ID 457 458 branchingStatusId := 0 // 0 Started, 1 InProgress, 2 Completed, 3 Failed 459 i := 0 460 for branchingStatusId != 2 { 461 dataBranchingStatus, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("/projects/branch/%v", branchedProjectId), nil, header) 462 if err != nil { 463 sys.logger.Warnf("Failed to poll status of branching process: %s", err) 464 } else { 465 var branchingStatusResponse ProjectBranchingStatusResponse 466 json.Unmarshal(dataBranchingStatus, &branchingStatusResponse) 467 branchingStatusId = branchingStatusResponse.Status.ID 468 branchingStatusValue := branchingStatusResponse.Status.Value 469 sys.logger.Debugf("Branching process status: %s", branchingStatusValue) 470 if branchingStatusId == 2 { 471 sys.logger.Debug("Branching process completed successfuly") 472 break 473 } else if branchingStatusId == 3 { 474 sys.logger.Errorf("Branching process failed. Error is: %s", branchingStatusResponse.ErrorMessage) 475 return 0 476 } 477 } 478 if i >= 30 { 479 // time out after 5 minutes 480 sys.logger.Errorf("Branching process timed out.") 481 return 0 482 } 483 i++ 484 time.Sleep(10 * time.Second) 485 } 486 return branchedProjectId 487 } 488 489 // UploadProjectSourceCode zips and uploads the project sources for scanning 490 func (sys *SystemInstance) UploadProjectSourceCode(projectID int, zipFile string) error { 491 sys.logger.Debug("Starting to upload files...") 492 493 header := http.Header{} 494 header.Add("Accept-Encoding", "gzip,deflate") 495 header.Add("Accept", "text/plain") 496 resp, err := sys.client.UploadFile(fmt.Sprintf("%v/cxrestapi/projects/%v/sourceCode/attachments", sys.serverURL, projectID), zipFile, "zippedSource", header, nil, "form") 497 if err != nil { 498 return errors.Wrap(err, "failed to uploaded zipped sources") 499 } 500 501 data, err := io.ReadAll(resp.Body) 502 defer resp.Body.Close() 503 if err != nil { 504 return errors.Wrap(err, "error reading the response data") 505 } 506 507 responseData := make(map[string]string) 508 json.Unmarshal(data, &responseData) 509 510 if resp.StatusCode == http.StatusNoContent { 511 return nil 512 } 513 514 sys.logger.Debugf("Body %s", data) 515 return errors.Wrapf(err, "error writing the request's body, status: %s", resp.Status) 516 } 517 518 // UpdateProjectExcludeSettings updates the exclude configuration of the project 519 func (sys *SystemInstance) UpdateProjectExcludeSettings(projectID int, excludeFolders string, excludeFiles string) error { 520 jsonData := map[string]string{ 521 "excludeFoldersPattern": excludeFolders, 522 "excludeFilesPattern": excludeFiles, 523 } 524 525 jsonValue, err := json.Marshal(jsonData) 526 if err != nil { 527 return errors.Wrap(err, "error marhalling project exclude settings") 528 } 529 530 header := http.Header{} 531 header.Set("Content-Type", "application/json") 532 _, err = sendRequest(sys, http.MethodPut, fmt.Sprintf("/projects/%v/sourceCode/excludeSettings", projectID), bytes.NewBuffer(jsonValue), header) 533 if err != nil { 534 return errors.Wrap(err, "request to checkmarx system failed") 535 } 536 537 return nil 538 } 539 540 // GetPresets loads the preset values defined in the Checkmarx backend 541 func (sys *SystemInstance) GetPresets() []Preset { 542 sys.logger.Debug("Getting Presets...") 543 var presets []Preset 544 545 data, err := sendRequest(sys, http.MethodGet, "/sast/presets", nil, nil) 546 if err != nil { 547 sys.logger.Errorf("Fetching presets failed: %s", err) 548 return presets 549 } 550 551 json.Unmarshal(data, &presets) 552 return presets 553 } 554 555 // UpdateProjectConfiguration updates the configuration of the project addressed by projectID 556 func (sys *SystemInstance) UpdateProjectConfiguration(projectID int, presetID int, engineConfigurationID string) error { 557 engineConfigID, _ := strconv.Atoi(engineConfigurationID) 558 559 var projectScanSettings ScanSettings 560 header := http.Header{} 561 header.Set("Content-Type", "application/json") 562 data, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("/sast/scanSettings/%v", projectID), nil, header) 563 if err != nil { 564 // if an error happens, try to update the config anyway 565 sys.logger.Warnf("Failed to fetch scan settings of project %v: %s", projectID, err) 566 } else { 567 // Check if the current project config needs to be updated 568 json.Unmarshal(data, &projectScanSettings) 569 if projectScanSettings.Preset.PresetID == presetID && (projectScanSettings.EngineConfiguration.EngineConfigurationID == engineConfigID || engineConfigID == 0) { 570 sys.logger.Debugf("Project configuration does not need to be updated") 571 return nil 572 } 573 } 574 575 // use the project-level value to configure the project if no value was provided in piper config 576 if engineConfigID == 0 { 577 engineConfigID = projectScanSettings.EngineConfiguration.EngineConfigurationID 578 } 579 580 jsonData := map[string]interface{}{ 581 "projectId": projectID, 582 "presetId": presetID, 583 "engineConfigurationId": engineConfigID, 584 } 585 586 jsonValue, err := json.Marshal(jsonData) 587 if err != nil { 588 return errors.Wrapf(err, "error marshalling project data") 589 } 590 591 _, err = sendRequest(sys, http.MethodPost, "/sast/scanSettings", bytes.NewBuffer(jsonValue), header) 592 if err != nil { 593 return errors.Wrapf(err, "request to checkmarx system failed") 594 } 595 sys.logger.Debugf("Project configuration updated") 596 597 return nil 598 } 599 600 // ScanProject triggers a scan on the project addressed by projectID 601 func (sys *SystemInstance) ScanProject(projectID int, isIncremental, isPublic, forceScan bool) (Scan, error) { 602 scan := Scan{} 603 jsonData := map[string]interface{}{ 604 "projectId": projectID, 605 "isIncremental": isIncremental, 606 "isPublic": isPublic, 607 "forceScan": forceScan, 608 "comment": "Scan From Golang Script", 609 } 610 611 jsonValue, _ := json.Marshal(jsonData) 612 613 header := http.Header{} 614 header.Set("cxOrigin", "GolangScript") 615 header.Set("Content-Type", "application/json") 616 data, err := sendRequest(sys, http.MethodPost, "/sast/scans", bytes.NewBuffer(jsonValue), header) 617 if err != nil { 618 sys.logger.Errorf("Failed to trigger scan of project %v: %s", projectID, err) 619 return scan, errors.Wrapf(err, "Failed to trigger scan of project %v", projectID) 620 } 621 622 json.Unmarshal(data, &scan) 623 return scan, nil 624 } 625 626 // GetScans returns all scan status on the project addressed by projectID 627 func (sys *SystemInstance) GetScans(projectID int) ([]ScanStatus, error) { 628 scans := []ScanStatus{} 629 body := url.Values{ 630 "projectId": {fmt.Sprintf("%v", projectID)}, 631 "last": {fmt.Sprintf("%v", 20)}, 632 } 633 634 header := http.Header{} 635 header.Set("cxOrigin", "GolangScript") 636 header.Set("Accept-Type", "application/json") 637 data, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("/sast/scans?%v", body.Encode()), nil, header) 638 if err != nil { 639 sys.logger.Errorf("Failed to fetch scans of project %v: %s", projectID, err) 640 return scans, errors.Wrapf(err, "failed to fetch scans of project %v", projectID) 641 } 642 643 json.Unmarshal(data, &scans) 644 return scans, nil 645 } 646 647 // GetScanStatusAndDetail returns the status of the scan addressed by scanID 648 func (sys *SystemInstance) GetScanStatusAndDetail(scanID int) (string, ScanStatusDetail) { 649 var scanStatus ScanStatus 650 651 data, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("/sast/scans/%v", scanID), nil, nil) 652 if err != nil { 653 sys.logger.Errorf("Failed to get scan status for scanID %v: %s", scanID, err) 654 return "Failed", ScanStatusDetail{} 655 } 656 657 json.Unmarshal(data, &scanStatus) 658 return scanStatus.Status.Name, scanStatus.Status.Details 659 } 660 661 // GetResults returns the results of the scan addressed by scanID 662 func (sys *SystemInstance) GetResults(scanID int) ResultsStatistics { 663 var results ResultsStatistics 664 665 data, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("/sast/scans/%v/resultsStatistics", scanID), nil, nil) 666 if err != nil { 667 sys.logger.Errorf("Failed to fetch scan results for scanID %v: %s", scanID, err) 668 return results 669 } 670 671 json.Unmarshal(data, &results) 672 return results 673 } 674 675 // RequestNewReport triggers the generation of a report for a specific scan addressed by scanID 676 func (sys *SystemInstance) RequestNewReport(scanID int, reportType string) (Report, error) { 677 report := Report{} 678 jsonData := map[string]interface{}{ 679 "scanId": scanID, 680 "reportType": reportType, 681 "comment": "Scan report triggered by Piper", 682 } 683 684 jsonValue, _ := json.Marshal(jsonData) 685 686 header := http.Header{} 687 header.Set("cxOrigin", "GolangScript") 688 header.Set("Content-Type", "application/json") 689 data, err := sendRequest(sys, http.MethodPost, "/reports/sastScan", bytes.NewBuffer(jsonValue), header) 690 if err != nil { 691 return report, errors.Wrapf(err, "Failed to trigger report generation for scan %v", scanID) 692 } 693 694 json.Unmarshal(data, &report) 695 return report, nil 696 } 697 698 // GetReportStatus returns the status of the report generation process 699 func (sys *SystemInstance) GetReportStatus(reportID int) (ReportStatusResponse, error) { 700 var response ReportStatusResponse 701 702 header := http.Header{} 703 header.Set("Accept", "application/json") 704 data, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("/reports/sastScan/%v/status", reportID), nil, header) 705 if err != nil { 706 sys.logger.Errorf("Failed to fetch report status for reportID %v: %s", reportID, err) 707 return response, errors.Wrapf(err, "failed to fetch report status for reportID %v", reportID) 708 } 709 710 json.Unmarshal(data, &response) 711 return response, nil 712 } 713 714 // GetShortDescription returns the short description for an issue with a scanID and pathID 715 func (sys *SystemInstance) GetShortDescription(scanID int, pathID int) (ShortDescription, error) { 716 var shortDescription ShortDescription 717 718 data, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("/sast/scans/%v/results/%v/shortDescription", scanID, pathID), nil, nil) 719 if err != nil { 720 sys.logger.Errorf("Failed to get short description for scanID %v and pathID %v: %s", scanID, pathID, err) 721 return shortDescription, err 722 } 723 724 json.Unmarshal(data, &shortDescription) 725 return shortDescription, nil 726 } 727 728 // DownloadReport downloads the report addressed by reportID and returns the XML contents 729 func (sys *SystemInstance) DownloadReport(reportID int) ([]byte, error) { 730 header := http.Header{} 731 header.Set("Accept", "application/json") 732 data, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("/reports/sastScan/%v", reportID), nil, header) 733 if err != nil { 734 return []byte{}, errors.Wrapf(err, "failed to download report with reportID %v", reportID) 735 } 736 return data, nil 737 } 738 739 // FilterTeamByName filters a team by its name 740 func (sys *SystemInstance) FilterTeamByName(teams []Team, teamName string) (Team, error) { 741 for _, team := range teams { 742 if team.FullName == teamName || team.FullName == strings.ReplaceAll(teamName, `\`, `/`) { 743 return team, nil 744 } 745 } 746 return Team{}, errors.New("Failed to find team with name " + teamName) 747 } 748 749 // FilterTeamByID filters a team by its ID 750 func (sys *SystemInstance) FilterTeamByID(teams []Team, teamID json.RawMessage) Team { 751 teamIDBytes1, _ := teamID.MarshalJSON() 752 for _, team := range teams { 753 teamIDBytes2, _ := team.ID.MarshalJSON() 754 if bytes.Compare(teamIDBytes1, teamIDBytes2) == 0 { 755 return team 756 } 757 } 758 return Team{} 759 } 760 761 // FilterProjectByName filters a project by its name 762 func (sys *SystemInstance) FilterProjectByName(projects []Project, projectName string) Project { 763 for _, project := range projects { 764 if project.Name == projectName { 765 sys.logger.Debugf("Filtered project with name %v", project.Name) 766 return project 767 } 768 } 769 return Project{} 770 } 771 772 // FilterPresetByName filters a preset by its name 773 func (sys *SystemInstance) FilterPresetByName(presets []Preset, presetName string) Preset { 774 for _, preset := range presets { 775 if preset.Name == presetName { 776 return preset 777 } 778 } 779 return Preset{} 780 } 781 782 // FilterPresetByID filters a preset by its name 783 func (sys *SystemInstance) FilterPresetByID(presets []Preset, presetID int) Preset { 784 for _, preset := range presets { 785 if preset.ID == presetID { 786 return preset 787 } 788 } 789 return Preset{} 790 }