mirror of
https://github.com/SAP/jenkins-library.git
synced 2024-12-16 11:09:33 +02:00
2536a9f598
* feat(checkmarx): Support threshold for Low finding per Query name Co-authored-by: sumeet patil <sumeet.patil@sap.com>
738 lines
24 KiB
Go
738 lines
24 KiB
Go
package checkmarx
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"encoding/xml"
|
|
|
|
piperHttp "github.com/SAP/jenkins-library/pkg/http"
|
|
"github.com/SAP/jenkins-library/pkg/log"
|
|
"github.com/SAP/jenkins-library/pkg/piperutils"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// ReportsDirectory defines the subfolder for the Checkmarx reports which are generated
|
|
const ReportsDirectory = "checkmarx"
|
|
|
|
// AuthToken - Structure to store OAuth2 token
|
|
type AuthToken struct {
|
|
TokenType string `json:"token_type"`
|
|
AccessToken string `json:"access_token"`
|
|
ExpiresIn int `json:"expires_in"`
|
|
}
|
|
|
|
// Preset - Project's Preset
|
|
type Preset struct {
|
|
ID int `json:"id"`
|
|
Name string `json:"name"`
|
|
OwnerName string `json:"ownerName"`
|
|
Link Link `json:"link"`
|
|
}
|
|
|
|
// Scan - Scan Structure
|
|
type Scan struct {
|
|
ID int `json:"id"`
|
|
Link Link `json:"link"`
|
|
}
|
|
|
|
// ProjectCreateResult - ProjectCreateResult Structure
|
|
type ProjectCreateResult struct {
|
|
ID int `json:"id"`
|
|
Link Link `json:"link"`
|
|
}
|
|
|
|
// Report - Report Structure
|
|
type Report struct {
|
|
ReportID int `json:"reportId"`
|
|
Links Links `json:"links"`
|
|
}
|
|
|
|
// ResultsStatistics - ResultsStatistics Structure
|
|
type ResultsStatistics struct {
|
|
High int `json:"highSeverity"`
|
|
Medium int `json:"mediumSeverity"`
|
|
Low int `json:"lowSeverity"`
|
|
Info int `json:"infoSeverity"`
|
|
}
|
|
|
|
// ScanStatus - ScanStatus Structure
|
|
type ScanStatus struct {
|
|
ID int `json:"id"`
|
|
Link Link `json:"link"`
|
|
Status Status `json:"status"`
|
|
ScanType string `json:"scanType"`
|
|
Comment string `json:"comment"`
|
|
IsIncremental bool `json:"isIncremental"`
|
|
}
|
|
|
|
// Status - Status Structure
|
|
type Status struct {
|
|
ID int `json:"id"`
|
|
Name string `json:"name"`
|
|
Details ScanStatusDetail `json:"details"`
|
|
}
|
|
|
|
// ScanStatusDetail - ScanStatusDetail Structure
|
|
type ScanStatusDetail struct {
|
|
Stage string `json:"stage"`
|
|
Step string `json:"step"`
|
|
}
|
|
|
|
// ReportStatusResponse - ReportStatusResponse Structure
|
|
type ReportStatusResponse struct {
|
|
Location string `json:"location"`
|
|
ContentType string `json:"contentType"`
|
|
Status ReportStatus `json:"status"`
|
|
}
|
|
|
|
// ReportStatus - ReportStatus Structure
|
|
type ReportStatus struct {
|
|
ID int `json:"id"`
|
|
Value string `json:"value"`
|
|
}
|
|
|
|
// Project - Project Structure
|
|
type Project struct {
|
|
ID int `json:"id"`
|
|
TeamID string `json:"teamId"`
|
|
Name string `json:"name"`
|
|
IsPublic bool `json:"isPublic"`
|
|
SourceSettingsLink SourceSettingsLink `json:"sourceSettingsLink"`
|
|
Link Link `json:"link"`
|
|
}
|
|
|
|
// ScanSettings - scan settings at project level
|
|
type ScanSettings struct {
|
|
Project ProjectLink `json:"project"`
|
|
Preset PresetLink `json:"preset"`
|
|
EngineConfiguration EngineConfigurationLink `json:"engineConfiguration" `
|
|
}
|
|
|
|
// ProjectLink - project link found in ScanSettings response
|
|
type ProjectLink struct {
|
|
ProjectID int `json:"id"`
|
|
Link Link `json:"link"`
|
|
}
|
|
|
|
// PresetLink - preset link found in ScanSettings response
|
|
type PresetLink struct {
|
|
PresetID int `json:"id"`
|
|
Link Link `json:"link"`
|
|
}
|
|
|
|
// EngineConfigurationLink - engine configuration link found in ScanSettings response
|
|
type EngineConfigurationLink struct {
|
|
EngineConfigurationID int `json:"id"`
|
|
Link Link `json:"link"`
|
|
}
|
|
|
|
// Team - Team Structure
|
|
type Team struct {
|
|
ID json.RawMessage `json:"id"`
|
|
FullName string `json:"fullName"`
|
|
}
|
|
|
|
// Links - Links Structure
|
|
type Links struct {
|
|
Report Link `json:"report"`
|
|
Status Link `json:"status"`
|
|
}
|
|
|
|
// Link - Link Structure
|
|
type Link struct {
|
|
Rel string `json:"rel"`
|
|
URI string `json:"uri"`
|
|
}
|
|
|
|
// SourceSettingsLink - SourceSettingsLink Structure
|
|
type SourceSettingsLink struct {
|
|
Type string `json:"type"`
|
|
Rel string `json:"rel"`
|
|
URI string `json:"uri"`
|
|
}
|
|
|
|
type ShortDescription struct {
|
|
Text string `json:"shortDescription"`
|
|
}
|
|
|
|
//DetailedResult - DetailedResult Structure
|
|
type DetailedResult struct {
|
|
XMLName xml.Name `xml:"CxXMLResults"`
|
|
InitiatorName string `xml:"InitiatorName,attr"`
|
|
ScanID string `xml:"ScanId,attr"`
|
|
Owner string `xml:"Owner,attr"`
|
|
ProjectID string `xml:"ProjectId,attr"`
|
|
ProjectName string `xml:"ProjectName,attr"`
|
|
TeamFullPathOnReportDate string `xml:"TeamFullPathOnReportDate,attr"`
|
|
DeepLink string `xml:"DeepLink,attr"`
|
|
ScanStart string `xml:"ScanStart,attr"`
|
|
Preset string `xml:"Preset,attr"`
|
|
ScanTime string `xml:"ScanTime,attr"`
|
|
LinesOfCodeScanned int `xml:"LinesOfCodeScanned,attr"`
|
|
FilesScanned int `xml:"FilesScanned,attr"`
|
|
ReportCreationTime string `xml:"ReportCreationTime,attr"`
|
|
Team string `xml:"Team,attr"`
|
|
CheckmarxVersion string `xml:"CheckmarxVersion,attr"`
|
|
ScanType string `xml:"ScanType,attr"`
|
|
SourceOrigin string `xml:"SourceOrigin,attr"`
|
|
Visibility string `xml:"Visibility,attr"`
|
|
Queries []Query `xml:"Query"`
|
|
}
|
|
|
|
// Query - Query Structure
|
|
type Query struct {
|
|
XMLName xml.Name `xml:"Query"`
|
|
Name string `xml:"name,attr"`
|
|
Results []Result `xml:"Result"`
|
|
}
|
|
|
|
// Result - Result Structure
|
|
type Result struct {
|
|
XMLName xml.Name `xml:"Result"`
|
|
State string `xml:"state,attr"`
|
|
Severity string `xml:"Severity,attr"`
|
|
FalsePositive string `xml:"FalsePositive,attr"`
|
|
}
|
|
|
|
// SystemInstance is the client communicating with the Checkmarx backend
|
|
type SystemInstance struct {
|
|
serverURL string
|
|
username string
|
|
password string
|
|
client piperHttp.Uploader
|
|
logger *logrus.Entry
|
|
}
|
|
|
|
// System is the interface abstraction of a specific SystemIns
|
|
type System interface {
|
|
FilterPresetByName(presets []Preset, presetName string) Preset
|
|
FilterPresetByID(presets []Preset, presetID int) Preset
|
|
FilterProjectByName(projects []Project, projectName string) Project
|
|
FilterTeamByName(teams []Team, teamName string) (Team, error)
|
|
FilterTeamByID(teams []Team, teamID json.RawMessage) Team
|
|
DownloadReport(reportID int) ([]byte, error)
|
|
GetReportStatus(reportID int) (ReportStatusResponse, error)
|
|
RequestNewReport(scanID int, reportType string) (Report, error)
|
|
GetResults(scanID int) ResultsStatistics
|
|
GetScanStatusAndDetail(scanID int) (string, ScanStatusDetail)
|
|
GetScans(projectID int) ([]ScanStatus, error)
|
|
ScanProject(projectID int, isIncremental, isPublic, forceScan bool) (Scan, error)
|
|
UpdateProjectConfiguration(projectID int, presetID int, engineConfigurationID string) error
|
|
UpdateProjectExcludeSettings(projectID int, excludeFolders string, excludeFiles string) error
|
|
UploadProjectSourceCode(projectID int, zipFile string) error
|
|
CreateProject(projectName, teamID string) (ProjectCreateResult, error)
|
|
CreateBranch(projectID int, branchName string) int
|
|
GetPresets() []Preset
|
|
GetProjectByID(projectID int) (Project, error)
|
|
GetProjectsByNameAndTeam(projectName, teamID string) ([]Project, error)
|
|
GetProjects() ([]Project, error)
|
|
GetShortDescription(scanID int, pathID int) (ShortDescription, error)
|
|
GetTeams() []Team
|
|
}
|
|
|
|
// NewSystemInstance returns a new Checkmarx client for communicating with the backend
|
|
func NewSystemInstance(client piperHttp.Uploader, serverURL, username, password string) (*SystemInstance, error) {
|
|
loggerInstance := log.Entry().WithField("package", "SAP/jenkins-library/pkg/checkmarx")
|
|
sys := &SystemInstance{
|
|
serverURL: serverURL,
|
|
username: username,
|
|
password: password,
|
|
client: client,
|
|
logger: loggerInstance,
|
|
}
|
|
|
|
token, err := sys.getOAuth2Token()
|
|
if err != nil {
|
|
return sys, errors.Wrap(err, "Error fetching oAuth token")
|
|
}
|
|
|
|
log.RegisterSecret(token)
|
|
|
|
options := piperHttp.ClientOptions{
|
|
Token: token,
|
|
TransportTimeout: time.Minute * 15,
|
|
}
|
|
sys.client.SetOptions(options)
|
|
|
|
return sys, nil
|
|
}
|
|
|
|
func sendRequest(sys *SystemInstance, method, url string, body io.Reader, header http.Header) ([]byte, error) {
|
|
return sendRequestInternal(sys, method, url, body, header, []int{})
|
|
}
|
|
|
|
func sendRequestInternal(sys *SystemInstance, method, url string, body io.Reader, header http.Header, acceptedErrorCodes []int) ([]byte, error) {
|
|
var requestBody io.Reader
|
|
var requestBodyCopy io.Reader
|
|
if body != nil {
|
|
closer := ioutil.NopCloser(body)
|
|
bodyBytes, _ := ioutil.ReadAll(closer)
|
|
requestBody = bytes.NewBuffer(bodyBytes)
|
|
requestBodyCopy = bytes.NewBuffer(bodyBytes)
|
|
defer closer.Close()
|
|
}
|
|
response, err := sys.client.SendRequest(method, fmt.Sprintf("%v/cxrestapi%v", sys.serverURL, url), requestBody, header, nil)
|
|
if err != nil && (response == nil || !piperutils.ContainsInt(acceptedErrorCodes, response.StatusCode)) {
|
|
sys.recordRequestDetailsInErrorCase(requestBodyCopy, response)
|
|
sys.logger.Errorf("HTTP request failed with error: %s", err)
|
|
return nil, err
|
|
}
|
|
|
|
data, _ := ioutil.ReadAll(response.Body)
|
|
sys.logger.Debugf("Valid response body: %v", string(data))
|
|
defer response.Body.Close()
|
|
return data, nil
|
|
}
|
|
|
|
func (sys *SystemInstance) recordRequestDetailsInErrorCase(requestBody io.Reader, response *http.Response) {
|
|
if requestBody != nil {
|
|
data, _ := ioutil.ReadAll(ioutil.NopCloser(requestBody))
|
|
sys.logger.Errorf("Request body: %s", data)
|
|
}
|
|
if response != nil && response.Body != nil {
|
|
data, _ := ioutil.ReadAll(response.Body)
|
|
sys.logger.Errorf("Response body: %s", data)
|
|
response.Body.Close()
|
|
}
|
|
}
|
|
|
|
func (sys *SystemInstance) getOAuth2Token() (string, error) {
|
|
body := url.Values{
|
|
"username": {sys.username},
|
|
"password": {sys.password},
|
|
"grant_type": {"password"},
|
|
"scope": {"sast_rest_api"},
|
|
"client_id": {"resource_owner_client"},
|
|
"client_secret": {"014DF517-39D1-4453-B7B3-9930C563627C"},
|
|
}
|
|
header := http.Header{}
|
|
header.Add("Content-type", "application/x-www-form-urlencoded")
|
|
data, err := sendRequest(sys, http.MethodPost, "/auth/identity/connect/token", strings.NewReader(body.Encode()), header)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
var token AuthToken
|
|
json.Unmarshal(data, &token)
|
|
return token.TokenType + " " + token.AccessToken, nil
|
|
}
|
|
|
|
// GetTeams returns the teams the user is assigned to
|
|
func (sys *SystemInstance) GetTeams() []Team {
|
|
sys.logger.Debug("Getting Teams...")
|
|
var teams []Team
|
|
|
|
data, err := sendRequest(sys, http.MethodGet, "/auth/teams", nil, nil)
|
|
if err != nil {
|
|
sys.logger.Errorf("Fetching teams failed: %s", err)
|
|
return teams
|
|
}
|
|
|
|
json.Unmarshal(data, &teams)
|
|
return teams
|
|
}
|
|
|
|
// GetProjects returns the projects defined in the Checkmarx backend which the user has access to
|
|
func (sys *SystemInstance) GetProjects() ([]Project, error) {
|
|
return sys.GetProjectsByNameAndTeam("", "")
|
|
}
|
|
|
|
// GetProjectByID returns the project addressed by projectID from the Checkmarx backend which the user has access to
|
|
func (sys *SystemInstance) GetProjectByID(projectID int) (Project, error) {
|
|
sys.logger.Debugf("Getting Project with ID %v...", projectID)
|
|
var project Project
|
|
|
|
data, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("/projects/%v", projectID), nil, nil)
|
|
if err != nil {
|
|
return project, errors.Wrapf(err, "fetching project %v failed", projectID)
|
|
}
|
|
|
|
json.Unmarshal(data, &project)
|
|
return project, nil
|
|
}
|
|
|
|
// GetProjectsByNameAndTeam returns the project addressed by projectID from the Checkmarx backend which the user has access to
|
|
func (sys *SystemInstance) GetProjectsByNameAndTeam(projectName, teamID string) ([]Project, error) {
|
|
sys.logger.Debugf("Getting projects with name %v of team %v...", projectName, teamID)
|
|
var projects []Project
|
|
header := http.Header{}
|
|
header.Set("Accept-Type", "application/json")
|
|
var data []byte
|
|
var err error
|
|
if len(teamID) > 0 && len(projectName) > 0 {
|
|
body := url.Values{
|
|
"projectName": {projectName},
|
|
"teamId": {teamID},
|
|
}
|
|
data, err = sendRequestInternal(sys, http.MethodGet, fmt.Sprintf("/projects?%v", body.Encode()), nil, header, []int{404})
|
|
} else {
|
|
data, err = sendRequestInternal(sys, http.MethodGet, "/projects", nil, header, []int{404})
|
|
}
|
|
if err != nil {
|
|
return projects, errors.Wrapf(err, "fetching project %v failed", projectName)
|
|
}
|
|
|
|
json.Unmarshal(data, &projects)
|
|
return projects, nil
|
|
}
|
|
|
|
// CreateProject creates a new project in the Checkmarx backend
|
|
func (sys *SystemInstance) CreateProject(projectName, teamID string) (ProjectCreateResult, error) {
|
|
var result ProjectCreateResult
|
|
jsonData := map[string]interface{}{
|
|
"name": projectName,
|
|
"owningTeam": teamID,
|
|
"isPublic": true,
|
|
}
|
|
|
|
jsonValue, err := json.Marshal(jsonData)
|
|
if err != nil {
|
|
return result, errors.Wrapf(err, "failed to marshal project data")
|
|
}
|
|
|
|
header := http.Header{}
|
|
header.Set("Content-Type", "application/json")
|
|
|
|
data, err := sendRequest(sys, http.MethodPost, "/projects", bytes.NewBuffer(jsonValue), header)
|
|
if err != nil {
|
|
return result, errors.Wrapf(err, "failed to create project %v", projectName)
|
|
}
|
|
|
|
json.Unmarshal(data, &result)
|
|
return result, nil
|
|
}
|
|
|
|
// CreateBranch creates a branch of an existing project in the Checkmarx backend
|
|
func (sys *SystemInstance) CreateBranch(projectID int, branchName string) int {
|
|
jsonData := map[string]interface{}{
|
|
"name": branchName,
|
|
}
|
|
|
|
jsonValue, err := json.Marshal(jsonData)
|
|
if err != nil {
|
|
sys.logger.Errorf("Error Marshal: %s", err)
|
|
return 0
|
|
}
|
|
|
|
header := http.Header{}
|
|
header.Set("Content-Type", "application/json")
|
|
data, err := sendRequest(sys, http.MethodPost, fmt.Sprintf("/projects/%v/branch", projectID), bytes.NewBuffer(jsonValue), header)
|
|
if err != nil {
|
|
sys.logger.Errorf("Failed to create project: %s", err)
|
|
return 0
|
|
}
|
|
|
|
var scan Scan
|
|
|
|
json.Unmarshal(data, &scan)
|
|
return scan.ID
|
|
}
|
|
|
|
// UploadProjectSourceCode zips and uploads the project sources for scanning
|
|
func (sys *SystemInstance) UploadProjectSourceCode(projectID int, zipFile string) error {
|
|
sys.logger.Debug("Starting to upload files...")
|
|
|
|
header := http.Header{}
|
|
header.Add("Accept-Encoding", "gzip,deflate")
|
|
header.Add("Accept", "text/plain")
|
|
resp, err := sys.client.UploadFile(fmt.Sprintf("%v/cxrestapi/projects/%v/sourceCode/attachments", sys.serverURL, projectID), zipFile, "zippedSource", header, nil, "form")
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to uploaded zipped sources")
|
|
}
|
|
|
|
data, err := ioutil.ReadAll(resp.Body)
|
|
defer resp.Body.Close()
|
|
if err != nil {
|
|
return errors.Wrap(err, "error reading the response data")
|
|
}
|
|
|
|
responseData := make(map[string]string)
|
|
json.Unmarshal(data, &responseData)
|
|
|
|
if resp.StatusCode == http.StatusNoContent {
|
|
return nil
|
|
}
|
|
|
|
sys.logger.Debugf("Body %s", data)
|
|
return errors.Wrapf(err, "error writing the request's body, status: %s", resp.Status)
|
|
}
|
|
|
|
// UpdateProjectExcludeSettings updates the exclude configuration of the project
|
|
func (sys *SystemInstance) UpdateProjectExcludeSettings(projectID int, excludeFolders string, excludeFiles string) error {
|
|
jsonData := map[string]string{
|
|
"excludeFoldersPattern": excludeFolders,
|
|
"excludeFilesPattern": excludeFiles,
|
|
}
|
|
|
|
jsonValue, err := json.Marshal(jsonData)
|
|
if err != nil {
|
|
return errors.Wrap(err, "error marhalling project exclude settings")
|
|
}
|
|
|
|
header := http.Header{}
|
|
header.Set("Content-Type", "application/json")
|
|
_, err = sendRequest(sys, http.MethodPut, fmt.Sprintf("/projects/%v/sourceCode/excludeSettings", projectID), bytes.NewBuffer(jsonValue), header)
|
|
if err != nil {
|
|
return errors.Wrap(err, "request to checkmarx system failed")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetPresets loads the preset values defined in the Checkmarx backend
|
|
func (sys *SystemInstance) GetPresets() []Preset {
|
|
sys.logger.Debug("Getting Presets...")
|
|
var presets []Preset
|
|
|
|
data, err := sendRequest(sys, http.MethodGet, "/sast/presets", nil, nil)
|
|
if err != nil {
|
|
sys.logger.Errorf("Fetching presets failed: %s", err)
|
|
return presets
|
|
}
|
|
|
|
json.Unmarshal(data, &presets)
|
|
return presets
|
|
}
|
|
|
|
// UpdateProjectConfiguration updates the configuration of the project addressed by projectID
|
|
func (sys *SystemInstance) UpdateProjectConfiguration(projectID int, presetID int, engineConfigurationID string) error {
|
|
engineConfigID, _ := strconv.Atoi(engineConfigurationID)
|
|
|
|
var projectScanSettings ScanSettings
|
|
header := http.Header{}
|
|
header.Set("Content-Type", "application/json")
|
|
data, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("/sast/scanSettings/%v", projectID), nil, header)
|
|
if err != nil {
|
|
// if an error happens, try to update the config anyway
|
|
sys.logger.Warnf("Failed to fetch scan settings of project %v: %s", projectID, err)
|
|
} else {
|
|
// Check if the current project config needs to be updated
|
|
json.Unmarshal(data, &projectScanSettings)
|
|
if projectScanSettings.Preset.PresetID == presetID && projectScanSettings.EngineConfiguration.EngineConfigurationID == engineConfigID {
|
|
sys.logger.Debugf("Project configuration does not need to be updated")
|
|
return nil
|
|
}
|
|
}
|
|
|
|
jsonData := map[string]interface{}{
|
|
"projectId": projectID,
|
|
"presetId": presetID,
|
|
"engineConfigurationId": engineConfigID,
|
|
}
|
|
|
|
jsonValue, err := json.Marshal(jsonData)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error marshalling project data")
|
|
}
|
|
|
|
_, err = sendRequest(sys, http.MethodPost, "/sast/scanSettings", bytes.NewBuffer(jsonValue), header)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "request to checkmarx system failed")
|
|
}
|
|
sys.logger.Debugf("Project configuration updated")
|
|
|
|
return nil
|
|
}
|
|
|
|
// ScanProject triggers a scan on the project addressed by projectID
|
|
func (sys *SystemInstance) ScanProject(projectID int, isIncremental, isPublic, forceScan bool) (Scan, error) {
|
|
scan := Scan{}
|
|
jsonData := map[string]interface{}{
|
|
"projectId": projectID,
|
|
"isIncremental": isIncremental,
|
|
"isPublic": isPublic,
|
|
"forceScan": forceScan,
|
|
"comment": "Scan From Golang Script",
|
|
}
|
|
|
|
jsonValue, _ := json.Marshal(jsonData)
|
|
|
|
header := http.Header{}
|
|
header.Set("cxOrigin", "GolangScript")
|
|
header.Set("Content-Type", "application/json")
|
|
data, err := sendRequest(sys, http.MethodPost, "/sast/scans", bytes.NewBuffer(jsonValue), header)
|
|
if err != nil {
|
|
sys.logger.Errorf("Failed to trigger scan of project %v: %s", projectID, err)
|
|
return scan, errors.Wrapf(err, "Failed to trigger scan of project %v", projectID)
|
|
}
|
|
|
|
json.Unmarshal(data, &scan)
|
|
return scan, nil
|
|
}
|
|
|
|
// GetScans returns all scan status on the project addressed by projectID
|
|
func (sys *SystemInstance) GetScans(projectID int) ([]ScanStatus, error) {
|
|
scans := []ScanStatus{}
|
|
body := url.Values{
|
|
"projectId": {fmt.Sprintf("%v", projectID)},
|
|
"last": {fmt.Sprintf("%v", 20)},
|
|
}
|
|
|
|
header := http.Header{}
|
|
header.Set("cxOrigin", "GolangScript")
|
|
header.Set("Accept-Type", "application/json")
|
|
data, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("/sast/scans?%v", body.Encode()), nil, header)
|
|
if err != nil {
|
|
sys.logger.Errorf("Failed to fetch scans of project %v: %s", projectID, err)
|
|
return scans, errors.Wrapf(err, "failed to fetch scans of project %v", projectID)
|
|
}
|
|
|
|
json.Unmarshal(data, &scans)
|
|
return scans, nil
|
|
}
|
|
|
|
// GetScanStatusAndDetail returns the status of the scan addressed by scanID
|
|
func (sys *SystemInstance) GetScanStatusAndDetail(scanID int) (string, ScanStatusDetail) {
|
|
var scanStatus ScanStatus
|
|
|
|
data, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("/sast/scans/%v", scanID), nil, nil)
|
|
if err != nil {
|
|
sys.logger.Errorf("Failed to get scan status for scanID %v: %s", scanID, err)
|
|
return "Failed", ScanStatusDetail{}
|
|
}
|
|
|
|
json.Unmarshal(data, &scanStatus)
|
|
return scanStatus.Status.Name, scanStatus.Status.Details
|
|
}
|
|
|
|
// GetResults returns the results of the scan addressed by scanID
|
|
func (sys *SystemInstance) GetResults(scanID int) ResultsStatistics {
|
|
var results ResultsStatistics
|
|
|
|
data, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("/sast/scans/%v/resultsStatistics", scanID), nil, nil)
|
|
if err != nil {
|
|
sys.logger.Errorf("Failed to fetch scan results for scanID %v: %s", scanID, err)
|
|
return results
|
|
}
|
|
|
|
json.Unmarshal(data, &results)
|
|
return results
|
|
}
|
|
|
|
// RequestNewReport triggers the generation of a report for a specific scan addressed by scanID
|
|
func (sys *SystemInstance) RequestNewReport(scanID int, reportType string) (Report, error) {
|
|
report := Report{}
|
|
jsonData := map[string]interface{}{
|
|
"scanId": scanID,
|
|
"reportType": reportType,
|
|
"comment": "Scan report triggered by Piper",
|
|
}
|
|
|
|
jsonValue, _ := json.Marshal(jsonData)
|
|
|
|
header := http.Header{}
|
|
header.Set("cxOrigin", "GolangScript")
|
|
header.Set("Content-Type", "application/json")
|
|
data, err := sendRequest(sys, http.MethodPost, "/reports/sastScan", bytes.NewBuffer(jsonValue), header)
|
|
if err != nil {
|
|
return report, errors.Wrapf(err, "Failed to trigger report generation for scan %v", scanID)
|
|
}
|
|
|
|
json.Unmarshal(data, &report)
|
|
return report, nil
|
|
}
|
|
|
|
// GetReportStatus returns the status of the report generation process
|
|
func (sys *SystemInstance) GetReportStatus(reportID int) (ReportStatusResponse, error) {
|
|
var response ReportStatusResponse
|
|
|
|
header := http.Header{}
|
|
header.Set("Accept", "application/json")
|
|
data, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("/reports/sastScan/%v/status", reportID), nil, header)
|
|
if err != nil {
|
|
sys.logger.Errorf("Failed to fetch report status for reportID %v: %s", reportID, err)
|
|
return response, errors.Wrapf(err, "failed to fetch report status for reportID %v", reportID)
|
|
}
|
|
|
|
json.Unmarshal(data, &response)
|
|
return response, nil
|
|
}
|
|
|
|
// GetShortDescription returns the short description for an issue with a scanID and pathID
|
|
func (sys *SystemInstance) GetShortDescription(scanID int, pathID int) (ShortDescription, error) {
|
|
var shortDescription ShortDescription
|
|
|
|
data, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("/sast/scans/%v/results/%v/shortDescription", scanID, pathID), nil, nil)
|
|
if err != nil {
|
|
sys.logger.Errorf("Failed to get short description for scanID %v and pathID %v: %s", scanID, pathID, err)
|
|
return shortDescription, err
|
|
}
|
|
|
|
json.Unmarshal(data, &shortDescription)
|
|
return shortDescription, nil
|
|
}
|
|
|
|
// DownloadReport downloads the report addressed by reportID and returns the XML contents
|
|
func (sys *SystemInstance) DownloadReport(reportID int) ([]byte, error) {
|
|
header := http.Header{}
|
|
header.Set("Accept", "application/json")
|
|
data, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("/reports/sastScan/%v", reportID), nil, header)
|
|
if err != nil {
|
|
return []byte{}, errors.Wrapf(err, "failed to download report with reportID %v", reportID)
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// FilterTeamByName filters a team by its name
|
|
func (sys *SystemInstance) FilterTeamByName(teams []Team, teamName string) (Team, error) {
|
|
for _, team := range teams {
|
|
if team.FullName == teamName || team.FullName == strings.ReplaceAll(teamName, `\`, `/`) {
|
|
return team, nil
|
|
}
|
|
}
|
|
return Team{}, errors.New("Failed to find team with name " + teamName)
|
|
}
|
|
|
|
// FilterTeamByID filters a team by its ID
|
|
func (sys *SystemInstance) FilterTeamByID(teams []Team, teamID json.RawMessage) Team {
|
|
teamIDBytes1, _ := teamID.MarshalJSON()
|
|
for _, team := range teams {
|
|
teamIDBytes2, _ := team.ID.MarshalJSON()
|
|
if bytes.Compare(teamIDBytes1, teamIDBytes2) == 0 {
|
|
return team
|
|
}
|
|
}
|
|
return Team{}
|
|
}
|
|
|
|
// FilterProjectByName filters a project by its name
|
|
func (sys *SystemInstance) FilterProjectByName(projects []Project, projectName string) Project {
|
|
for _, project := range projects {
|
|
if project.Name == projectName {
|
|
sys.logger.Debugf("Filtered project with name %v", project.Name)
|
|
return project
|
|
}
|
|
}
|
|
return Project{}
|
|
}
|
|
|
|
// FilterPresetByName filters a preset by its name
|
|
func (sys *SystemInstance) FilterPresetByName(presets []Preset, presetName string) Preset {
|
|
for _, preset := range presets {
|
|
if preset.Name == presetName {
|
|
return preset
|
|
}
|
|
}
|
|
return Preset{}
|
|
}
|
|
|
|
// FilterPresetByID filters a preset by its name
|
|
func (sys *SystemInstance) FilterPresetByID(presets []Preset, presetID int) Preset {
|
|
for _, preset := range presets {
|
|
if preset.ID == presetID {
|
|
return preset
|
|
}
|
|
}
|
|
return Preset{}
|
|
}
|