1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-07-13 01:30:24 +02:00

Fortify implementation in golang (#1428)

This commit is contained in:
Sven Merk
2020-05-25 19:48:59 +02:00
committed by GitHub
parent 5a7b3db3a9
commit af2a01c064
56 changed files with 5461 additions and 129 deletions

1
.gitignore vendored
View File

@ -25,6 +25,7 @@ consumer-test/**/workspace
/piper.exe /piper.exe
.factorypath .factorypath
debug.test
/cache/protecode /cache/protecode
*errorDetails.json *errorDetails.json

View File

@ -189,7 +189,7 @@ Define ` + "`" + `buildTool: custom` + "`" + `, ` + "`" + `filePath: <path to yo
func addArtifactPrepareVersionFlags(cmd *cobra.Command, stepConfig *artifactPrepareVersionOptions) { func addArtifactPrepareVersionFlags(cmd *cobra.Command, stepConfig *artifactPrepareVersionOptions) {
cmd.Flags().StringVar(&stepConfig.BuildTool, "buildTool", os.Getenv("PIPER_buildTool"), "Defines the tool which is used for building the artifact. Supports `custom`, `dub`, `golang`, `maven`, `mta`, `npm`, `pip`, `sbt`.") cmd.Flags().StringVar(&stepConfig.BuildTool, "buildTool", os.Getenv("PIPER_buildTool"), "Defines the tool which is used for building the artifact. Supports `custom`, `dub`, `golang`, `maven`, `mta`, `npm`, `pip`, `sbt`.")
cmd.Flags().StringVar(&stepConfig.CommitUserName, "commitUserName", "Project Piper", "Defines the user name which appears in version control for the versioning update (in case `versioningType: cloud`).") cmd.Flags().StringVar(&stepConfig.CommitUserName, "commitUserName", `Project Piper`, "Defines the user name which appears in version control for the versioning update (in case `versioningType: cloud`).")
cmd.Flags().StringVar(&stepConfig.CustomVersionField, "customVersionField", os.Getenv("PIPER_customVersionField"), "For `buildTool: custom`: Defines the field which contains the version in the descriptor file.") cmd.Flags().StringVar(&stepConfig.CustomVersionField, "customVersionField", os.Getenv("PIPER_customVersionField"), "For `buildTool: custom`: Defines the field which contains the version in the descriptor file.")
cmd.Flags().StringVar(&stepConfig.CustomVersionSection, "customVersionSection", os.Getenv("PIPER_customVersionSection"), "For `buildTool: custom`: Defines the section for version retrieval in vase a *.ini/*.cfg file is used.") cmd.Flags().StringVar(&stepConfig.CustomVersionSection, "customVersionSection", os.Getenv("PIPER_customVersionSection"), "For `buildTool: custom`: Defines the section for version retrieval in vase a *.ini/*.cfg file is used.")
cmd.Flags().StringVar(&stepConfig.CustomVersioningScheme, "customVersioningScheme", os.Getenv("PIPER_customVersioningScheme"), "For `buildTool: custom`: Defines the versioning scheme to be used (possible options `pep440`, `maven`, `semver2`).") cmd.Flags().StringVar(&stepConfig.CustomVersioningScheme, "customVersioningScheme", os.Getenv("PIPER_customVersioningScheme"), "For `buildTool: custom`: Defines the versioning scheme to be used (possible options `pep440`, `maven`, `semver2`).")
@ -201,11 +201,11 @@ func addArtifactPrepareVersionFlags(cmd *cobra.Command, stepConfig *artifactPrep
cmd.Flags().StringVar(&stepConfig.Password, "password", os.Getenv("PIPER_password"), "Password/token for git authentication.") cmd.Flags().StringVar(&stepConfig.Password, "password", os.Getenv("PIPER_password"), "Password/token for git authentication.")
cmd.Flags().StringVar(&stepConfig.ProjectSettingsFile, "projectSettingsFile", os.Getenv("PIPER_projectSettingsFile"), "Maven only - Path to the mvn settings file that should be used as project settings file.") cmd.Flags().StringVar(&stepConfig.ProjectSettingsFile, "projectSettingsFile", os.Getenv("PIPER_projectSettingsFile"), "Maven only - Path to the mvn settings file that should be used as project settings file.")
cmd.Flags().BoolVar(&stepConfig.ShortCommitID, "shortCommitId", false, "Defines if a short version of the commitId should be used. GitHub format is used (first 7 characters).") cmd.Flags().BoolVar(&stepConfig.ShortCommitID, "shortCommitId", false, "Defines if a short version of the commitId should be used. GitHub format is used (first 7 characters).")
cmd.Flags().StringVar(&stepConfig.TagPrefix, "tagPrefix", "build_", "Defines the prefix which is used for the git tag which is written during the versioning run (only `versioningType: cloud`).") cmd.Flags().StringVar(&stepConfig.TagPrefix, "tagPrefix", `build_`, "Defines the prefix which is used for the git tag which is written during the versioning run (only `versioningType: cloud`).")
cmd.Flags().BoolVar(&stepConfig.UnixTimestamp, "unixTimestamp", false, "Defines if the Unix timestamp number should be used as build number instead of the standard date format.") cmd.Flags().BoolVar(&stepConfig.UnixTimestamp, "unixTimestamp", false, "Defines if the Unix timestamp number should be used as build number instead of the standard date format.")
cmd.Flags().StringVar(&stepConfig.Username, "username", os.Getenv("PIPER_username"), "User name for git authentication") cmd.Flags().StringVar(&stepConfig.Username, "username", os.Getenv("PIPER_username"), "User name for git authentication")
cmd.Flags().StringVar(&stepConfig.VersioningTemplate, "versioningTemplate", os.Getenv("PIPER_versioningTemplate"), "DEPRECATED: Defines the template for the automatic version which will be created") cmd.Flags().StringVar(&stepConfig.VersioningTemplate, "versioningTemplate", os.Getenv("PIPER_versioningTemplate"), "DEPRECATED: Defines the template for the automatic version which will be created")
cmd.Flags().StringVar(&stepConfig.VersioningType, "versioningType", "cloud", "Defines the type of versioning (`cloud`: fully automatic, `cloud_noTag`: automatic but no tag created, `library`: manual)") cmd.Flags().StringVar(&stepConfig.VersioningType, "versioningType", `cloud`, "Defines the type of versioning (`cloud`: fully automatic, `cloud_noTag`: automatic but no tag created, `library`: manual)")
cmd.MarkFlagRequired("buildTool") cmd.MarkFlagRequired("buildTool")
} }

View File

@ -2,6 +2,7 @@ package cmd
import ( import (
"fmt" "fmt"
"github.com/SAP/jenkins-library/pkg/versioning"
"testing" "testing"
"time" "time"
@ -44,6 +45,10 @@ func (a *artifactVersioningMock) SetVersion(version string) error {
return nil return nil
} }
func (a *artifactVersioningMock) GetCoordinates() (versioning.Coordinates, error) {
return nil, fmt.Errorf("not implemented")
}
type gitRepositoryMock struct { type gitRepositoryMock struct {
createRemoteConfigs []*gitConfig.RemoteConfig createRemoteConfigs []*gitConfig.RemoteConfig
createRemoteCalls int createRemoteCalls int

View File

@ -226,8 +226,8 @@ thresholds instead of ` + "`" + `percentage` + "`" + ` whereas we strongly recom
func addCheckmarxExecuteScanFlags(cmd *cobra.Command, stepConfig *checkmarxExecuteScanOptions) { func addCheckmarxExecuteScanFlags(cmd *cobra.Command, stepConfig *checkmarxExecuteScanOptions) {
cmd.Flags().BoolVar(&stepConfig.AvoidDuplicateProjectScans, "avoidDuplicateProjectScans", false, "Whether duplicate scans of the same project state shall be avoided or not") cmd.Flags().BoolVar(&stepConfig.AvoidDuplicateProjectScans, "avoidDuplicateProjectScans", false, "Whether duplicate scans of the same project state shall be avoided or not")
cmd.Flags().StringVar(&stepConfig.FilterPattern, "filterPattern", "!**/node_modules/**, !**/.xmake/**, !**/*_test.go, !**/vendor/**/*.go, **/*.html, **/*.xml, **/*.go, **/*.py, **/*.js, **/*.scala, **/*.ts", "The filter pattern used to zip the files relevant for scanning, patterns can be negated by setting an exclamation mark in front i.e. `!test/*.js` would avoid adding any javascript files located in the test directory") cmd.Flags().StringVar(&stepConfig.FilterPattern, "filterPattern", `!**/node_modules/**, !**/.xmake/**, !**/*_test.go, !**/vendor/**/*.go, **/*.html, **/*.xml, **/*.go, **/*.py, **/*.js, **/*.scala, **/*.ts`, "The filter pattern used to zip the files relevant for scanning, patterns can be negated by setting an exclamation mark in front i.e. `!test/*.js` would avoid adding any javascript files located in the test directory")
cmd.Flags().StringVar(&stepConfig.FullScanCycle, "fullScanCycle", "5", "Indicates how often a full scan should happen between the incremental scans when activated") cmd.Flags().StringVar(&stepConfig.FullScanCycle, "fullScanCycle", `5`, "Indicates how often a full scan should happen between the incremental scans when activated")
cmd.Flags().BoolVar(&stepConfig.FullScansScheduled, "fullScansScheduled", true, "Whether full scans are to be scheduled or not. Should be used in relation with `incremental` and `fullScanCycle`") cmd.Flags().BoolVar(&stepConfig.FullScansScheduled, "fullScansScheduled", true, "Whether full scans are to be scheduled or not. Should be used in relation with `incremental` and `fullScanCycle`")
cmd.Flags().BoolVar(&stepConfig.GeneratePdfReport, "generatePdfReport", true, "Whether to generate a PDF report of the analysis results or not") cmd.Flags().BoolVar(&stepConfig.GeneratePdfReport, "generatePdfReport", true, "Whether to generate a PDF report of the analysis results or not")
cmd.Flags().BoolVar(&stepConfig.Incremental, "incremental", true, "Whether incremental scans are to be applied which optimizes the scan time but might reduce detection capabilities. Therefore full scans are still required from time to time and should be scheduled via `fullScansScheduled` and `fullScanCycle`") cmd.Flags().BoolVar(&stepConfig.Incremental, "incremental", true, "Whether incremental scans are to be applied which optimizes the scan time but might reduce detection capabilities. Therefore full scans are still required from time to time and should be scheduled via `fullScansScheduled` and `fullScanCycle`")
@ -236,7 +236,7 @@ func addCheckmarxExecuteScanFlags(cmd *cobra.Command, stepConfig *checkmarxExecu
cmd.Flags().StringVar(&stepConfig.ProjectName, "projectName", os.Getenv("PIPER_projectName"), "The name of the Checkmarx project to scan into") cmd.Flags().StringVar(&stepConfig.ProjectName, "projectName", os.Getenv("PIPER_projectName"), "The name of the Checkmarx project to scan into")
cmd.Flags().StringVar(&stepConfig.PullRequestName, "pullRequestName", os.Getenv("PIPER_pullRequestName"), "Used to supply the name for the newly created PR project branch when being used in pull request scenarios") cmd.Flags().StringVar(&stepConfig.PullRequestName, "pullRequestName", os.Getenv("PIPER_pullRequestName"), "Used to supply the name for the newly created PR project branch when being used in pull request scenarios")
cmd.Flags().StringVar(&stepConfig.ServerURL, "serverUrl", os.Getenv("PIPER_serverUrl"), "The URL pointing to the root of the Checkmarx server to be used") cmd.Flags().StringVar(&stepConfig.ServerURL, "serverUrl", os.Getenv("PIPER_serverUrl"), "The URL pointing to the root of the Checkmarx server to be used")
cmd.Flags().StringVar(&stepConfig.SourceEncoding, "sourceEncoding", "1", "The source encoding to be used, if not set explicitly the project's default will be used") cmd.Flags().StringVar(&stepConfig.SourceEncoding, "sourceEncoding", `1`, "The source encoding to be used, if not set explicitly the project's default will be used")
cmd.Flags().StringVar(&stepConfig.TeamID, "teamId", os.Getenv("PIPER_teamId"), "The group ID related to your team which can be obtained via the Pipeline Syntax plugin as described in the `Details` section") cmd.Flags().StringVar(&stepConfig.TeamID, "teamId", os.Getenv("PIPER_teamId"), "The group ID related to your team which can be obtained via the Pipeline Syntax plugin as described in the `Details` section")
cmd.Flags().StringVar(&stepConfig.TeamName, "teamName", os.Getenv("PIPER_teamName"), "The full name of the team to assign newly created projects to which is preferred to teamId") cmd.Flags().StringVar(&stepConfig.TeamName, "teamName", os.Getenv("PIPER_teamName"), "The full name of the team to assign newly created projects to which is preferred to teamId")
cmd.Flags().StringVar(&stepConfig.Username, "username", os.Getenv("PIPER_username"), "The username to authenticate") cmd.Flags().StringVar(&stepConfig.Username, "username", os.Getenv("PIPER_username"), "The username to authenticate")
@ -244,8 +244,8 @@ func addCheckmarxExecuteScanFlags(cmd *cobra.Command, stepConfig *checkmarxExecu
cmd.Flags().IntVar(&stepConfig.VulnerabilityThresholdHigh, "vulnerabilityThresholdHigh", 100, "The specific threshold for high severity findings") cmd.Flags().IntVar(&stepConfig.VulnerabilityThresholdHigh, "vulnerabilityThresholdHigh", 100, "The specific threshold for high severity findings")
cmd.Flags().IntVar(&stepConfig.VulnerabilityThresholdLow, "vulnerabilityThresholdLow", 10, "The specific threshold for low severity findings") cmd.Flags().IntVar(&stepConfig.VulnerabilityThresholdLow, "vulnerabilityThresholdLow", 10, "The specific threshold for low severity findings")
cmd.Flags().IntVar(&stepConfig.VulnerabilityThresholdMedium, "vulnerabilityThresholdMedium", 100, "The specific threshold for medium severity findings") cmd.Flags().IntVar(&stepConfig.VulnerabilityThresholdMedium, "vulnerabilityThresholdMedium", 100, "The specific threshold for medium severity findings")
cmd.Flags().StringVar(&stepConfig.VulnerabilityThresholdResult, "vulnerabilityThresholdResult", "FAILURE", "The result of the build in case thresholds are enabled and exceeded") cmd.Flags().StringVar(&stepConfig.VulnerabilityThresholdResult, "vulnerabilityThresholdResult", `FAILURE`, "The result of the build in case thresholds are enabled and exceeded")
cmd.Flags().StringVar(&stepConfig.VulnerabilityThresholdUnit, "vulnerabilityThresholdUnit", "percentage", "The unit for the threshold to apply.") cmd.Flags().StringVar(&stepConfig.VulnerabilityThresholdUnit, "vulnerabilityThresholdUnit", `percentage`, "The unit for the threshold to apply.")
cmd.MarkFlagRequired("password") cmd.MarkFlagRequired("password")
cmd.MarkFlagRequired("projectName") cmd.MarkFlagRequired("projectName")

View File

@ -83,9 +83,9 @@ func addDetectExecuteScanFlags(cmd *cobra.Command, stepConfig *detectExecuteScan
cmd.Flags().StringVar(&stepConfig.CodeLocation, "codeLocation", os.Getenv("PIPER_codeLocation"), "An override for the name Detect will use for the scan file it creates.") cmd.Flags().StringVar(&stepConfig.CodeLocation, "codeLocation", os.Getenv("PIPER_codeLocation"), "An override for the name Detect will use for the scan file it creates.")
cmd.Flags().StringVar(&stepConfig.ProjectName, "projectName", os.Getenv("PIPER_projectName"), "Name of the Synopsis Detect (formerly BlackDuck) project.") cmd.Flags().StringVar(&stepConfig.ProjectName, "projectName", os.Getenv("PIPER_projectName"), "Name of the Synopsis Detect (formerly BlackDuck) project.")
cmd.Flags().StringVar(&stepConfig.ProjectVersion, "projectVersion", os.Getenv("PIPER_projectVersion"), "Version of the Synopsis Detect (formerly BlackDuck) project.") cmd.Flags().StringVar(&stepConfig.ProjectVersion, "projectVersion", os.Getenv("PIPER_projectVersion"), "Version of the Synopsis Detect (formerly BlackDuck) project.")
cmd.Flags().StringSliceVar(&stepConfig.Scanners, "scanners", []string{"signature"}, "List of scanners to be used for Synopsis Detect (formerly BlackDuck) scan.") cmd.Flags().StringSliceVar(&stepConfig.Scanners, "scanners", []string{`signature`}, "List of scanners to be used for Synopsis Detect (formerly BlackDuck) scan.")
cmd.Flags().StringSliceVar(&stepConfig.ScanPaths, "scanPaths", []string{"."}, "List of paths which should be scanned by the Synopsis Detect (formerly BlackDuck) scan.") cmd.Flags().StringSliceVar(&stepConfig.ScanPaths, "scanPaths", []string{`.`}, "List of paths which should be scanned by the Synopsis Detect (formerly BlackDuck) scan.")
cmd.Flags().StringSliceVar(&stepConfig.ScanProperties, "scanProperties", []string{"--blackduck.signature.scanner.memory=4096", "--blackduck.timeout=6000", "--blackduck.trust.cert=true", "--detect.policy.check.fail.on.severities=BLOCKER,CRITICAL,MAJOR", "--detect.report.timeout=4800", "--logging.level.com.synopsys.integration=DEBUG"}, "Properties passed to the Synopsis Detect (formerly BlackDuck) scan. You can find details in the [Synopsis Detect documentation](https://synopsys.atlassian.net/wiki/spaces/INTDOCS/pages/622846/Using+Synopsys+Detect+Properties)") cmd.Flags().StringSliceVar(&stepConfig.ScanProperties, "scanProperties", []string{`--blackduck.signature.scanner.memory=4096`, `--blackduck.timeout=6000`, `--blackduck.trust.cert=true`, `--detect.policy.check.fail.on.severities=BLOCKER,CRITICAL,MAJOR`, `--detect.report.timeout=4800`, `--logging.level.com.synopsys.integration=DEBUG`}, "Properties passed to the Synopsis Detect (formerly BlackDuck) scan. You can find details in the [Synopsis Detect documentation](https://synopsys.atlassian.net/wiki/spaces/INTDOCS/pages/622846/Using+Synopsys+Detect+Properties)")
cmd.Flags().StringVar(&stepConfig.ServerURL, "serverUrl", os.Getenv("PIPER_serverUrl"), "Server url to the Synopsis Detect (formerly BlackDuck) Server.") cmd.Flags().StringVar(&stepConfig.ServerURL, "serverUrl", os.Getenv("PIPER_serverUrl"), "Server url to the Synopsis Detect (formerly BlackDuck) Server.")
cmd.MarkFlagRequired("apiToken") cmd.MarkFlagRequired("apiToken")

690
cmd/fortifyExecuteScan.go Normal file
View File

@ -0,0 +1,690 @@
package cmd
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"math"
"os"
"regexp"
"strings"
"time"
"github.com/google/go-github/v28/github"
"github.com/google/uuid"
"github.com/piper-validation/fortify-client-go/models"
"github.com/SAP/jenkins-library/pkg/command"
"github.com/SAP/jenkins-library/pkg/fortify"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/maven"
"github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/SAP/jenkins-library/pkg/versioning"
piperGithub "github.com/SAP/jenkins-library/pkg/github"
)
type pullRequestService interface {
ListPullRequestsWithCommit(ctx context.Context, owner, repo, sha string, opts *github.PullRequestListOptions) ([]*github.PullRequest, *github.Response, error)
}
const checkString = "<---CHECK FORTIFY---"
const classpathFileName = "cp.txt"
func fortifyExecuteScan(config fortifyExecuteScanOptions, telemetryData *telemetry.CustomData, influx *fortifyExecuteScanInflux) {
auditStatus := map[string]string{}
sys := fortify.NewSystemInstance(config.ServerURL, config.APIEndpoint, config.AuthToken, time.Second*30)
c := command.Command{}
// reroute command output to logging framework
c.Stdout(log.Entry().Writer())
c.Stderr(log.Entry().Writer())
err := runFortifyScan(config, sys, &c, telemetryData, influx, auditStatus)
if err != nil {
log.Entry().WithError(err).Fatalf("Fortify scan and check failed")
}
}
func runFortifyScan(config fortifyExecuteScanOptions, sys fortify.System, command execRunner, telemetryData *telemetry.CustomData, influx *fortifyExecuteScanInflux, auditStatus map[string]string) error {
log.Entry().Debugf("Running Fortify scan against SSC at %v", config.ServerURL)
artifact, err := versioning.GetArtifact(config.BuildTool, config.BuildDescriptorFile, &versioning.Options{}, command)
if err != nil {
return fmt.Errorf("unable to get artifact from descriptor %v: %w", config.BuildDescriptorFile, err)
}
gav, err := artifact.GetCoordinates()
if err != nil {
return fmt.Errorf("unable to get project coordinates from descriptor %v: %w", config.BuildDescriptorFile, err)
}
log.Entry().Debugf("determined project coordinates %v", gav)
fortifyProjectName, fortifyProjectVersion := versioning.DetermineProjectCoordinates(config.ProjectName, config.DefaultVersioningModel, gav)
project, err := sys.GetProjectByName(fortifyProjectName, config.AutoCreate, fortifyProjectVersion)
if err != nil {
return fmt.Errorf("Failed to load project %v: %w", fortifyProjectName, err)
}
projectVersion, err := sys.GetProjectVersionDetailsByProjectIDAndVersionName(project.ID, fortifyProjectVersion, config.AutoCreate, fortifyProjectName)
if err != nil {
return fmt.Errorf("Failed to load project version %v: %w", fortifyProjectVersion, err)
}
if len(config.PullRequestName) > 0 {
fortifyProjectVersion = config.PullRequestName
projectVersion, err := sys.LookupOrCreateProjectVersionDetailsForPullRequest(project.ID, projectVersion, fortifyProjectVersion)
if err != nil {
return fmt.Errorf("Failed to lookup / create project version for pull request %v: %w", fortifyProjectVersion, err)
}
log.Entry().Debugf("Looked up / created project version with ID %v for PR %v", projectVersion.ID, fortifyProjectVersion)
} else {
prID := determinePullRequestMerge(config)
if len(prID) > 0 {
log.Entry().Debugf("Determined PR ID '%v' for merge check", prID)
pullRequestProjectName := fmt.Sprintf("PR-%v", prID)
err = sys.MergeProjectVersionStateOfPRIntoMaster(config.FprDownloadEndpoint, config.FprUploadEndpoint, project.ID, projectVersion.ID, pullRequestProjectName)
if err != nil {
return fmt.Errorf("Failed to merge project version state for pull request %v into project version %v of project %v: %w", pullRequestProjectName, fortifyProjectVersion, project.ID, err)
}
}
}
log.Entry().Debugf("Scanning and uploading to project %v with version %v and projectVersionId %v", fortifyProjectName, fortifyProjectVersion, projectVersion.ID)
buildLabel := fmt.Sprintf("%v/repos/%v/%v/commits/%v", config.GithubAPIURL, config.Owner, config.Repository, config.CommitID)
// Create sourceanalyzer command based on configuration
buildID := uuid.New().String()
command.SetDir(config.ModulePath)
os.MkdirAll(fmt.Sprintf("%v/%v", config.ModulePath, "target"), os.ModePerm)
if config.UpdateRulePack {
err := command.RunExecutable("fortifyupdate", "-acceptKey", "-acceptSSLCertificate", "-url", config.ServerURL)
if err != nil {
log.Entry().WithError(err).WithField("serverUrl", config.ServerURL).Fatal("Failed to update rule pack")
}
err = command.RunExecutable("fortifyupdate", "-acceptKey", "-acceptSSLCertificate", "-showInstalledRules")
if err != nil {
log.Entry().WithError(err).WithField("serverUrl", config.ServerURL).Fatal("Failed to fetch details of installed rule pack")
}
}
triggerFortifyScan(config, command, buildID, buildLabel)
var reports []piperutils.Path
reports = append(reports, piperutils.Path{Target: fmt.Sprintf("%vtarget/fortify-scan.*", config.ModulePath)})
reports = append(reports, piperutils.Path{Target: fmt.Sprintf("%vtarget/*.fpr", config.ModulePath)})
var message string
if config.UploadResults {
log.Entry().Debug("Uploading results")
resultFilePath := fmt.Sprintf("%vtarget/result.fpr", config.ModulePath)
err = sys.UploadResultFile(config.FprUploadEndpoint, resultFilePath, projectVersion.ID)
message = fmt.Sprintf("Failed to upload result file %v to Fortify SSC at %v", resultFilePath, config.ServerURL)
} else {
log.Entry().Debug("Generating XML report")
xmlReportName := "fortify_result.xml"
err = command.RunExecutable("ReportGenerator", "-format", "xml", "-f", xmlReportName, "-source", fmt.Sprintf("%vtarget/result.fpr", config.ModulePath))
message = fmt.Sprintf("Failed to generate XML report %v", xmlReportName)
if err != nil {
reports = append(reports, piperutils.Path{Target: fmt.Sprintf("%vfortify_result.xml", config.ModulePath)})
}
}
piperutils.PersistReportsAndLinks("fortifyExecuteScan", config.ModulePath, reports, nil)
if err != nil {
return fmt.Errorf(message+": %w", err)
}
log.Entry().Debugf("Starting audit status check on project %v with version %v and project version ID %v", fortifyProjectName, fortifyProjectVersion, projectVersion.ID)
filterSet, err := sys.GetFilterSetOfProjectVersionByTitle(projectVersion.ID, config.FilterSetTitle)
if filterSet == nil || err != nil {
return fmt.Errorf("Failed to load filter set with title %v", config.FilterSetTitle)
}
// Ensure latest FPR is processed
err = verifyScanResultsFinishedUploading(config, sys, projectVersion.ID, buildLabel, filterSet, 0)
if err != nil {
return err
}
// Generate report
if config.Reporting {
resultURL := []byte(fmt.Sprintf("https://fortify.tools.sap/ssc/html/ssc/version/%v/fix/null/", projectVersion.ID))
ioutil.WriteFile(fmt.Sprintf("%vtarget/%v-%v.%v", config.ModulePath, *project.Name, *projectVersion.Name, "txt"), resultURL, 0700)
data, err := generateAndDownloadQGateReport(config, sys, project, projectVersion)
if err != nil {
return err
}
ioutil.WriteFile(fmt.Sprintf("%vtarget/%v-%v.%v", config.ModulePath, *project.Name, *projectVersion.Name, config.ReportType), data, 0700)
}
// Perform audit compliance checks
issueFilterSelectorSet, err := sys.GetIssueFilterSelectorOfProjectVersionByName(projectVersion.ID, []string{"Analysis", "Folder", "Category"}, nil)
if err != nil {
log.Entry().WithError(err).Fatalf("Failed to fetch project version issue filter selector for project version ID %v", projectVersion.ID)
}
log.Entry().Debugf("initial filter selector set: %v", issueFilterSelectorSet)
numberOfViolations := analyseUnauditedIssues(config, sys, projectVersion, filterSet, issueFilterSelectorSet, influx, auditStatus)
numberOfViolations += analyseSuspiciousExploitable(config, sys, projectVersion, filterSet, issueFilterSelectorSet, influx, auditStatus)
log.Entry().Infof("Counted %v violations, details: %v", numberOfViolations, auditStatus)
influx.fortify_data.fields.projectName = fortifyProjectName
influx.fortify_data.fields.projectVersion = fortifyProjectVersion
influx.fortify_data.fields.violations = fmt.Sprintf("%v", numberOfViolations)
if numberOfViolations > 0 {
return errors.New("fortify scan failed, the project is not compliant. For details check the archived report")
}
return nil
}
func analyseUnauditedIssues(config fortifyExecuteScanOptions, sys fortify.System, projectVersion *models.ProjectVersion, filterSet *models.FilterSet, issueFilterSelectorSet *models.IssueFilterSelectorSet, influx *fortifyExecuteScanInflux, auditStatus map[string]string) int {
log.Entry().Info("Analyzing unaudited issues")
reducedFilterSelectorSet := sys.ReduceIssueFilterSelectorSet(issueFilterSelectorSet, []string{"Folder"}, nil)
fetchedIssueGroups, err := sys.GetProjectIssuesByIDAndFilterSetGroupedBySelector(projectVersion.ID, "", filterSet.GUID, reducedFilterSelectorSet)
if err != nil {
log.Entry().WithError(err).Fatalf("Failed to fetch project version issue groups with filter set %v and selector %v for project version ID %v", filterSet, issueFilterSelectorSet, projectVersion.ID)
}
overallViolations := 0
for _, issueGroup := range fetchedIssueGroups {
overallViolations += getIssueDeltaFor(config, sys, issueGroup, projectVersion.ID, filterSet, issueFilterSelectorSet, influx, auditStatus)
}
return overallViolations
}
func getIssueDeltaFor(config fortifyExecuteScanOptions, sys fortify.System, issueGroup *models.ProjectVersionIssueGroup, projectVersionID int64, filterSet *models.FilterSet, issueFilterSelectorSet *models.IssueFilterSelectorSet, influx *fortifyExecuteScanInflux, auditStatus map[string]string) int {
totalMinusAuditedDelta := 0
group := ""
total := 0
audited := 0
if issueGroup != nil {
group = *issueGroup.ID
total = int(*issueGroup.TotalCount)
audited = int(*issueGroup.AuditedCount)
}
groupTotalMinusAuditedDelta := total - audited
if groupTotalMinusAuditedDelta > 0 {
reducedFilterSelectorSet := sys.ReduceIssueFilterSelectorSet(issueFilterSelectorSet, []string{"Folder", "Analysis"}, []string{group})
folderSelector := sys.GetFilterSetByDisplayName(reducedFilterSelectorSet, "Folder")
if folderSelector == nil {
log.Entry().Fatal("folder selector not found")
}
analysisSelector := sys.GetFilterSetByDisplayName(reducedFilterSelectorSet, "Analysis")
auditStatus[group] = fmt.Sprintf("%v total : %v audited", total, audited)
if strings.Contains(config.MustAuditIssueGroups, group) {
totalMinusAuditedDelta += groupTotalMinusAuditedDelta
if group == "Corporate Security Requirements" {
influx.fortify_data.fields.corporateTotal = fmt.Sprintf("%v", total)
influx.fortify_data.fields.corporateAudited = fmt.Sprintf("%v", audited)
}
if group == "Audit All" {
influx.fortify_data.fields.auditAllTotal = fmt.Sprintf("%v", total)
influx.fortify_data.fields.auditAllAudited = fmt.Sprintf("%v", audited)
}
log.Entry().Errorf("[projectVersionId %v]: Unaudited %v detected, count %v", projectVersionID, group, totalMinusAuditedDelta)
logIssueURL(config, projectVersionID, folderSelector, analysisSelector)
}
if strings.Contains(config.SpotAuditIssueGroups, group) {
log.Entry().Infof("Analyzing %v", config.SpotAuditIssueGroups)
filter := fmt.Sprintf("%v:%v", folderSelector.EntityType, folderSelector.SelectorOptions[0].Value)
fetchedIssueGroups, err := sys.GetProjectIssuesByIDAndFilterSetGroupedBySelector(projectVersionID, filter, filterSet.GUID, sys.ReduceIssueFilterSelectorSet(issueFilterSelectorSet, []string{"Category"}, nil))
if err != nil {
log.Entry().WithError(err).Fatalf("Failed to fetch project version issue groups with filter %v, filter set %v and selector %v for project version ID %v", filter, filterSet, issueFilterSelectorSet, projectVersionID)
}
totalMinusAuditedDelta += getSpotIssueCount(config, sys, fetchedIssueGroups, projectVersionID, filterSet, reducedFilterSelectorSet, influx, auditStatus)
}
}
return totalMinusAuditedDelta
}
func getSpotIssueCount(config fortifyExecuteScanOptions, sys fortify.System, spotCheckCategories []*models.ProjectVersionIssueGroup, projectVersionID int64, filterSet *models.FilterSet, issueFilterSelectorSet *models.IssueFilterSelectorSet, influx *fortifyExecuteScanInflux, auditStatus map[string]string) int {
overallDelta := 0
overallIssues := 0
overallIssuesAudited := 0
for _, issueGroup := range spotCheckCategories {
group := ""
total := 0
audited := 0
if issueGroup != nil {
group = *issueGroup.ID
total = int(*issueGroup.TotalCount)
audited = int(*issueGroup.AuditedCount)
}
flagOutput := ""
if ((total <= config.SpotCheckMinimum || config.SpotCheckMinimum < 0) && audited != total) || (total > config.SpotCheckMinimum && audited < config.SpotCheckMinimum) {
currentDelta := config.SpotCheckMinimum - audited
if config.SpotCheckMinimum < 0 || config.SpotCheckMinimum > total {
currentDelta = total - audited
}
if currentDelta > 0 {
filterSelectorFolder := sys.GetFilterSetByDisplayName(issueFilterSelectorSet, "Folder")
filterSelectorAnalysis := sys.GetFilterSetByDisplayName(issueFilterSelectorSet, "Analysis")
overallDelta += currentDelta
log.Entry().Errorf("[projectVersionId %v]: %v unaudited spot check issues detected in group %v", projectVersionID, currentDelta, group)
logIssueURL(config, projectVersionID, filterSelectorFolder, filterSelectorAnalysis)
flagOutput = checkString
}
}
overallIssues += total
overallIssuesAudited += audited
auditStatus[group] = fmt.Sprintf("%v total : %v audited %v", total, audited, flagOutput)
}
influx.fortify_data.fields.spotChecksTotal = fmt.Sprintf("%v", overallIssues)
influx.fortify_data.fields.spotChecksAudited = fmt.Sprintf("%v", overallIssuesAudited)
influx.fortify_data.fields.spotChecksGap = fmt.Sprintf("%v", overallDelta)
return overallDelta
}
func analyseSuspiciousExploitable(config fortifyExecuteScanOptions, sys fortify.System, projectVersion *models.ProjectVersion, filterSet *models.FilterSet, issueFilterSelectorSet *models.IssueFilterSelectorSet, influx *fortifyExecuteScanInflux, auditStatus map[string]string) int {
log.Entry().Info("Analyzing suspicious and exploitable issues")
reducedFilterSelectorSet := sys.ReduceIssueFilterSelectorSet(issueFilterSelectorSet, []string{"Analysis"}, []string{})
fetchedGroups, err := sys.GetProjectIssuesByIDAndFilterSetGroupedBySelector(projectVersion.ID, "", filterSet.GUID, reducedFilterSelectorSet)
suspiciousCount := 0
exploitableCount := 0
for _, issueGroup := range fetchedGroups {
if *issueGroup.ID == "3" {
suspiciousCount = int(*issueGroup.TotalCount)
} else if *issueGroup.ID == "4" {
exploitableCount = int(*issueGroup.TotalCount)
}
}
result := 0
if (suspiciousCount > 0 && config.ConsiderSuspicious) || exploitableCount > 0 {
result = result + suspiciousCount + exploitableCount
log.Entry().Errorf("[projectVersionId %v]: %v suspicious and %v exploitable issues detected", projectVersion.ID, suspiciousCount, exploitableCount)
log.Entry().Errorf("%v/html/ssc/index.jsp#!/version/%v/fix?issueGrouping=%v_%v&issueFilters=%v_%v", config.ServerURL, projectVersion.ID, reducedFilterSelectorSet.GroupBySet[0].EntityType, reducedFilterSelectorSet.GroupBySet[0].Value, reducedFilterSelectorSet.FilterBySet[0].EntityType, reducedFilterSelectorSet.FilterBySet[0].Value)
}
issueStatistics, err := sys.GetIssueStatisticsOfProjectVersion(projectVersion.ID)
if err != nil {
log.Entry().WithError(err).Errorf("Failed to fetch project version statistics for project version ID %v", projectVersion.ID)
}
auditStatus["Suspicious"] = fmt.Sprintf("%v", suspiciousCount)
auditStatus["Exploitable"] = fmt.Sprintf("%v", exploitableCount)
suppressedCount := *issueStatistics[0].SuppressedCount
if suppressedCount > 0 {
auditStatus["Suppressed"] = fmt.Sprintf("WARNING: Detected %v suppressed issues which could violate audit compliance!!!", suppressedCount)
}
influx.fortify_data.fields.suspicious = fmt.Sprintf("%v", suspiciousCount)
influx.fortify_data.fields.exploitable = fmt.Sprintf("%v", exploitableCount)
influx.fortify_data.fields.suppressed = fmt.Sprintf("%v", suppressedCount)
return result
}
func logIssueURL(config fortifyExecuteScanOptions, projectVersionID int64, folderSelector, analysisSelector *models.IssueFilterSelector) {
url := fmt.Sprintf("%v/html/ssc/index.jsp#!/version/%v/fix", config.ServerURL, projectVersionID)
if len(folderSelector.SelectorOptions) > 0 {
url += fmt.Sprintf("?issueFilters=%v_%v:%v",
folderSelector.EntityType,
folderSelector.Value,
folderSelector.SelectorOptions[0].Value)
} else {
log.Entry().Debugf("no 'filter by set' array entries")
}
if analysisSelector != nil {
url += fmt.Sprintf("&issueFilters=%v_%v:",
analysisSelector.EntityType,
analysisSelector.Value)
} else {
log.Entry().Debugf("no second entry in 'filter by set' array")
}
log.Entry().Error(url)
}
func generateAndDownloadQGateReport(config fortifyExecuteScanOptions, sys fortify.System, project *models.Project, projectVersion *models.ProjectVersion) ([]byte, error) {
log.Entry().Infof("Generating report with template ID %v", config.ReportTemplateID)
report, err := sys.GenerateQGateReport(project.ID, projectVersion.ID, int64(config.ReportTemplateID), *project.Name, *projectVersion.Name, config.ReportType)
if err != nil {
log.Entry().WithError(err).Fatal("Failed to generate Q-Gate report")
}
log.Entry().Debugf("Triggered report generation of report ID %v", report.ID)
status := report.Status
for status != "Complete" && status != "Error Processing" {
time.Sleep(10 * time.Second)
report, err = sys.GetReportDetails(report.ID)
if err != nil {
return []byte{}, fmt.Errorf("Failed to fetch Q-Gate report generation status: %w", err)
}
status = report.Status
}
data, err := sys.DownloadReportFile(config.ReportDownloadEndpoint, projectVersion.ID)
if err != nil {
return []byte{}, fmt.Errorf("Failed to download Q-Gate Report: %w", err)
}
return data, nil
}
func checkArtifactStatus(config fortifyExecuteScanOptions, sys fortify.System, projectVersionID int64, buildLabel string, filterSet *models.FilterSet, artifact *models.Artifact, numInvokes int) error {
numInvokes++
if "PROCESSING" == artifact.Status || "SCHED_PROCESSING" == artifact.Status {
if numInvokes >= (config.PollingMinutes * 6) {
return fmt.Errorf("Terminating after %v minutes since artifact for Project Version %v is still in status %v", config.PollingMinutes, projectVersionID, artifact.Status)
}
log.Entry().Infof("Most recent artifact uploaded on %v of Project Version %v is still in status %v...", artifact.UploadDate, projectVersionID, artifact.Status)
time.Sleep(10 * time.Second)
return verifyScanResultsFinishedUploading(config, sys, projectVersionID, buildLabel, filterSet, numInvokes)
}
if "REQUIRE_AUTH" == artifact.Status {
// verify no manual issue approval needed
return fmt.Errorf("There are artifacts that require manual approval for Project Version %v\n%v/html/ssc/index.jsp#!/version/%v/artifacts?filterSet=%v", projectVersionID, config.ServerURL, projectVersionID, filterSet.GUID)
}
if "ERROR_PROCESSING" == artifact.Status {
return fmt.Errorf("There are artifacts that failed processing for Project Version %v\n%v/html/ssc/index.jsp#!/version/%v/artifacts?filterSet=%v", projectVersionID, config.ServerURL, projectVersionID, filterSet.GUID)
}
return nil
}
func verifyScanResultsFinishedUploading(config fortifyExecuteScanOptions, sys fortify.System, projectVersionID int64, buildLabel string, filterSet *models.FilterSet, numInvokes int) error {
log.Entry().Debug("Verifying scan results have finished uploading and processing")
var artifacts []*models.Artifact
var relatedUpload *models.Artifact
for relatedUpload == nil {
artifacts, err := sys.GetArtifactsOfProjectVersion(projectVersionID)
log.Entry().Debugf("Received %v artifacts for project version ID %v", len(artifacts), projectVersionID)
if err != nil {
log.Entry().WithError(err).Fatalf("Failed to fetch artifacts of project version ID %v", projectVersionID)
}
if len(artifacts) > 0 {
latest := artifacts[0]
if err := checkArtifactStatus(config, sys, projectVersionID, buildLabel, filterSet, latest, numInvokes); err != nil {
return err
}
notFound := true
for _, artifact := range artifacts {
if len(buildLabel) > 0 && artifact.Embed != nil && artifact.Embed.Scans != nil && len(artifact.Embed.Scans) > 0 {
scan := artifact.Embed.Scans[0]
if notFound && scan != nil && strings.HasSuffix(scan.BuildLabel, buildLabel) {
relatedUpload = artifact
notFound = false
}
}
}
} else {
return fmt.Errorf("No uploaded artifacts for assessment detected for project version with ID %v", projectVersionID)
}
if relatedUpload == nil {
log.Entry().Warn("Unable to identify artifact based on the build label, will consider most recent artifact as related to the scan")
relatedUpload = artifacts[0]
}
}
differenceInSeconds := calculateTimeDifferenceToLastUpload(relatedUpload.UploadDate, projectVersionID)
// Use the absolute value for checking the time difference
if differenceInSeconds > float64(60*config.DeltaMinutes) {
return errors.New("No recent upload detected on Project Version")
}
warn := false
for _, upload := range artifacts {
if upload.Status == "ERROR_PROCESSING" {
warn = true
}
}
if warn {
log.Entry().Warn("Previous uploads detected that failed processing, please ensure that your scans are properly configured")
}
return nil
}
func calculateTimeDifferenceToLastUpload(uploadDate models.Iso8601MilliDateTime, projectVersionID int64) float64 {
log.Entry().Infof("Last upload on project version %v happened on %v", projectVersionID, uploadDate)
uploadDateAsTime := time.Time(uploadDate)
duration := time.Since(uploadDateAsTime)
log.Entry().Debugf("Difference duration is %v", duration)
absoluteSeconds := math.Abs(duration.Seconds())
log.Entry().Infof("Difference since %v in seconds is %v", uploadDateAsTime, absoluteSeconds)
return absoluteSeconds
}
func executeTemplatedCommand(command execRunner, cmdTemplate []string, context map[string]string) {
for index, cmdTemplatePart := range cmdTemplate {
result, err := piperutils.ExecuteTemplate(cmdTemplatePart, context)
if err != nil {
log.Entry().WithError(err).Fatalf("Failed to transform template for command fragment: %v", cmdTemplatePart)
}
cmdTemplate[index] = result
}
err := command.RunExecutable(cmdTemplate[0], cmdTemplate[1:]...)
if err != nil {
log.Entry().WithError(err).WithField("command", cmdTemplate).Fatal("Failed to execute command")
}
}
func autoresolvePipClasspath(executable string, parameters []string, file string, command execRunner) string {
// redirect stdout and create cp file from command output
outfile, err := os.Create(file)
if err != nil {
log.Entry().WithError(err).Fatal("Failed to create classpath file")
}
defer outfile.Close()
command.Stdout(outfile)
err = command.RunExecutable(executable, parameters...)
if err != nil {
log.Entry().WithError(err).WithField("command", fmt.Sprintf("%v with parameters %v", executable, parameters)).Fatal("Failed to run classpath autodetection command")
}
command.Stdout(log.Entry().Writer())
return readClasspathFile(file)
}
func autoresolveMavenClasspath(pomFilePath, file string, command execRunner) string {
executeOptions := maven.ExecuteOptions{
PomPath: pomFilePath,
Goals: []string{"dependency:build-classpath"},
Defines: []string{fmt.Sprintf("-Dmdep.outputFile=%v", file), "-DincludeScope=compile"},
ReturnStdout: false,
}
_, err := maven.Execute(&executeOptions, command)
if err != nil {
log.Entry().WithError(err).Warn("failed to determine classpath using Maven")
}
return readClasspathFile(file)
}
func readClasspathFile(file string) string {
data, err := ioutil.ReadFile(file)
if err != nil {
log.Entry().WithError(err).Warnf("failed to read classpath from file '%v'", file)
}
return strings.TrimSpace(string(data))
}
func triggerFortifyScan(config fortifyExecuteScanOptions, command execRunner, buildID, buildLabel string) {
// Do special Python related prep
pipVersion := "pip3"
if config.PythonVersion != "python3" {
pipVersion = "pip2"
}
classpath := ""
if config.BuildTool == "maven" {
if config.AutodetectClasspath {
classpath = autoresolveMavenClasspath(config.BuildDescriptorFile, classpathFileName, command)
}
if len(config.Translate) == 0 {
translate := `[{"classpath":"`
translate += classpath
translate += `","src":"**/*.xml **/*.html **/*.jsp **/*.js src/main/resources/**/* src/main/java/**/*"}]`
config.Translate = translate
}
}
if config.BuildTool == "pip" {
if config.AutodetectClasspath {
classpath = autoresolvePipClasspath(config.PythonVersion, []string{"-c", "import sys;p=sys.path;p.remove('');print(';'.join(p))"}, classpathFileName, command)
}
// install the dev dependencies
if len(config.PythonRequirementsFile) > 0 {
context := map[string]string{}
cmdTemplate := []string{pipVersion, "install", "--user", "-r", config.PythonRequirementsFile}
cmdTemplate = append(cmdTemplate, tokenize(config.PythonRequirementsInstallSuffix)...)
executeTemplatedCommand(command, cmdTemplate, context)
}
executeTemplatedCommand(command, tokenize(config.PythonInstallCommand), map[string]string{"Pip": pipVersion})
if len(config.Translate) == 0 {
translate := `[{"pythonPath":"`
translate += classpath
translate += ";"
translate += config.PythonAdditionalPath
translate += `","pythonIncludes":"`
translate += config.PythonIncludes
translate += `","pythonExcludes":"`
translate += strings.ReplaceAll(config.PythonExcludes, "-exclude ", "")
translate += `"}]`
config.Translate = translate
}
}
translateProject(&config, command, buildID, classpath)
scanProject(&config, command, buildID, buildLabel)
}
func translateProject(config *fortifyExecuteScanOptions, command execRunner, buildID, classpath string) {
var translateList []map[string]string
json.Unmarshal([]byte(config.Translate), &translateList)
log.Entry().Debugf("Translating with options: %v", translateList)
for _, translate := range translateList {
if len(classpath) > 0 {
translate["autoClasspath"] = classpath
}
handleSingleTranslate(config, command, buildID, translate)
}
}
func handleSingleTranslate(config *fortifyExecuteScanOptions, command execRunner, buildID string, t map[string]string) {
if t != nil {
log.Entry().Debugf("Handling translate config %v", t)
translateOptions := []string{
"-verbose",
"-64",
"-b",
buildID,
}
translateOptions = append(translateOptions, tokenize(config.Memory)...)
translateOptions = appendToOptions(config, translateOptions, t)
log.Entry().Debugf("Running sourceanalyzer translate command with options %v", translateOptions)
err := command.RunExecutable("sourceanalyzer", translateOptions...)
if err != nil {
log.Entry().WithError(err).WithField("translateOptions", translateOptions).Fatal("failed to execute sourceanalyzer translate command")
}
} else {
log.Entry().Debug("Skipping translate with nil value")
}
}
func scanProject(config *fortifyExecuteScanOptions, command execRunner, buildID, buildLabel string) {
var scanOptions = []string{
"-verbose",
"-64",
"-b",
buildID,
"-scan",
}
scanOptions = append(scanOptions, tokenize(config.Memory)...)
if config.QuickScan {
scanOptions = append(scanOptions, "-quick")
}
if len(buildLabel) > 0 {
scanOptions = append(scanOptions, "-build-label", buildLabel)
}
scanOptions = append(scanOptions, "-logfile", "target/fortify-scan.log", "-f", "target/result.fpr")
err := command.RunExecutable("sourceanalyzer", scanOptions...)
if err != nil {
log.Entry().WithError(err).WithField("scanOptions", scanOptions).Fatal("failed to execute sourceanalyzer scan command")
}
}
func determinePullRequestMerge(config fortifyExecuteScanOptions) string {
ctx, client, err := piperGithub.NewClient(config.GithubToken, config.GithubAPIURL, "")
if err == nil {
result, err := determinePullRequestMergeGithub(ctx, config, client.PullRequests)
if err != nil {
log.Entry().WithError(err).Warn("Failed to get PR metadata via GitHub client")
} else {
return result
}
}
log.Entry().Infof("Trying to determine PR ID in commit message: %v", config.CommitMessage)
r, _ := regexp.Compile(config.PullRequestMessageRegex)
matches := r.FindSubmatch([]byte(config.CommitMessage))
if matches != nil && len(matches) > 1 {
return string(matches[config.PullRequestMessageRegexGroup])
}
return ""
}
func determinePullRequestMergeGithub(ctx context.Context, config fortifyExecuteScanOptions, pullRequestServiceInstance pullRequestService) (string, error) {
options := github.PullRequestListOptions{State: "closed", Sort: "updated", Direction: "desc"}
prList, _, err := pullRequestServiceInstance.ListPullRequestsWithCommit(ctx, config.Owner, config.Repository, config.CommitID, &options)
if err == nil && len(prList) > 0 {
return fmt.Sprintf("%v", prList[0].GetNumber()), nil
}
return "", err
}
func appendToOptions(config *fortifyExecuteScanOptions, options []string, t map[string]string) []string {
if config.BuildTool == "windows" {
if len(t["aspnetcore"]) > 0 {
options = append(options, "-aspnetcore")
}
if len(t["dotNetCoreVersion"]) > 0 {
options = append(options, "-dotnet-core-version", t["dotNetCoreVersion"])
}
if len(t["exclude"]) > 0 {
options = append(options, "-exclude", t["exclude"])
}
if len(t["libDirs"]) > 0 {
options = append(options, "-libdirs", t["libDirs"])
}
return append(options, tokenize(t["src"])...)
}
if config.BuildTool == "maven" {
if len(t["autoClasspath"]) > 0 {
options = append(options, "-cp", t["autoClasspath"])
} else if len(t["classpath"]) > 0 {
options = append(options, "-cp", t["classpath"])
}
if len(t["extdirs"]) > 0 {
options = append(options, "-extdirs", t["extdirs"])
}
if len(t["javaBuildDir"]) > 0 {
options = append(options, "-java-build-dir", t["javaBuildDir"])
}
if len(t["source"]) > 0 {
options = append(options, "-source", t["source"])
}
if len(t["jdk"]) > 0 {
options = append(options, "-jdk", t["jdk"])
}
if len(t["sourcepath"]) > 0 {
options = append(options, "-sourcepath", t["sourcepath"])
}
return append(options, tokenize(t["src"])...)
}
if config.BuildTool == "pip" {
if len(t["autoClasspath"]) > 0 {
options = append(options, "-python-path", t["autoClasspath"])
} else if len(t["pythonPath"]) > 0 {
options = append(options, "-python-path", t["pythonPath"])
}
if len(t["djangoTemplatDirs"]) > 0 {
options = append(options, "-django-template-dirs", t["djangoTemplatDirs"])
}
if len(t["pythonExcludes"]) > 0 {
options = append(options, "-exclude", t["pythonExcludes"])
}
return append(options, t["pythonIncludes"])
}
return options
}

View File

@ -0,0 +1,630 @@
// Code generated by piper's step-generator. DO NOT EDIT.
package cmd
import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/SAP/jenkins-library/pkg/config"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperenv"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/spf13/cobra"
)
type fortifyExecuteScanOptions struct {
AuthToken string `json:"authToken,omitempty"`
GithubToken string `json:"githubToken,omitempty"`
AutoCreate bool `json:"autoCreate,omitempty"`
MvnCustomArgs string `json:"mvnCustomArgs,omitempty"`
ModulePath string `json:"modulePath,omitempty"`
PythonRequirementsFile string `json:"pythonRequirementsFile,omitempty"`
AutodetectClasspath bool `json:"autodetectClasspath,omitempty"`
MustAuditIssueGroups string `json:"mustAuditIssueGroups,omitempty"`
SpotAuditIssueGroups string `json:"spotAuditIssueGroups,omitempty"`
PythonRequirementsInstallSuffix string `json:"pythonRequirementsInstallSuffix,omitempty"`
PythonVersion string `json:"pythonVersion,omitempty"`
UploadResults bool `json:"uploadResults,omitempty"`
BuildDescriptorFile string `json:"buildDescriptorFile,omitempty"`
CommitID string `json:"commitId,omitempty"`
CommitMessage string `json:"commitMessage,omitempty"`
GithubAPIURL string `json:"githubApiUrl,omitempty"`
Owner string `json:"owner,omitempty"`
Repository string `json:"repository,omitempty"`
Memory string `json:"memory,omitempty"`
UpdateRulePack bool `json:"updateRulePack,omitempty"`
PythonExcludes string `json:"pythonExcludes,omitempty"`
ReportDownloadEndpoint string `json:"reportDownloadEndpoint,omitempty"`
PollingMinutes int `json:"pollingMinutes,omitempty"`
QuickScan bool `json:"quickScan,omitempty"`
Translate string `json:"translate,omitempty"`
APIEndpoint string `json:"apiEndpoint,omitempty"`
ReportType string `json:"reportType,omitempty"`
PythonAdditionalPath string `json:"pythonAdditionalPath,omitempty"`
ArtifactURL string `json:"artifactUrl,omitempty"`
ConsiderSuspicious bool `json:"considerSuspicious,omitempty"`
FprUploadEndpoint string `json:"fprUploadEndpoint,omitempty"`
ProjectName string `json:"projectName,omitempty"`
PythonIncludes string `json:"pythonIncludes,omitempty"`
Reporting bool `json:"reporting,omitempty"`
ServerURL string `json:"serverUrl,omitempty"`
BuildDescriptorExcludeList string `json:"buildDescriptorExcludeList,omitempty"`
PullRequestMessageRegexGroup int `json:"pullRequestMessageRegexGroup,omitempty"`
DeltaMinutes int `json:"deltaMinutes,omitempty"`
SpotCheckMinimum int `json:"spotCheckMinimum,omitempty"`
FprDownloadEndpoint string `json:"fprDownloadEndpoint,omitempty"`
DefaultVersioningModel string `json:"defaultVersioningModel,omitempty"`
PythonInstallCommand string `json:"pythonInstallCommand,omitempty"`
ReportTemplateID int `json:"reportTemplateId,omitempty"`
FilterSetTitle string `json:"filterSetTitle,omitempty"`
PullRequestName string `json:"pullRequestName,omitempty"`
PullRequestMessageRegex string `json:"pullRequestMessageRegex,omitempty"`
BuildTool string `json:"buildTool,omitempty"`
}
type fortifyExecuteScanInflux struct {
fortify_data struct {
fields struct {
projectName string
projectVersion string
violations string
corporateTotal string
corporateAudited string
auditAllTotal string
auditAllAudited string
spotChecksTotal string
spotChecksAudited string
spotChecksGap string
suspicious string
exploitable string
suppressed string
}
tags struct {
}
}
}
func (i *fortifyExecuteScanInflux) persist(path, resourceName string) {
measurementContent := []struct {
measurement string
valType string
name string
value string
}{
{valType: config.InfluxField, measurement: "fortify_data", name: "projectName", value: i.fortify_data.fields.projectName},
{valType: config.InfluxField, measurement: "fortify_data", name: "projectVersion", value: i.fortify_data.fields.projectVersion},
{valType: config.InfluxField, measurement: "fortify_data", name: "violations", value: i.fortify_data.fields.violations},
{valType: config.InfluxField, measurement: "fortify_data", name: "corporateTotal", value: i.fortify_data.fields.corporateTotal},
{valType: config.InfluxField, measurement: "fortify_data", name: "corporateAudited", value: i.fortify_data.fields.corporateAudited},
{valType: config.InfluxField, measurement: "fortify_data", name: "auditAllTotal", value: i.fortify_data.fields.auditAllTotal},
{valType: config.InfluxField, measurement: "fortify_data", name: "auditAllAudited", value: i.fortify_data.fields.auditAllAudited},
{valType: config.InfluxField, measurement: "fortify_data", name: "spotChecksTotal", value: i.fortify_data.fields.spotChecksTotal},
{valType: config.InfluxField, measurement: "fortify_data", name: "spotChecksAudited", value: i.fortify_data.fields.spotChecksAudited},
{valType: config.InfluxField, measurement: "fortify_data", name: "spotChecksGap", value: i.fortify_data.fields.spotChecksGap},
{valType: config.InfluxField, measurement: "fortify_data", name: "suspicious", value: i.fortify_data.fields.suspicious},
{valType: config.InfluxField, measurement: "fortify_data", name: "exploitable", value: i.fortify_data.fields.exploitable},
{valType: config.InfluxField, measurement: "fortify_data", name: "suppressed", value: i.fortify_data.fields.suppressed},
}
errCount := 0
for _, metric := range measurementContent {
err := piperenv.SetResourceParameter(path, resourceName, filepath.Join(metric.measurement, fmt.Sprintf("%vs", metric.valType), metric.name), metric.value)
if err != nil {
log.Entry().WithError(err).Error("Error persisting influx environment.")
errCount++
}
}
if errCount > 0 {
log.Entry().Fatal("failed to persist Influx environment")
}
}
// FortifyExecuteScanCommand This BETA step executes a Fortify scan on the specified project to perform static code analysis and check the source code for security flaws.
func FortifyExecuteScanCommand() *cobra.Command {
const STEP_NAME = "fortifyExecuteScan"
metadata := fortifyExecuteScanMetadata()
var stepConfig fortifyExecuteScanOptions
var startTime time.Time
var influx fortifyExecuteScanInflux
var createFortifyExecuteScanCmd = &cobra.Command{
Use: STEP_NAME,
Short: "This BETA step executes a Fortify scan on the specified project to perform static code analysis and check the source code for security flaws.",
Long: `This step executes a Fortify scan on the specified project to perform static code analysis and check the source code for security flaws.
The Fortify step triggers a scan locally on your Jenkins within a docker container so finally you have to supply a docker image with a Fortify SCA
and Java plus Maven or alternatively Python installed into it for being able to perform any scans.
DISCLAIMER: The step has not yet been tested on a wide variaty of projects, and is therefore considered of BETA quality.`,
PreRunE: func(cmd *cobra.Command, args []string) error {
startTime = time.Now()
log.SetStepName(STEP_NAME)
log.SetVerbose(GeneralConfig.Verbose)
path, _ := os.Getwd()
fatalHook := &log.FatalHook{CorrelationID: GeneralConfig.CorrelationID, Path: path}
log.RegisterHook(fatalHook)
err := PrepareConfig(cmd, &metadata, STEP_NAME, &stepConfig, config.OpenPiperFile)
if err != nil {
return err
}
log.RegisterSecret(stepConfig.AuthToken)
log.RegisterSecret(stepConfig.GithubToken)
if len(GeneralConfig.HookConfig.SentryConfig.Dsn) > 0 {
sentryHook := log.NewSentryHook(GeneralConfig.HookConfig.SentryConfig.Dsn, GeneralConfig.CorrelationID)
log.RegisterHook(&sentryHook)
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
telemetryData := telemetry.CustomData{}
telemetryData.ErrorCode = "1"
handler := func() {
influx.persist(GeneralConfig.EnvRootPath, "influx")
telemetryData.Duration = fmt.Sprintf("%v", time.Since(startTime).Milliseconds())
telemetry.Send(&telemetryData)
}
log.DeferExitHandler(handler)
defer handler()
telemetry.Initialize(GeneralConfig.NoTelemetry, STEP_NAME)
fortifyExecuteScan(stepConfig, &telemetryData, &influx)
telemetryData.ErrorCode = "0"
log.Entry().Info("SUCCESS")
},
}
addFortifyExecuteScanFlags(createFortifyExecuteScanCmd, &stepConfig)
return createFortifyExecuteScanCmd
}
func addFortifyExecuteScanFlags(cmd *cobra.Command, stepConfig *fortifyExecuteScanOptions) {
cmd.Flags().StringVar(&stepConfig.AuthToken, "authToken", os.Getenv("PIPER_authToken"), "The FortifyToken to use for authentication")
cmd.Flags().StringVar(&stepConfig.GithubToken, "githubToken", os.Getenv("PIPER_githubToken"), "GitHub personal access token as per https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line")
cmd.Flags().BoolVar(&stepConfig.AutoCreate, "autoCreate", false, "Whether Fortify project and project version shall be implicitly auto created in case they cannot be found in the backend")
cmd.Flags().StringVar(&stepConfig.MvnCustomArgs, "mvnCustomArgs", ``, "Allows providing additional Maven command line parameters")
cmd.Flags().StringVar(&stepConfig.ModulePath, "modulePath", `./`, "Allows providing the path for the module to scan")
cmd.Flags().StringVar(&stepConfig.PythonRequirementsFile, "pythonRequirementsFile", os.Getenv("PIPER_pythonRequirementsFile"), "The requirements file used in `buildTool: 'pip'` to populate the build environment with the necessary dependencies")
cmd.Flags().BoolVar(&stepConfig.AutodetectClasspath, "autodetectClasspath", true, "Whether the classpath is automatically determined via build tool i.e. maven or pip or not at all")
cmd.Flags().StringVar(&stepConfig.MustAuditIssueGroups, "mustAuditIssueGroups", `Corporate Security Requirements, Audit All`, "Comma separated list of issue groups that must be audited completely")
cmd.Flags().StringVar(&stepConfig.SpotAuditIssueGroups, "spotAuditIssueGroups", `Spot Checks of Each Category`, "Comma separated list of issue groups that are spot checked and for which `spotCheckMinimum` audited issues are enforced")
cmd.Flags().StringVar(&stepConfig.PythonRequirementsInstallSuffix, "pythonRequirementsInstallSuffix", os.Getenv("PIPER_pythonRequirementsInstallSuffix"), "The suffix for the command used to install the requirements file in `buildTool: 'pip'` to populate the build environment with the necessary dependencies")
cmd.Flags().StringVar(&stepConfig.PythonVersion, "pythonVersion", `python3`, "Python version to be used in `buildTool: 'pip'`")
cmd.Flags().BoolVar(&stepConfig.UploadResults, "uploadResults", true, "Whether results shall be uploaded or not")
cmd.Flags().StringVar(&stepConfig.BuildDescriptorFile, "buildDescriptorFile", os.Getenv("PIPER_buildDescriptorFile"), "Path to the build descriptor file addressing the module/folder to be scanned. Defaults are for buildTool=`maven`: `./pom.xml`, buildTool=`pip`: `./setup.py`.")
cmd.Flags().StringVar(&stepConfig.CommitID, "commitId", os.Getenv("PIPER_commitId"), "Set the Git commit ID for identifying artifacts throughout the scan.")
cmd.Flags().StringVar(&stepConfig.CommitMessage, "commitMessage", os.Getenv("PIPER_commitMessage"), "Set the Git commit message for identifying pull request merges throughout the scan.")
cmd.Flags().StringVar(&stepConfig.GithubAPIURL, "githubApiUrl", `https://api.github.com`, "Set the GitHub API url.")
cmd.Flags().StringVar(&stepConfig.Owner, "owner", os.Getenv("PIPER_owner"), "Set the GitHub organization.")
cmd.Flags().StringVar(&stepConfig.Repository, "repository", os.Getenv("PIPER_repository"), "Set the GitHub repository.")
cmd.Flags().StringVar(&stepConfig.Memory, "memory", `-Xmx4G -Xms512M`, "The amount of memory granted to the translate/scan executions")
cmd.Flags().BoolVar(&stepConfig.UpdateRulePack, "updateRulePack", true, "Whether the rule pack shall be updated and pulled from Fortify SSC before scanning or not")
cmd.Flags().StringVar(&stepConfig.PythonExcludes, "pythonExcludes", `-exclude ./**/tests/**/*;./**/setup.py`, "The excludes pattern used in `buildTool: 'pip'` for excluding specific .py files i.e. tests")
cmd.Flags().StringVar(&stepConfig.ReportDownloadEndpoint, "reportDownloadEndpoint", `/transfer/reportDownload.html`, "Fortify SSC endpoint for Report downloads")
cmd.Flags().IntVar(&stepConfig.PollingMinutes, "pollingMinutes", 30, "The number of minutes for which an uploaded FPR artifact's status is being polled to finish queuing/processing, if exceeded polling will be stopped and an error will be thrown")
cmd.Flags().BoolVar(&stepConfig.QuickScan, "quickScan", false, "Whether a quick scan should be performed, please consult the related Fortify documentation on JAM on the impact of this setting")
cmd.Flags().StringVar(&stepConfig.Translate, "translate", os.Getenv("PIPER_translate"), "JSON string of list of maps with required key `'src'`, and optional keys `'exclude'`, `'libDirs'`, `'aspnetcore'`, and `'dotNetCoreVersion'`")
cmd.Flags().StringVar(&stepConfig.APIEndpoint, "apiEndpoint", `/api/v1`, "Fortify SSC endpoint used for uploading the scan results and checking the audit state")
cmd.Flags().StringVar(&stepConfig.ReportType, "reportType", `PDF`, "The type of report to be generated")
cmd.Flags().StringVar(&stepConfig.PythonAdditionalPath, "pythonAdditionalPath", `./lib;.`, "The addional path which can be used in `buildTool: 'pip'` for customization purposes")
cmd.Flags().StringVar(&stepConfig.ArtifactURL, "artifactUrl", os.Getenv("PIPER_artifactUrl"), "Path/Url pointing to an additional artifact repository for resolution of additional artifacts during the build")
cmd.Flags().BoolVar(&stepConfig.ConsiderSuspicious, "considerSuspicious", true, "Whether suspicious issues should trigger the check to fail or not")
cmd.Flags().StringVar(&stepConfig.FprUploadEndpoint, "fprUploadEndpoint", `/upload/resultFileUpload.html`, "Fortify SSC endpoint for FPR uploads")
cmd.Flags().StringVar(&stepConfig.ProjectName, "projectName", `{{list .GroupID .ArtifactID | join "-" | trimAll "-"}}`, "The project used for reporting results in SSC")
cmd.Flags().StringVar(&stepConfig.PythonIncludes, "pythonIncludes", `./**/*`, "The includes pattern used in `buildTool: 'pip'` for including .py files")
cmd.Flags().BoolVar(&stepConfig.Reporting, "reporting", false, "Influences whether a report is generated or not")
cmd.Flags().StringVar(&stepConfig.ServerURL, "serverUrl", os.Getenv("PIPER_serverUrl"), "Fortify SSC Url to be used for accessing the APIs")
cmd.Flags().StringVar(&stepConfig.BuildDescriptorExcludeList, "buildDescriptorExcludeList", `[]`, "Build descriptor files to exclude modules from being scanned")
cmd.Flags().IntVar(&stepConfig.PullRequestMessageRegexGroup, "pullRequestMessageRegexGroup", 1, "The group number for extracting the pull request id in `pullRequestMessageRegex`")
cmd.Flags().IntVar(&stepConfig.DeltaMinutes, "deltaMinutes", 5, "The number of minutes for which an uploaded FPR artifact is considered to be recent and healthy, if exceeded an error will be thrown")
cmd.Flags().IntVar(&stepConfig.SpotCheckMinimum, "spotCheckMinimum", 1, "The minimum number of issues that must be audited per category in the `Spot Checks of each Category` folder to avoid an error being thrown")
cmd.Flags().StringVar(&stepConfig.FprDownloadEndpoint, "fprDownloadEndpoint", `/download/currentStateFprDownload.html`, "Fortify SSC endpoint for FPR downloads")
cmd.Flags().StringVar(&stepConfig.DefaultVersioningModel, "defaultVersioningModel", `major`, "The default project versioning model used in case `projectVersion` parameter is empty for creating the version based on the build descriptor version to report results in SSC, can be one of `'major'`, `'major-minor'`, `'semantic'`, `'full'`")
cmd.Flags().StringVar(&stepConfig.PythonInstallCommand, "pythonInstallCommand", `{{.Pip}} install --user .`, "Additional install command that can be run when `buildTool: 'pip'` is used which allows further customizing the execution environment of the scan")
cmd.Flags().IntVar(&stepConfig.ReportTemplateID, "reportTemplateId", 18, "Report template ID to be used for generating the Fortify report")
cmd.Flags().StringVar(&stepConfig.FilterSetTitle, "filterSetTitle", `SAP`, "Title of the filter set to use for analysing the results")
cmd.Flags().StringVar(&stepConfig.PullRequestName, "pullRequestName", os.Getenv("PIPER_pullRequestName"), "The name of the pull request branch which will trigger creation of a new version in Fortify SSC based on the master branch version")
cmd.Flags().StringVar(&stepConfig.PullRequestMessageRegex, "pullRequestMessageRegex", `.*Merge pull request #(\\d+) from.*`, "Regex used to identify the PR-XXX reference within the merge commit message")
cmd.Flags().StringVar(&stepConfig.BuildTool, "buildTool", `maven`, "Scan type used for the step which can be `'maven'`, `'pip'`")
cmd.MarkFlagRequired("authToken")
}
// retrieve step metadata
func fortifyExecuteScanMetadata() config.StepData {
var theMetaData = config.StepData{
Metadata: config.StepMetadata{
Name: "fortifyExecuteScan",
Aliases: []config.Alias{},
},
Spec: config.StepSpec{
Inputs: config.StepInputs{
Parameters: []config.StepParameters{
{
Name: "authToken",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: true,
Aliases: []config.Alias{},
},
{
Name: "githubToken",
ResourceRef: []config.ResourceReference{},
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "autoCreate",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "bool",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "mvnCustomArgs",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "modulePath",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "pythonRequirementsFile",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "autodetectClasspath",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "bool",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "mustAuditIssueGroups",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "spotAuditIssueGroups",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "pythonRequirementsInstallSuffix",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "pythonVersion",
ResourceRef: []config.ResourceReference{},
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "uploadResults",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "bool",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "buildDescriptorFile",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "commitId",
ResourceRef: []config.ResourceReference{{Name: "commonPipelineEnvironment", Param: "git/commitId"}},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "commitMessage",
ResourceRef: []config.ResourceReference{{Name: "commonPipelineEnvironment", Param: "git/commitMessage"}},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "githubApiUrl",
ResourceRef: []config.ResourceReference{},
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "owner",
ResourceRef: []config.ResourceReference{{Name: "commonPipelineEnvironment", Param: "github/owner"}},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "githubOrg"}},
},
{
Name: "repository",
ResourceRef: []config.ResourceReference{{Name: "commonPipelineEnvironment", Param: "github/repository"}},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "githubRepo"}},
},
{
Name: "memory",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "updateRulePack",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "bool",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "pythonExcludes",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "reportDownloadEndpoint",
ResourceRef: []config.ResourceReference{},
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "fortifyReportDownloadEndpoint"}},
},
{
Name: "pollingMinutes",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "int",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "quickScan",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "bool",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "translate",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "apiEndpoint",
ResourceRef: []config.ResourceReference{},
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "fortifyApiEndpoint"}},
},
{
Name: "reportType",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "pythonAdditionalPath",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "artifactUrl",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "considerSuspicious",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "bool",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "fprUploadEndpoint",
ResourceRef: []config.ResourceReference{},
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "fortifyFprUploadEndpoint"}},
},
{
Name: "projectName",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "fortifyProjectName"}},
},
{
Name: "pythonIncludes",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "reporting",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "bool",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "serverUrl",
ResourceRef: []config.ResourceReference{},
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "fortifyServerUrl"}, {Name: "sscUrl"}},
},
{
Name: "buildDescriptorExcludeList",
ResourceRef: []config.ResourceReference{},
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "pullRequestMessageRegexGroup",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "int",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "deltaMinutes",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "int",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "spotCheckMinimum",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "int",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "fprDownloadEndpoint",
ResourceRef: []config.ResourceReference{},
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "fortifyFprDownloadEndpoint"}},
},
{
Name: "defaultVersioningModel",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "pythonInstallCommand",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "reportTemplateId",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "int",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "filterSetTitle",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "pullRequestName",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "pullRequestMessageRegex",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
{
Name: "buildTool",
ResourceRef: []config.ResourceReference{},
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
},
},
},
},
}
return theMetaData
}

View File

@ -0,0 +1,16 @@
package cmd
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestFortifyExecuteScanCommand(t *testing.T) {
testCmd := FortifyExecuteScanCommand()
// only high level testing performed - details are tested in step generation procudure
assert.Equal(t, "fortifyExecuteScan", testCmd.Use, "command name incorrect")
}

View File

@ -0,0 +1,612 @@
package cmd
import (
"context"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/google/go-github/v28/github"
"github.com/stretchr/testify/assert"
"github.com/piper-validation/fortify-client-go/models"
)
type fortifyMock struct {
Successive bool
}
func (f *fortifyMock) GetProjectByName(name string, autoCreate bool, projectVersion string) (*models.Project, error) {
return &models.Project{Name: &name}, nil
}
func (f *fortifyMock) GetProjectVersionDetailsByProjectIDAndVersionName(id int64, name string, autoCreate bool, projectName string) (*models.ProjectVersion, error) {
return &models.ProjectVersion{ID: id, Name: &name, Project: &models.Project{Name: &projectName}}, nil
}
func (f *fortifyMock) GetProjectVersionAttributesByProjectVersionID(id int64) ([]*models.Attribute, error) {
return []*models.Attribute{}, nil
}
func (f *fortifyMock) SetProjectVersionAttributesByProjectVersionID(id int64, attributes []*models.Attribute) ([]*models.Attribute, error) {
return attributes, nil
}
func (f *fortifyMock) CreateProjectVersionIfNotExist(projectName, projectVersionName, description string) (*models.ProjectVersion, error) {
return &models.ProjectVersion{ID: 4711, Name: &projectVersionName, Project: &models.Project{Name: &projectName}}, nil
}
func (f *fortifyMock) LookupOrCreateProjectVersionDetailsForPullRequest(projectID int64, masterProjectVersion *models.ProjectVersion, pullRequestName string) (*models.ProjectVersion, error) {
return &models.ProjectVersion{ID: 4712, Name: &pullRequestName, Project: masterProjectVersion.Project}, nil
}
func (f *fortifyMock) CreateProjectVersion(version *models.ProjectVersion) (*models.ProjectVersion, error) {
return version, nil
}
func (f *fortifyMock) ProjectVersionCopyFromPartial(sourceID, targetID int64) error {
return nil
}
func (f *fortifyMock) ProjectVersionCopyCurrentState(sourceID, targetID int64) error {
return nil
}
func (f *fortifyMock) ProjectVersionCopyPermissions(sourceID, targetID int64) error {
return nil
}
func (f *fortifyMock) CommitProjectVersion(id int64) (*models.ProjectVersion, error) {
name := "Committed"
return &models.ProjectVersion{ID: id, Name: &name}, nil
}
func (f *fortifyMock) MergeProjectVersionStateOfPRIntoMaster(downloadEndpoint, uploadEndpoint string, masterProjectID, masterProjectVersionID int64, pullRequestName string) error {
return nil
}
func (f *fortifyMock) GetArtifactsOfProjectVersion(id int64) ([]*models.Artifact, error) {
if id == 4711 {
return []*models.Artifact{{Status: "PROCESSED", UploadDate: models.Iso8601MilliDateTime(time.Now().UTC())}}, nil
}
if id == 4712 {
return []*models.Artifact{{Status: "ERROR_PROCESSING", UploadDate: models.Iso8601MilliDateTime(time.Now().UTC())}}, nil
}
if id == 4713 {
return []*models.Artifact{{Status: "REQUIRE_AUTH", UploadDate: models.Iso8601MilliDateTime(time.Now().UTC())}}, nil
}
if id == 4714 {
return []*models.Artifact{{Status: "PROCESSING", UploadDate: models.Iso8601MilliDateTime(time.Now().UTC())}}, nil
}
if id == 4715 {
return []*models.Artifact{{Status: "PROCESSED", Embed: &models.EmbeddedScans{[]*models.Scan{{BuildLabel: "/commit/test"}}}, UploadDate: models.Iso8601MilliDateTime(time.Now().UTC())}}, nil
}
return []*models.Artifact{}, nil
}
func (f *fortifyMock) GetFilterSetOfProjectVersionByTitle(id int64, title string) (*models.FilterSet, error) {
return &models.FilterSet{}, nil
}
func (f *fortifyMock) GetIssueFilterSelectorOfProjectVersionByName(id int64, names []string, options []string) (*models.IssueFilterSelectorSet, error) {
return &models.IssueFilterSelectorSet{}, nil
}
func (f *fortifyMock) GetFilterSetByDisplayName(issueFilterSelectorSet *models.IssueFilterSelectorSet, name string) *models.IssueFilterSelector {
if issueFilterSelectorSet.FilterBySet != nil {
for _, filter := range issueFilterSelectorSet.FilterBySet {
if filter.DisplayName == name {
return filter
}
}
}
return nil
}
func (f *fortifyMock) GetProjectIssuesByIDAndFilterSetGroupedBySelector(id int64, filter, filterSetGUID string, issueFilterSelectorSet *models.IssueFilterSelectorSet) ([]*models.ProjectVersionIssueGroup, error) {
if filter == "ET1:abcd" {
group := "HTTP Verb tampering"
total := int32(4)
audited := int32(3)
group2 := "Password in code"
total2 := int32(4)
audited2 := int32(4)
group3 := "Memory leak"
total3 := int32(5)
audited3 := int32(4)
return []*models.ProjectVersionIssueGroup{
{ID: &group, TotalCount: &total, AuditedCount: &audited},
{ID: &group2, TotalCount: &total2, AuditedCount: &audited2},
{ID: &group3, TotalCount: &total3, AuditedCount: &audited3},
}, nil
}
if issueFilterSelectorSet != nil && issueFilterSelectorSet.FilterBySet[0].GUID == "3" {
group := "3"
total := int32(4)
audited := int32(0)
group2 := "4"
total2 := int32(5)
audited2 := int32(0)
return []*models.ProjectVersionIssueGroup{
{ID: &group, TotalCount: &total, AuditedCount: &audited},
{ID: &group2, TotalCount: &total2, AuditedCount: &audited2},
}, nil
}
group := "Audit All"
total := int32(15)
audited := int32(12)
group2 := "Corporate Security Requirements"
total2 := int32(20)
audited2 := int32(11)
group3 := "Spot Checks of Each Category"
total3 := int32(5)
audited3 := int32(4)
return []*models.ProjectVersionIssueGroup{
{ID: &group, TotalCount: &total, AuditedCount: &audited},
{ID: &group2, TotalCount: &total2, AuditedCount: &audited2},
{ID: &group3, TotalCount: &total3, AuditedCount: &audited3},
}, nil
}
func (f *fortifyMock) ReduceIssueFilterSelectorSet(issueFilterSelectorSet *models.IssueFilterSelectorSet, names []string, options []string) *models.IssueFilterSelectorSet {
return issueFilterSelectorSet
}
func (f *fortifyMock) GetIssueStatisticsOfProjectVersion(id int64) ([]*models.IssueStatistics, error) {
suppressed := int32(6)
return []*models.IssueStatistics{{SuppressedCount: &suppressed}}, nil
}
func (f *fortifyMock) GenerateQGateReport(projectID, projectVersionID, reportTemplateID int64, projectName, projectVersionName, reportFormat string) (*models.SavedReport, error) {
if !f.Successive {
f.Successive = true
return &models.SavedReport{Status: "Processing"}, nil
}
f.Successive = false
return &models.SavedReport{Status: "Complete"}, nil
}
func (f *fortifyMock) GetReportDetails(id int64) (*models.SavedReport, error) {
return &models.SavedReport{Status: "Complete"}, nil
}
func (f *fortifyMock) UploadResultFile(endpoint, file string, projectVersionID int64) error {
return nil
}
func (f *fortifyMock) DownloadReportFile(endpoint string, projectVersionID int64) ([]byte, error) {
return []byte("abcd"), nil
}
func (f *fortifyMock) DownloadResultFile(endpoint string, projectVersionID int64) ([]byte, error) {
return []byte("defg"), nil
}
type pullRequestServiceMock struct{}
func (prService pullRequestServiceMock) ListPullRequestsWithCommit(ctx context.Context, owner, repo, sha string, opts *github.PullRequestListOptions) ([]*github.PullRequest, *github.Response, error) {
if owner == "A" {
result := 17
return []*github.PullRequest{{Number: &result}}, &github.Response{}, nil
} else if owner == "C" {
return []*github.PullRequest{}, &github.Response{}, errors.New("Test error")
}
return []*github.PullRequest{}, &github.Response{}, nil
}
type execRunnerMock struct {
numExecutions int
current *execution
executions []*execution
}
type execution struct {
dirValue string
envValue []string
outWriter io.Writer
errWriter io.Writer
executable string
parameters []string
}
func (er *execRunnerMock) newExecution() *execution {
newExecution := &execution{}
er.executions = append(er.executions, newExecution)
return newExecution
}
func (er *execRunnerMock) currentExecution() *execution {
if nil == er.current {
er.numExecutions = 0
er.current = er.newExecution()
}
return er.current
}
func (er *execRunnerMock) SetDir(d string) {
er.currentExecution().dirValue = d
}
func (er *execRunnerMock) SetEnv(e []string) {
er.currentExecution().envValue = e
}
func (er *execRunnerMock) Stdout(out io.Writer) {
er.currentExecution().outWriter = out
}
func (er *execRunnerMock) Stderr(err io.Writer) {
er.currentExecution().errWriter = err
}
func (er *execRunnerMock) RunExecutable(e string, p ...string) error {
er.numExecutions++
er.currentExecution().executable = e
er.currentExecution().parameters = p
classpathPip := "/usr/lib/python35.zip;/usr/lib/python3.5;/usr/lib/python3.5/plat-x86_64-linux-gnu;/usr/lib/python3.5/lib-dynload;/home/piper/.local/lib/python3.5/site-packages;/usr/local/lib/python3.5/dist-packages;/usr/lib/python3/dist-packages;./lib"
classpathMaven := "some.jar;someother.jar"
if e == "python2" {
er.currentExecution().outWriter.Write([]byte(classpathPip))
} else if e == "mvn" {
path := strings.ReplaceAll(p[2], "-Dmdep.outputFile=", "")
err := ioutil.WriteFile(path, []byte(classpathMaven), 0644)
if err != nil {
return err
}
}
er.current = er.newExecution()
return nil
}
func TestParametersAreValidated(t *testing.T) {
type parameterTestData struct {
nameOfRun string
config fortifyExecuteScanOptions
expectedError string
}
testData := []parameterTestData{
{
nameOfRun: "all parameters empty",
config: fortifyExecuteScanOptions{},
expectedError: "unable to get artifact from descriptor : build tool '' not supported",
},
}
for _, data := range testData {
t.Run(data.nameOfRun, func(t *testing.T) {
ff := fortifyMock{}
runner := execRunnerMock{}
influx := fortifyExecuteScanInflux{}
auditStatus := map[string]string{}
err := runFortifyScan(data.config, &ff, &runner, nil, &influx, auditStatus)
assert.EqualError(t, err, data.expectedError)
})
}
}
func TestAnalyseSuspiciousExploitable(t *testing.T) {
config := fortifyExecuteScanOptions{SpotCheckMinimum: 4, MustAuditIssueGroups: "Audit All, Corporate Security Requirements", SpotAuditIssueGroups: "Spot Checks of Each Category"}
ff := fortifyMock{}
influx := fortifyExecuteScanInflux{}
name := "test"
selectorGUID := "3"
selectorName := "Analysis"
selectorEntityType := "CUSTOMTAG"
projectVersion := models.ProjectVersion{ID: 4711, Name: &name}
auditStatus := map[string]string{}
selectorSet := models.IssueFilterSelectorSet{
FilterBySet: []*models.IssueFilterSelector{
{
GUID: selectorGUID,
DisplayName: selectorName,
EntityType: selectorEntityType,
},
},
GroupBySet: []*models.IssueSelector{
{
GUID: &selectorGUID,
DisplayName: &selectorName,
EntityType: &selectorEntityType,
},
},
}
issues := analyseSuspiciousExploitable(config, &ff, &projectVersion, &models.FilterSet{}, &selectorSet, &influx, auditStatus)
assert.Equal(t, 9, issues)
assert.Equal(t, "4", influx.fortify_data.fields.suspicious)
assert.Equal(t, "5", influx.fortify_data.fields.exploitable)
assert.Equal(t, "6", influx.fortify_data.fields.suppressed)
}
func TestAnalyseUnauditedIssues(t *testing.T) {
config := fortifyExecuteScanOptions{SpotCheckMinimum: 4, MustAuditIssueGroups: "Audit All, Corporate Security Requirements", SpotAuditIssueGroups: "Spot Checks of Each Category"}
ff := fortifyMock{}
influx := fortifyExecuteScanInflux{}
name := "test"
projectVersion := models.ProjectVersion{ID: 4711, Name: &name}
auditStatus := map[string]string{}
selectorSet := models.IssueFilterSelectorSet{
FilterBySet: []*models.IssueFilterSelector{
{
GUID: "1",
DisplayName: "Folder",
EntityType: "ET1",
SelectorOptions: []*models.SelectorOption{
{
Value: "abcd",
},
},
},
{
GUID: "2",
DisplayName: "Category",
EntityType: "ET2",
SelectorOptions: []*models.SelectorOption{
{
Value: "abcd",
},
},
},
{
GUID: "3",
DisplayName: "Analysis",
EntityType: "ET3",
SelectorOptions: []*models.SelectorOption{
{
Value: "abcd",
},
},
},
},
}
issues := analyseUnauditedIssues(config, &ff, &projectVersion, &models.FilterSet{}, &selectorSet, &influx, auditStatus)
assert.Equal(t, 13, issues)
assert.Equal(t, "15", influx.fortify_data.fields.auditAllTotal)
assert.Equal(t, "12", influx.fortify_data.fields.auditAllAudited)
assert.Equal(t, "20", influx.fortify_data.fields.corporateTotal)
assert.Equal(t, "11", influx.fortify_data.fields.corporateAudited)
assert.Equal(t, "13", influx.fortify_data.fields.spotChecksTotal)
assert.Equal(t, "11", influx.fortify_data.fields.spotChecksAudited)
assert.Equal(t, "1", influx.fortify_data.fields.spotChecksGap)
}
func TestTriggerFortifyScan(t *testing.T) {
t.Run("maven", func(t *testing.T) {
dir, err := ioutil.TempDir("", "test trigger fortify scan")
if err != nil {
t.Fatal("Failed to create temporary directory")
}
oldCWD, _ := os.Getwd()
_ = os.Chdir(dir)
// clean up tmp dir
defer func() {
_ = os.Chdir(oldCWD)
_ = os.RemoveAll(dir)
}()
runner := execRunnerMock{}
config := fortifyExecuteScanOptions{BuildTool: "maven", AutodetectClasspath: true, BuildDescriptorFile: "./pom.xml", Memory: "-Xmx4G -Xms2G"}
triggerFortifyScan(config, &runner, "test", "testLabel")
assert.Equal(t, 3, runner.numExecutions)
assert.Equal(t, "mvn", runner.executions[0].executable)
assert.Equal(t, []string{"--file", "./pom.xml", "-Dmdep.outputFile=cp.txt", "-DincludeScope=compile", "-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn", "--batch-mode", "dependency:build-classpath"}, runner.executions[0].parameters)
assert.Equal(t, "sourceanalyzer", runner.executions[1].executable)
assert.Equal(t, []string{"-verbose", "-64", "-b", "test", "-Xmx4G", "-Xms2G", "-cp", "some.jar;someother.jar", "**/*.xml", "**/*.html", "**/*.jsp", "**/*.js", "src/main/resources/**/*", "src/main/java/**/*"}, runner.executions[1].parameters)
assert.Equal(t, "sourceanalyzer", runner.executions[2].executable)
assert.Equal(t, []string{"-verbose", "-64", "-b", "test", "-scan", "-Xmx4G", "-Xms2G", "-build-label", "testLabel", "-logfile", "target/fortify-scan.log", "-f", "target/result.fpr"}, runner.executions[2].parameters)
})
t.Run("pip", func(t *testing.T) {
dir, err := ioutil.TempDir("", "test trigger fortify scan")
if err != nil {
t.Fatal("Failed to create temporary directory")
}
oldCWD, _ := os.Getwd()
_ = os.Chdir(dir)
// clean up tmp dir
defer func() {
_ = os.Chdir(oldCWD)
_ = os.RemoveAll(dir)
}()
runner := execRunnerMock{}
config := fortifyExecuteScanOptions{BuildTool: "pip", PythonVersion: "python2", AutodetectClasspath: true, BuildDescriptorFile: "./setup.py", PythonRequirementsFile: "./requirements.txt", PythonInstallCommand: "pip2 install --user", Memory: "-Xmx4G -Xms2G"}
triggerFortifyScan(config, &runner, "test", "testLabel")
assert.Equal(t, 5, runner.numExecutions)
assert.Equal(t, "python2", runner.executions[0].executable)
assert.Equal(t, []string{"-c", "import sys;p=sys.path;p.remove('');print(';'.join(p))"}, runner.executions[0].parameters)
assert.Equal(t, "pip2", runner.executions[1].executable)
assert.Equal(t, []string{"install", "--user", "-r", "./requirements.txt", ""}, runner.executions[1].parameters)
assert.Equal(t, "pip2", runner.executions[2].executable)
assert.Equal(t, []string{"install", "--user"}, runner.executions[2].parameters)
assert.Equal(t, "sourceanalyzer", runner.executions[3].executable)
assert.Equal(t, []string{"-verbose", "-64", "-b", "test", "-Xmx4G", "-Xms2G", "-python-path", "/usr/lib/python35.zip;/usr/lib/python3.5;/usr/lib/python3.5/plat-x86_64-linux-gnu;/usr/lib/python3.5/lib-dynload;/home/piper/.local/lib/python3.5/site-packages;/usr/local/lib/python3.5/dist-packages;/usr/lib/python3/dist-packages;./lib", ""}, runner.executions[3].parameters)
assert.Equal(t, "sourceanalyzer", runner.executions[4].executable)
assert.Equal(t, []string{"-verbose", "-64", "-b", "test", "-scan", "-Xmx4G", "-Xms2G", "-build-label", "testLabel", "-logfile", "target/fortify-scan.log", "-f", "target/result.fpr"}, runner.executions[4].parameters)
})
}
func TestGenerateAndDownloadQGateReport(t *testing.T) {
ffMock := fortifyMock{Successive: false}
config := fortifyExecuteScanOptions{ReportTemplateID: 18, ReportType: "PDF"}
name := "test"
projectVersion := models.ProjectVersion{ID: 4711, Name: &name}
project := models.Project{ID: 815, Name: &name}
projectVersion.Project = &project
t.Run("success", func(t *testing.T) {
data, err := generateAndDownloadQGateReport(config, &ffMock, &project, &projectVersion)
assert.NoError(t, err)
assert.Equal(t, []byte("abcd"), data)
})
}
func TestVerifyScanResultsFinishedUploading(t *testing.T) {
ffMock := fortifyMock{Successive: false}
t.Run("error no recent upload detected", func(t *testing.T) {
config := fortifyExecuteScanOptions{DeltaMinutes: -1}
err := verifyScanResultsFinishedUploading(config, &ffMock, 4711, "", &models.FilterSet{}, 0)
assert.EqualError(t, err, "No recent upload detected on Project Version")
})
config := fortifyExecuteScanOptions{DeltaMinutes: 20}
t.Run("success", func(t *testing.T) {
err := verifyScanResultsFinishedUploading(config, &ffMock, 4711, "", &models.FilterSet{}, 0)
assert.NoError(t, err)
})
t.Run("error processing", func(t *testing.T) {
err := verifyScanResultsFinishedUploading(config, &ffMock, 4712, "", &models.FilterSet{}, 0)
assert.EqualError(t, err, "There are artifacts that failed processing for Project Version 4712\n/html/ssc/index.jsp#!/version/4712/artifacts?filterSet=")
})
t.Run("error required auth", func(t *testing.T) {
err := verifyScanResultsFinishedUploading(config, &ffMock, 4713, "", &models.FilterSet{}, 0)
assert.EqualError(t, err, "There are artifacts that require manual approval for Project Version 4713\n/html/ssc/index.jsp#!/version/4713/artifacts?filterSet=")
})
t.Run("error polling timeout", func(t *testing.T) {
err := verifyScanResultsFinishedUploading(config, &ffMock, 4714, "", &models.FilterSet{}, 1)
assert.EqualError(t, err, "Terminating after 0 minutes since artifact for Project Version 4714 is still in status PROCESSING")
})
t.Run("success build label", func(t *testing.T) {
err := verifyScanResultsFinishedUploading(config, &ffMock, 4715, "/commit/test", &models.FilterSet{}, 0)
assert.NoError(t, err)
})
t.Run("error no artifacts", func(t *testing.T) {
err := verifyScanResultsFinishedUploading(config, &ffMock, 4716, "", &models.FilterSet{}, 0)
assert.EqualError(t, err, "No uploaded artifacts for assessment detected for project version with ID 4716")
})
}
func TestCalculateTimeDifferenceToLastUpload(t *testing.T) {
diffSeconds := calculateTimeDifferenceToLastUpload(models.Iso8601MilliDateTime(time.Now().UTC()), 1234)
assert.Equal(t, true, diffSeconds < 1)
}
func TestExecuteTemplatedCommand(t *testing.T) {
runner := execRunnerMock{}
template := []string{"{{.Executable}}", "-c", "{{.Param}}"}
context := map[string]string{"Executable": "test.cmd", "Param": "abcd"}
executeTemplatedCommand(&runner, template, context)
assert.Equal(t, "test.cmd", runner.executions[0].executable)
assert.Equal(t, []string{"-c", "abcd"}, runner.executions[0].parameters)
}
func TestDeterminePullRequestMerge(t *testing.T) {
config := fortifyExecuteScanOptions{CommitMessage: "Merge pull request #2462 from branch f-test", PullRequestMessageRegex: `(?m).*Merge pull request #(\d+) from.*`, PullRequestMessageRegexGroup: 1}
t.Run("success", func(t *testing.T) {
match := determinePullRequestMerge(config)
assert.Equal(t, "2462", match, "Expected different result")
})
t.Run("no match", func(t *testing.T) {
config.CommitMessage = "Some test commit"
match := determinePullRequestMerge(config)
assert.Equal(t, "", match, "Expected different result")
})
}
func TestDeterminePullRequestMergeGithub(t *testing.T) {
prServiceMock := pullRequestServiceMock{}
t.Run("success", func(t *testing.T) {
match, err := determinePullRequestMergeGithub(nil, fortifyExecuteScanOptions{Owner: "A"}, prServiceMock)
assert.NoError(t, err)
assert.Equal(t, "17", match, "Expected different result")
})
t.Run("no match", func(t *testing.T) {
match, err := determinePullRequestMergeGithub(nil, fortifyExecuteScanOptions{Owner: "B"}, prServiceMock)
assert.NoError(t, err)
assert.Equal(t, "", match, "Expected different result")
})
t.Run("error", func(t *testing.T) {
match, err := determinePullRequestMergeGithub(nil, fortifyExecuteScanOptions{Owner: "C"}, prServiceMock)
assert.EqualError(t, err, "Test error")
assert.Equal(t, "", match, "Expected different result")
})
}
func TestTranslateProject(t *testing.T) {
t.Run("python", func(t *testing.T) {
execRunner := execRunnerMock{}
config := fortifyExecuteScanOptions{BuildTool: "pip", Memory: "-Xmx4G", Translate: `[{"pythonPath":"./some/path","pythonIncludes":"./**/*","pythonExcludes":"./tests/**/*"}]`}
translateProject(&config, &execRunner, "/commit/7267658798797", "")
assert.Equal(t, "sourceanalyzer", execRunner.executions[0].executable, "Expected different executable")
assert.Equal(t, []string{"-verbose", "-64", "-b", "/commit/7267658798797", "-Xmx4G", "-python-path", "./some/path", "-exclude", "./tests/**/*", "./**/*"}, execRunner.executions[0].parameters, "Expected different parameters")
})
t.Run("asp", func(t *testing.T) {
execRunner := execRunnerMock{}
config := fortifyExecuteScanOptions{BuildTool: "windows", Memory: "-Xmx6G", Translate: `[{"aspnetcore":"true","dotNetCoreVersion":"3.5","exclude":"./tests/**/*","libDirs":"tmp/","src":"./**/*"}]`}
translateProject(&config, &execRunner, "/commit/7267658798797", "")
assert.Equal(t, "sourceanalyzer", execRunner.executions[0].executable, "Expected different executable")
assert.Equal(t, []string{"-verbose", "-64", "-b", "/commit/7267658798797", "-Xmx6G", "-aspnetcore", "-dotnet-core-version", "3.5", "-exclude", "./tests/**/*", "-libdirs", "tmp/", "./**/*"}, execRunner.executions[0].parameters, "Expected different parameters")
})
t.Run("java", func(t *testing.T) {
execRunner := execRunnerMock{}
config := fortifyExecuteScanOptions{BuildTool: "maven", Memory: "-Xmx2G", Translate: `[{"classpath":"./classes/*.jar","extdirs":"tmp/","jdk":"1.8.0-21","source":"1.8","sourcepath":"src/ext/","src":"./**/*"}]`}
translateProject(&config, &execRunner, "/commit/7267658798797", "")
assert.Equal(t, "sourceanalyzer", execRunner.executions[0].executable, "Expected different executable")
assert.Equal(t, []string{"-verbose", "-64", "-b", "/commit/7267658798797", "-Xmx2G", "-cp", "./classes/*.jar", "-extdirs", "tmp/", "-source", "1.8", "-jdk", "1.8.0-21", "-sourcepath", "src/ext/", "./**/*"}, execRunner.executions[0].parameters, "Expected different parameters")
})
t.Run("auto classpath", func(t *testing.T) {
execRunner := execRunnerMock{}
config := fortifyExecuteScanOptions{BuildTool: "maven", Memory: "-Xmx2G", Translate: `[{"classpath":"./classes/*.jar", "extdirs":"tmp/","jdk":"1.8.0-21","source":"1.8","sourcepath":"src/ext/","src":"./**/*"}]`}
translateProject(&config, &execRunner, "/commit/7267658798797", "./WEB-INF/lib/*.jar")
assert.Equal(t, "sourceanalyzer", execRunner.executions[0].executable, "Expected different executable")
assert.Equal(t, []string{"-verbose", "-64", "-b", "/commit/7267658798797", "-Xmx2G", "-cp", "./WEB-INF/lib/*.jar", "-extdirs", "tmp/", "-source", "1.8", "-jdk", "1.8.0-21", "-sourcepath", "src/ext/", "./**/*"}, execRunner.executions[0].parameters, "Expected different parameters")
})
}
func TestScanProject(t *testing.T) {
config := fortifyExecuteScanOptions{Memory: "-Xmx4G"}
t.Run("normal", func(t *testing.T) {
execRunner := execRunnerMock{}
scanProject(&config, &execRunner, "/commit/7267658798797", "label")
assert.Equal(t, "sourceanalyzer", execRunner.executions[0].executable, "Expected different executable")
assert.Equal(t, []string{"-verbose", "-64", "-b", "/commit/7267658798797", "-scan", "-Xmx4G", "-build-label", "label", "-logfile", "target/fortify-scan.log", "-f", "target/result.fpr"}, execRunner.executions[0].parameters, "Expected different parameters")
})
t.Run("quick", func(t *testing.T) {
execRunner := execRunnerMock{}
config.QuickScan = true
scanProject(&config, &execRunner, "/commit/7267658798797", "")
assert.Equal(t, "sourceanalyzer", execRunner.executions[0].executable, "Expected different executable")
assert.Equal(t, []string{"-verbose", "-64", "-b", "/commit/7267658798797", "-scan", "-Xmx4G", "-quick", "-logfile", "target/fortify-scan.log", "-f", "target/result.fpr"}, execRunner.executions[0].parameters, "Expected different parameters")
})
}
func TestAutoresolveClasspath(t *testing.T) {
t.Run("success pip", func(t *testing.T) {
execRunner := execRunnerMock{}
dir, err := ioutil.TempDir("", "classpath")
assert.NoError(t, err, "Unexpected error detected")
defer os.RemoveAll(dir)
file := filepath.Join(dir, "cp.txt")
result := autoresolvePipClasspath("python2", []string{"-c", "import sys;p=sys.path;p.remove('');print(';'.join(p))"}, file, &execRunner)
assert.Equal(t, "python2", execRunner.executions[0].executable, "Expected different executable")
assert.Equal(t, []string{"-c", "import sys;p=sys.path;p.remove('');print(';'.join(p))"}, execRunner.executions[0].parameters, "Expected different parameters")
assert.Equal(t, "/usr/lib/python35.zip;/usr/lib/python3.5;/usr/lib/python3.5/plat-x86_64-linux-gnu;/usr/lib/python3.5/lib-dynload;/home/piper/.local/lib/python3.5/site-packages;/usr/local/lib/python3.5/dist-packages;/usr/lib/python3/dist-packages;./lib", result, "Expected different result")
})
t.Run("success maven", func(t *testing.T) {
execRunner := execRunnerMock{}
dir, err := ioutil.TempDir("", "classpath")
assert.NoError(t, err, "Unexpected error detected")
defer os.RemoveAll(dir)
file := filepath.Join(dir, "cp.txt")
result := autoresolveMavenClasspath("pom.xml", file, &execRunner)
assert.Equal(t, "mvn", execRunner.executions[0].executable, "Expected different executable")
assert.Equal(t, []string{"--file", "pom.xml", fmt.Sprintf("-Dmdep.outputFile=%v", file), "-DincludeScope=compile", "-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn", "--batch-mode", "dependency:build-classpath"}, execRunner.executions[0].parameters, "Expected different parameters")
assert.Equal(t, "some.jar;someother.jar", result, "Expected different result")
})
}

View File

@ -89,7 +89,7 @@ func addGctsCreateRepositoryFlags(cmd *cobra.Command, stepConfig *gctsCreateRepo
cmd.Flags().StringVar(&stepConfig.RemoteRepositoryURL, "remoteRepositoryURL", os.Getenv("PIPER_remoteRepositoryURL"), "URL of the corresponding remote repository") cmd.Flags().StringVar(&stepConfig.RemoteRepositoryURL, "remoteRepositoryURL", os.Getenv("PIPER_remoteRepositoryURL"), "URL of the corresponding remote repository")
cmd.Flags().StringVar(&stepConfig.Role, "role", os.Getenv("PIPER_role"), "Role of the local repository. Choose between 'TARGET' and 'SOURCE'. Local repositories with a TARGET role will NOT be able to be the source of code changes") cmd.Flags().StringVar(&stepConfig.Role, "role", os.Getenv("PIPER_role"), "Role of the local repository. Choose between 'TARGET' and 'SOURCE'. Local repositories with a TARGET role will NOT be able to be the source of code changes")
cmd.Flags().StringVar(&stepConfig.VSID, "vSID", os.Getenv("PIPER_vSID"), "Virtual SID of the local repository. The vSID corresponds to the transport route that delivers content to the remote Git repository") cmd.Flags().StringVar(&stepConfig.VSID, "vSID", os.Getenv("PIPER_vSID"), "Virtual SID of the local repository. The vSID corresponds to the transport route that delivers content to the remote Git repository")
cmd.Flags().StringVar(&stepConfig.Type, "type", "GIT", "Type of the used source code management tool") cmd.Flags().StringVar(&stepConfig.Type, "type", `GIT`, "Type of the used source code management tool")
cmd.MarkFlagRequired("username") cmd.MarkFlagRequired("username")
cmd.MarkFlagRequired("password") cmd.MarkFlagRequired("password")

View File

@ -87,11 +87,11 @@ func addGithubCreatePullRequestFlags(cmd *cobra.Command, stepConfig *githubCreat
cmd.Flags().StringSliceVar(&stepConfig.Assignees, "assignees", []string{}, "Login names of users to which the PR should be assigned to.") cmd.Flags().StringSliceVar(&stepConfig.Assignees, "assignees", []string{}, "Login names of users to which the PR should be assigned to.")
cmd.Flags().StringVar(&stepConfig.Base, "base", os.Getenv("PIPER_base"), "The name of the branch you want the changes pulled into.") cmd.Flags().StringVar(&stepConfig.Base, "base", os.Getenv("PIPER_base"), "The name of the branch you want the changes pulled into.")
cmd.Flags().StringVar(&stepConfig.Body, "body", os.Getenv("PIPER_body"), "The description text of the pull request in markdown format.") cmd.Flags().StringVar(&stepConfig.Body, "body", os.Getenv("PIPER_body"), "The description text of the pull request in markdown format.")
cmd.Flags().StringVar(&stepConfig.APIURL, "apiUrl", "https://api.github.com", "Set the GitHub API url.") cmd.Flags().StringVar(&stepConfig.APIURL, "apiUrl", `https://api.github.com`, "Set the GitHub API url.")
cmd.Flags().StringVar(&stepConfig.Head, "head", os.Getenv("PIPER_head"), "The name of the branch where your changes are implemented.") cmd.Flags().StringVar(&stepConfig.Head, "head", os.Getenv("PIPER_head"), "The name of the branch where your changes are implemented.")
cmd.Flags().StringVar(&stepConfig.Owner, "owner", os.Getenv("PIPER_owner"), "Set the GitHub organization.") cmd.Flags().StringVar(&stepConfig.Owner, "owner", os.Getenv("PIPER_owner"), "Set the GitHub organization.")
cmd.Flags().StringVar(&stepConfig.Repository, "repository", os.Getenv("PIPER_repository"), "Set the GitHub repository.") cmd.Flags().StringVar(&stepConfig.Repository, "repository", os.Getenv("PIPER_repository"), "Set the GitHub repository.")
cmd.Flags().StringVar(&stepConfig.ServerURL, "serverUrl", "https://github.com", "GitHub server url for end-user access.") cmd.Flags().StringVar(&stepConfig.ServerURL, "serverUrl", `https://github.com`, "GitHub server url for end-user access.")
cmd.Flags().StringVar(&stepConfig.Title, "title", os.Getenv("PIPER_title"), "Title of the pull request.") cmd.Flags().StringVar(&stepConfig.Title, "title", os.Getenv("PIPER_title"), "Title of the pull request.")
cmd.Flags().StringVar(&stepConfig.Token, "token", os.Getenv("PIPER_token"), "GitHub personal access token as per https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line") cmd.Flags().StringVar(&stepConfig.Token, "token", os.Getenv("PIPER_token"), "GitHub personal access token as per https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line")
cmd.Flags().StringSliceVar(&stepConfig.Labels, "labels", []string{}, "Labels to be added to the pull request.") cmd.Flags().StringSliceVar(&stepConfig.Labels, "labels", []string{}, "Labels to be added to the pull request.")

View File

@ -96,17 +96,17 @@ The result looks like
func addGithubPublishReleaseFlags(cmd *cobra.Command, stepConfig *githubPublishReleaseOptions) { func addGithubPublishReleaseFlags(cmd *cobra.Command, stepConfig *githubPublishReleaseOptions) {
cmd.Flags().BoolVar(&stepConfig.AddClosedIssues, "addClosedIssues", false, "If set to `true`, closed issues and merged pull-requests since the last release will added below the `releaseBodyHeader`") cmd.Flags().BoolVar(&stepConfig.AddClosedIssues, "addClosedIssues", false, "If set to `true`, closed issues and merged pull-requests since the last release will added below the `releaseBodyHeader`")
cmd.Flags().BoolVar(&stepConfig.AddDeltaToLastRelease, "addDeltaToLastRelease", false, "If set to `true`, a link will be added to the relese information that brings up all commits since the last release.") cmd.Flags().BoolVar(&stepConfig.AddDeltaToLastRelease, "addDeltaToLastRelease", false, "If set to `true`, a link will be added to the relese information that brings up all commits since the last release.")
cmd.Flags().StringVar(&stepConfig.APIURL, "apiUrl", "https://api.github.com", "Set the GitHub API url.") cmd.Flags().StringVar(&stepConfig.APIURL, "apiUrl", `https://api.github.com`, "Set the GitHub API url.")
cmd.Flags().StringVar(&stepConfig.AssetPath, "assetPath", os.Getenv("PIPER_assetPath"), "Path to a release asset which should be uploaded to the list of release assets.") cmd.Flags().StringVar(&stepConfig.AssetPath, "assetPath", os.Getenv("PIPER_assetPath"), "Path to a release asset which should be uploaded to the list of release assets.")
cmd.Flags().StringVar(&stepConfig.Commitish, "commitish", "master", "Target git commitish for the release") cmd.Flags().StringVar(&stepConfig.Commitish, "commitish", `master`, "Target git commitish for the release")
cmd.Flags().StringSliceVar(&stepConfig.ExcludeLabels, "excludeLabels", []string{}, "Allows to exclude issues with dedicated list of labels.") cmd.Flags().StringSliceVar(&stepConfig.ExcludeLabels, "excludeLabels", []string{}, "Allows to exclude issues with dedicated list of labels.")
cmd.Flags().StringSliceVar(&stepConfig.Labels, "labels", []string{}, "Labels to include in issue search.") cmd.Flags().StringSliceVar(&stepConfig.Labels, "labels", []string{}, "Labels to include in issue search.")
cmd.Flags().StringVar(&stepConfig.Owner, "owner", os.Getenv("PIPER_owner"), "Set the GitHub organization.") cmd.Flags().StringVar(&stepConfig.Owner, "owner", os.Getenv("PIPER_owner"), "Set the GitHub organization.")
cmd.Flags().StringVar(&stepConfig.ReleaseBodyHeader, "releaseBodyHeader", os.Getenv("PIPER_releaseBodyHeader"), "Content which will appear for the release.") cmd.Flags().StringVar(&stepConfig.ReleaseBodyHeader, "releaseBodyHeader", os.Getenv("PIPER_releaseBodyHeader"), "Content which will appear for the release.")
cmd.Flags().StringVar(&stepConfig.Repository, "repository", os.Getenv("PIPER_repository"), "Set the GitHub repository.") cmd.Flags().StringVar(&stepConfig.Repository, "repository", os.Getenv("PIPER_repository"), "Set the GitHub repository.")
cmd.Flags().StringVar(&stepConfig.ServerURL, "serverUrl", "https://github.com", "GitHub server url for end-user access.") cmd.Flags().StringVar(&stepConfig.ServerURL, "serverUrl", `https://github.com`, "GitHub server url for end-user access.")
cmd.Flags().StringVar(&stepConfig.Token, "token", os.Getenv("PIPER_token"), "GitHub personal access token as per https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line") cmd.Flags().StringVar(&stepConfig.Token, "token", os.Getenv("PIPER_token"), "GitHub personal access token as per https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line")
cmd.Flags().StringVar(&stepConfig.UploadURL, "uploadUrl", "https://uploads.github.com", "Set the GitHub API url.") cmd.Flags().StringVar(&stepConfig.UploadURL, "uploadUrl", `https://uploads.github.com`, "Set the GitHub API url.")
cmd.Flags().StringVar(&stepConfig.Version, "version", os.Getenv("PIPER_version"), "Define the version number which will be written as tag as well as release name.") cmd.Flags().StringVar(&stepConfig.Version, "version", os.Getenv("PIPER_version"), "Define the version number which will be written as tag as well as release name.")
cmd.MarkFlagRequired("apiUrl") cmd.MarkFlagRequired("apiUrl")

View File

@ -83,9 +83,9 @@ In the Docker network, the containers can be referenced by the values provided i
} }
func addKarmaExecuteTestsFlags(cmd *cobra.Command, stepConfig *karmaExecuteTestsOptions) { func addKarmaExecuteTestsFlags(cmd *cobra.Command, stepConfig *karmaExecuteTestsOptions) {
cmd.Flags().StringVar(&stepConfig.InstallCommand, "installCommand", "npm install --quiet", "The command that is executed to install the test tool.") cmd.Flags().StringVar(&stepConfig.InstallCommand, "installCommand", `npm install --quiet`, "The command that is executed to install the test tool.")
cmd.Flags().StringVar(&stepConfig.ModulePath, "modulePath", ".", "Define the path of the module to execute tests on.") cmd.Flags().StringVar(&stepConfig.ModulePath, "modulePath", `.`, "Define the path of the module to execute tests on.")
cmd.Flags().StringVar(&stepConfig.RunCommand, "runCommand", "npm run karma", "The command that is executed to start the tests.") cmd.Flags().StringVar(&stepConfig.RunCommand, "runCommand", `npm run karma`, "The command that is executed to start the tests.")
cmd.MarkFlagRequired("installCommand") cmd.MarkFlagRequired("installCommand")
cmd.MarkFlagRequired("modulePath") cmd.MarkFlagRequired("modulePath")

View File

@ -117,17 +117,17 @@ func addKubernetesDeployFlags(cmd *cobra.Command, stepConfig *kubernetesDeployOp
cmd.Flags().StringVar(&stepConfig.ContainerRegistryPassword, "containerRegistryPassword", os.Getenv("PIPER_containerRegistryPassword"), "Password for container registry access - typically provided by the CI/CD environment.") cmd.Flags().StringVar(&stepConfig.ContainerRegistryPassword, "containerRegistryPassword", os.Getenv("PIPER_containerRegistryPassword"), "Password for container registry access - typically provided by the CI/CD environment.")
cmd.Flags().StringVar(&stepConfig.ContainerRegistryURL, "containerRegistryUrl", os.Getenv("PIPER_containerRegistryUrl"), "http(s) url of the Container registry.") cmd.Flags().StringVar(&stepConfig.ContainerRegistryURL, "containerRegistryUrl", os.Getenv("PIPER_containerRegistryUrl"), "http(s) url of the Container registry.")
cmd.Flags().StringVar(&stepConfig.ContainerRegistryUser, "containerRegistryUser", os.Getenv("PIPER_containerRegistryUser"), "Username for container registry access - typically provided by the CI/CD environment.") cmd.Flags().StringVar(&stepConfig.ContainerRegistryUser, "containerRegistryUser", os.Getenv("PIPER_containerRegistryUser"), "Username for container registry access - typically provided by the CI/CD environment.")
cmd.Flags().StringVar(&stepConfig.ContainerRegistrySecret, "containerRegistrySecret", "regsecret", "Name of the container registry secret used for pulling containers from the registry.") cmd.Flags().StringVar(&stepConfig.ContainerRegistrySecret, "containerRegistrySecret", `regsecret`, "Name of the container registry secret used for pulling containers from the registry.")
cmd.Flags().BoolVar(&stepConfig.CreateDockerRegistrySecret, "createDockerRegistrySecret", false, "Toggle to turn on Regsecret creation with a \"deployTool:kubectl\" deployment.") cmd.Flags().BoolVar(&stepConfig.CreateDockerRegistrySecret, "createDockerRegistrySecret", false, "Toggle to turn on Regsecret creation with a \"deployTool:kubectl\" deployment.")
cmd.Flags().StringVar(&stepConfig.DeploymentName, "deploymentName", os.Getenv("PIPER_deploymentName"), "Defines the name of the deployment.") cmd.Flags().StringVar(&stepConfig.DeploymentName, "deploymentName", os.Getenv("PIPER_deploymentName"), "Defines the name of the deployment.")
cmd.Flags().StringVar(&stepConfig.DeployTool, "deployTool", "kubectl", "Defines the tool which should be used for deployment.") cmd.Flags().StringVar(&stepConfig.DeployTool, "deployTool", `kubectl`, "Defines the tool which should be used for deployment.")
cmd.Flags().IntVar(&stepConfig.HelmDeployWaitSeconds, "helmDeployWaitSeconds", 300, "Number of seconds before helm deploy returns.") cmd.Flags().IntVar(&stepConfig.HelmDeployWaitSeconds, "helmDeployWaitSeconds", 300, "Number of seconds before helm deploy returns.")
cmd.Flags().StringVar(&stepConfig.Image, "image", os.Getenv("PIPER_image"), "Full name of the image to be deployed.") cmd.Flags().StringVar(&stepConfig.Image, "image", os.Getenv("PIPER_image"), "Full name of the image to be deployed.")
cmd.Flags().StringSliceVar(&stepConfig.IngressHosts, "ingressHosts", []string{}, "List of ingress hosts to be exposed via helm deployment.") cmd.Flags().StringSliceVar(&stepConfig.IngressHosts, "ingressHosts", []string{}, "List of ingress hosts to be exposed via helm deployment.")
cmd.Flags().StringVar(&stepConfig.KubeConfig, "kubeConfig", os.Getenv("PIPER_kubeConfig"), "Defines the path to the \"kubeconfig\" file.") cmd.Flags().StringVar(&stepConfig.KubeConfig, "kubeConfig", os.Getenv("PIPER_kubeConfig"), "Defines the path to the \"kubeconfig\" file.")
cmd.Flags().StringVar(&stepConfig.KubeContext, "kubeContext", os.Getenv("PIPER_kubeContext"), "Defines the context to use from the \"kubeconfig\" file.") cmd.Flags().StringVar(&stepConfig.KubeContext, "kubeContext", os.Getenv("PIPER_kubeContext"), "Defines the context to use from the \"kubeconfig\" file.")
cmd.Flags().StringVar(&stepConfig.KubeToken, "kubeToken", os.Getenv("PIPER_kubeToken"), "Contains the id_token used by kubectl for authentication. Consider using kubeConfig parameter instead.") cmd.Flags().StringVar(&stepConfig.KubeToken, "kubeToken", os.Getenv("PIPER_kubeToken"), "Contains the id_token used by kubectl for authentication. Consider using kubeConfig parameter instead.")
cmd.Flags().StringVar(&stepConfig.Namespace, "namespace", "default", "Defines the target Kubernetes namespace for the deployment.") cmd.Flags().StringVar(&stepConfig.Namespace, "namespace", `default`, "Defines the target Kubernetes namespace for the deployment.")
cmd.Flags().StringVar(&stepConfig.TillerNamespace, "tillerNamespace", os.Getenv("PIPER_tillerNamespace"), "Defines optional tiller namespace for deployments using helm.") cmd.Flags().StringVar(&stepConfig.TillerNamespace, "tillerNamespace", os.Getenv("PIPER_tillerNamespace"), "Defines optional tiller namespace for deployments using helm.")
cmd.MarkFlagRequired("chartPath") cmd.MarkFlagRequired("chartPath")

View File

@ -79,7 +79,7 @@ supports ci friendly versioning by flattening the pom before installing.`,
} }
func addMavenBuildFlags(cmd *cobra.Command, stepConfig *mavenBuildOptions) { func addMavenBuildFlags(cmd *cobra.Command, stepConfig *mavenBuildOptions) {
cmd.Flags().StringVar(&stepConfig.PomPath, "pomPath", "pom.xml", "Path to the pom file which should be installed including all children.") cmd.Flags().StringVar(&stepConfig.PomPath, "pomPath", `pom.xml`, "Path to the pom file which should be installed including all children.")
cmd.Flags().BoolVar(&stepConfig.Flatten, "flatten", true, "Defines if the pom files should be flattened to support ci friendly maven versioning.") cmd.Flags().BoolVar(&stepConfig.Flatten, "flatten", true, "Defines if the pom files should be flattened to support ci friendly maven versioning.")
cmd.Flags().BoolVar(&stepConfig.Verify, "verify", false, "Instead of installing the artifact only the verify lifecycle phase is executed.") cmd.Flags().BoolVar(&stepConfig.Verify, "verify", false, "Instead of installing the artifact only the verify lifecycle phase is executed.")
cmd.Flags().StringVar(&stepConfig.ProjectSettingsFile, "projectSettingsFile", os.Getenv("PIPER_projectSettingsFile"), "Path to the mvn settings file that should be used as project settings file.") cmd.Flags().StringVar(&stepConfig.ProjectSettingsFile, "projectSettingsFile", os.Getenv("PIPER_projectSettingsFile"), "Path to the mvn settings file that should be used as project settings file.")

View File

@ -111,15 +111,15 @@ func MtaBuildCommand() *cobra.Command {
} }
func addMtaBuildFlags(cmd *cobra.Command, stepConfig *mtaBuildOptions) { func addMtaBuildFlags(cmd *cobra.Command, stepConfig *mtaBuildOptions) {
cmd.Flags().StringVar(&stepConfig.BuildTarget, "buildTarget", "NEO", "mtaBuildTool 'classic' only: The target platform to which the mtar can be deployed. Valid values: 'CF', 'NEO', 'XSA'.") cmd.Flags().StringVar(&stepConfig.BuildTarget, "buildTarget", `NEO`, "mtaBuildTool 'classic' only: The target platform to which the mtar can be deployed. Valid values: 'CF', 'NEO', 'XSA'.")
cmd.Flags().StringVar(&stepConfig.MtaBuildTool, "mtaBuildTool", "cloudMbt", "Tool to use when building the MTA. Valid values: 'classic', 'cloudMbt'.") cmd.Flags().StringVar(&stepConfig.MtaBuildTool, "mtaBuildTool", `cloudMbt`, "Tool to use when building the MTA. Valid values: 'classic', 'cloudMbt'.")
cmd.Flags().StringVar(&stepConfig.MtarName, "mtarName", os.Getenv("PIPER_mtarName"), "The name of the generated mtar file including its extension.") cmd.Flags().StringVar(&stepConfig.MtarName, "mtarName", os.Getenv("PIPER_mtarName"), "The name of the generated mtar file including its extension.")
cmd.Flags().StringVar(&stepConfig.MtaJarLocation, "mtaJarLocation", "/opt/sap/mta/lib/mta.jar", "mtaBuildTool 'classic' only: The location of the SAP Multitarget Application Archive Builder jar file, including file name and extension. If you run on Docker, this must match the location of the jar file in the container as well.") cmd.Flags().StringVar(&stepConfig.MtaJarLocation, "mtaJarLocation", `/opt/sap/mta/lib/mta.jar`, "mtaBuildTool 'classic' only: The location of the SAP Multitarget Application Archive Builder jar file, including file name and extension. If you run on Docker, this must match the location of the jar file in the container as well.")
cmd.Flags().StringVar(&stepConfig.Extensions, "extensions", os.Getenv("PIPER_extensions"), "The path to the extension descriptor file.") cmd.Flags().StringVar(&stepConfig.Extensions, "extensions", os.Getenv("PIPER_extensions"), "The path to the extension descriptor file.")
cmd.Flags().StringVar(&stepConfig.Platform, "platform", "CF", "mtaBuildTool 'cloudMbt' only: The target platform to which the mtar can be deployed.") cmd.Flags().StringVar(&stepConfig.Platform, "platform", `CF`, "mtaBuildTool 'cloudMbt' only: The target platform to which the mtar can be deployed.")
cmd.Flags().StringVar(&stepConfig.ApplicationName, "applicationName", os.Getenv("PIPER_applicationName"), "The name of the application which is being built. If the parameter has been provided and no `mta.yaml` exists, the `mta.yaml` will be automatically generated using this parameter and the information (`name` and `version`) from 'package.json` before the actual build starts.") cmd.Flags().StringVar(&stepConfig.ApplicationName, "applicationName", os.Getenv("PIPER_applicationName"), "The name of the application which is being built. If the parameter has been provided and no `mta.yaml` exists, the `mta.yaml` will be automatically generated using this parameter and the information (`name` and `version`) from 'package.json` before the actual build starts.")
cmd.Flags().StringVar(&stepConfig.DefaultNpmRegistry, "defaultNpmRegistry", os.Getenv("PIPER_defaultNpmRegistry"), "Url to the npm registry that should be used for installing npm dependencies.") cmd.Flags().StringVar(&stepConfig.DefaultNpmRegistry, "defaultNpmRegistry", os.Getenv("PIPER_defaultNpmRegistry"), "Url to the npm registry that should be used for installing npm dependencies.")
cmd.Flags().StringVar(&stepConfig.SapNpmRegistry, "sapNpmRegistry", "https://npm.sap.com", "Url to the sap npm registry that should be used for installing npm dependencies prefixed with @sap.") cmd.Flags().StringVar(&stepConfig.SapNpmRegistry, "sapNpmRegistry", `https://npm.sap.com`, "Url to the sap npm registry that should be used for installing npm dependencies prefixed with @sap.")
cmd.Flags().StringVar(&stepConfig.ProjectSettingsFile, "projectSettingsFile", os.Getenv("PIPER_projectSettingsFile"), "Path or url to the mvn settings file that should be used as project settings file.") cmd.Flags().StringVar(&stepConfig.ProjectSettingsFile, "projectSettingsFile", os.Getenv("PIPER_projectSettingsFile"), "Path or url to the mvn settings file that should be used as project settings file.")
cmd.Flags().StringVar(&stepConfig.GlobalSettingsFile, "globalSettingsFile", os.Getenv("PIPER_globalSettingsFile"), "Path or url to the mvn settings file that should be used as global settings file") cmd.Flags().StringVar(&stepConfig.GlobalSettingsFile, "globalSettingsFile", os.Getenv("PIPER_globalSettingsFile"), "Path or url to the mvn settings file that should be used as global settings file")

View File

@ -99,7 +99,7 @@ If an image for mavenExecute is configured, and npm packages are to be published
} }
func addNexusUploadFlags(cmd *cobra.Command, stepConfig *nexusUploadOptions) { func addNexusUploadFlags(cmd *cobra.Command, stepConfig *nexusUploadOptions) {
cmd.Flags().StringVar(&stepConfig.Version, "version", "nexus3", "The Nexus Repository Manager version. Currently supported are 'nexus2' and 'nexus3'.") cmd.Flags().StringVar(&stepConfig.Version, "version", `nexus3`, "The Nexus Repository Manager version. Currently supported are 'nexus2' and 'nexus3'.")
cmd.Flags().StringVar(&stepConfig.Url, "url", os.Getenv("PIPER_url"), "URL of the nexus. The scheme part of the URL will not be considered, because only http is supported.") cmd.Flags().StringVar(&stepConfig.Url, "url", os.Getenv("PIPER_url"), "URL of the nexus. The scheme part of the URL will not be considered, because only http is supported.")
cmd.Flags().StringVar(&stepConfig.MavenRepository, "mavenRepository", os.Getenv("PIPER_mavenRepository"), "Name of the nexus repository for Maven and MTA deployments. If this is not provided, Maven and MTA deployment is implicitly disabled.") cmd.Flags().StringVar(&stepConfig.MavenRepository, "mavenRepository", os.Getenv("PIPER_mavenRepository"), "Name of the nexus repository for Maven and MTA deployments. If this is not provided, Maven and MTA deployment is implicitly disabled.")
cmd.Flags().StringVar(&stepConfig.NpmRepository, "npmRepository", os.Getenv("PIPER_npmRepository"), "Name of the nexus repository for npm deployments. If this is not provided, npm deployment is implicitly disabled.") cmd.Flags().StringVar(&stepConfig.NpmRepository, "npmRepository", os.Getenv("PIPER_npmRepository"), "Name of the nexus repository for npm deployments. If this is not provided, npm deployment is implicitly disabled.")

View File

@ -77,7 +77,7 @@ func addNpmExecuteScriptsFlags(cmd *cobra.Command, stepConfig *npmExecuteScripts
cmd.Flags().BoolVar(&stepConfig.Install, "install", false, "Run npm install or similar commands depending on the project structure.") cmd.Flags().BoolVar(&stepConfig.Install, "install", false, "Run npm install or similar commands depending on the project structure.")
cmd.Flags().StringSliceVar(&stepConfig.RunScripts, "runScripts", []string{}, "List of additional run scripts to execute from package.json.") cmd.Flags().StringSliceVar(&stepConfig.RunScripts, "runScripts", []string{}, "List of additional run scripts to execute from package.json.")
cmd.Flags().StringVar(&stepConfig.DefaultNpmRegistry, "defaultNpmRegistry", os.Getenv("PIPER_defaultNpmRegistry"), "URL of the npm registry to use. Defaults to https://registry.npmjs.org/") cmd.Flags().StringVar(&stepConfig.DefaultNpmRegistry, "defaultNpmRegistry", os.Getenv("PIPER_defaultNpmRegistry"), "URL of the npm registry to use. Defaults to https://registry.npmjs.org/")
cmd.Flags().StringVar(&stepConfig.SapNpmRegistry, "sapNpmRegistry", "https://npm.sap.com", "The default npm registry URL to be used as the remote mirror for the SAP npm packages.") cmd.Flags().StringVar(&stepConfig.SapNpmRegistry, "sapNpmRegistry", `https://npm.sap.com`, "The default npm registry URL to be used as the remote mirror for the SAP npm packages.")
} }

View File

@ -74,6 +74,7 @@ func Execute() {
rootCmd.AddCommand(CloudFoundryDeleteServiceCommand()) rootCmd.AddCommand(CloudFoundryDeleteServiceCommand())
rootCmd.AddCommand(AbapEnvironmentPullGitRepoCommand()) rootCmd.AddCommand(AbapEnvironmentPullGitRepoCommand())
rootCmd.AddCommand(CheckmarxExecuteScanCommand()) rootCmd.AddCommand(CheckmarxExecuteScanCommand())
rootCmd.AddCommand(FortifyExecuteScanCommand())
rootCmd.AddCommand(MtaBuildCommand()) rootCmd.AddCommand(MtaBuildCommand())
rootCmd.AddCommand(ProtecodeExecuteScanCommand()) rootCmd.AddCommand(ProtecodeExecuteScanCommand())
rootCmd.AddCommand(MavenExecuteCommand()) rootCmd.AddCommand(MavenExecuteCommand())

View File

@ -140,17 +140,17 @@ func ProtecodeExecuteScanCommand() *cobra.Command {
} }
func addProtecodeExecuteScanFlags(cmd *cobra.Command, stepConfig *protecodeExecuteScanOptions) { func addProtecodeExecuteScanFlags(cmd *cobra.Command, stepConfig *protecodeExecuteScanOptions) {
cmd.Flags().StringVar(&stepConfig.ExcludeCVEs, "excludeCVEs", "[]", "DEPRECATED: Do use triaging within the Protecode UI instead") cmd.Flags().StringVar(&stepConfig.ExcludeCVEs, "excludeCVEs", `[]`, "DEPRECATED: Do use triaging within the Protecode UI instead")
cmd.Flags().BoolVar(&stepConfig.FailOnSevereVulnerabilities, "failOnSevereVulnerabilities", true, "Whether to fail the job on severe vulnerabilties or not") cmd.Flags().BoolVar(&stepConfig.FailOnSevereVulnerabilities, "failOnSevereVulnerabilities", true, "Whether to fail the job on severe vulnerabilties or not")
cmd.Flags().StringVar(&stepConfig.ScanImage, "scanImage", os.Getenv("PIPER_scanImage"), "The reference to the docker image to scan with Protecode") cmd.Flags().StringVar(&stepConfig.ScanImage, "scanImage", os.Getenv("PIPER_scanImage"), "The reference to the docker image to scan with Protecode")
cmd.Flags().StringVar(&stepConfig.DockerRegistryURL, "dockerRegistryUrl", os.Getenv("PIPER_dockerRegistryUrl"), "The reference to the docker registry to scan with Protecode") cmd.Flags().StringVar(&stepConfig.DockerRegistryURL, "dockerRegistryUrl", os.Getenv("PIPER_dockerRegistryUrl"), "The reference to the docker registry to scan with Protecode")
cmd.Flags().StringVar(&stepConfig.CleanupMode, "cleanupMode", "binary", "Decides which parts are removed from the Protecode backend after the scan") 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.FilePath, "filePath", os.Getenv("PIPER_filePath"), "The path to the file from local workspace to scan with Protecode")
cmd.Flags().BoolVar(&stepConfig.IncludeLayers, "includeLayers", false, "Flag if the docker layers should be included") cmd.Flags().BoolVar(&stepConfig.IncludeLayers, "includeLayers", false, "Flag if the docker layers should be included")
cmd.Flags().BoolVar(&stepConfig.AddSideBarLink, "addSideBarLink", true, "Whether to create a side bar link pointing to the report produced by Protecode or not") cmd.Flags().BoolVar(&stepConfig.AddSideBarLink, "addSideBarLink", true, "Whether to create a side bar link pointing to the report produced by Protecode or not")
cmd.Flags().StringVar(&stepConfig.TimeoutMinutes, "timeoutMinutes", "60", "The timeout to wait for the scan to finish") 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.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") cmd.Flags().StringVar(&stepConfig.ReportFileName, "reportFileName", `protecode_report.pdf`, "The file name of the report to be created")
cmd.Flags().StringVar(&stepConfig.FetchURL, "fetchUrl", os.Getenv("PIPER_fetchUrl"), "The URL to fetch the file to scan with Protecode which must be accessible via public HTTP GET request") cmd.Flags().StringVar(&stepConfig.FetchURL, "fetchUrl", os.Getenv("PIPER_fetchUrl"), "The URL to fetch the file to scan with Protecode which must be accessible via public HTTP GET request")
cmd.Flags().StringVar(&stepConfig.Group, "group", os.Getenv("PIPER_group"), "The Protecode group ID of your team") cmd.Flags().StringVar(&stepConfig.Group, "group", os.Getenv("PIPER_group"), "The Protecode group ID of your team")
cmd.Flags().BoolVar(&stepConfig.ReuseExisting, "reuseExisting", false, "Whether to reuse an existing product instead of creating a new one") cmd.Flags().BoolVar(&stepConfig.ReuseExisting, "reuseExisting", false, "Whether to reuse an existing product instead of creating a new one")

View File

@ -128,25 +128,25 @@ func SonarExecuteScanCommand() *cobra.Command {
} }
func addSonarExecuteScanFlags(cmd *cobra.Command, stepConfig *sonarExecuteScanOptions) { func addSonarExecuteScanFlags(cmd *cobra.Command, stepConfig *sonarExecuteScanOptions) {
cmd.Flags().StringVar(&stepConfig.Instance, "instance", "SonarCloud", "Jenkins only: The name of the SonarQube instance defined in the Jenkins settings. DEPRECATED: use host parameter instead") cmd.Flags().StringVar(&stepConfig.Instance, "instance", `SonarCloud`, "Jenkins only: The name of the SonarQube instance defined in the Jenkins settings. DEPRECATED: use host parameter instead")
cmd.Flags().StringVar(&stepConfig.Host, "host", os.Getenv("PIPER_host"), "The URL to the Sonar backend.") cmd.Flags().StringVar(&stepConfig.Host, "host", os.Getenv("PIPER_host"), "The URL to the Sonar backend.")
cmd.Flags().StringVar(&stepConfig.Token, "token", os.Getenv("PIPER_token"), "Token used to authenticate with the Sonar Server.") cmd.Flags().StringVar(&stepConfig.Token, "token", os.Getenv("PIPER_token"), "Token used to authenticate with the Sonar Server.")
cmd.Flags().StringVar(&stepConfig.Organization, "organization", os.Getenv("PIPER_organization"), "SonarCloud.io only: Organization that the project will be assigned to in SonarCloud.io.") cmd.Flags().StringVar(&stepConfig.Organization, "organization", os.Getenv("PIPER_organization"), "SonarCloud.io only: Organization that the project will be assigned to in SonarCloud.io.")
cmd.Flags().StringVar(&stepConfig.CustomTLSCertificateLinks, "customTlsCertificateLinks", os.Getenv("PIPER_customTlsCertificateLinks"), "List of comma-separated download links to custom TLS certificates. This is required to ensure trusted connections to instances with custom certificates.") cmd.Flags().StringVar(&stepConfig.CustomTLSCertificateLinks, "customTlsCertificateLinks", os.Getenv("PIPER_customTlsCertificateLinks"), "List of comma-separated download links to custom TLS certificates. This is required to ensure trusted connections to instances with custom certificates.")
cmd.Flags().StringVar(&stepConfig.SonarScannerDownloadURL, "sonarScannerDownloadUrl", "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.3.0.2102-linux.zip", "URL to the sonar-scanner-cli archive.") cmd.Flags().StringVar(&stepConfig.SonarScannerDownloadURL, "sonarScannerDownloadUrl", `https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.3.0.2102-linux.zip`, "URL to the sonar-scanner-cli archive.")
cmd.Flags().StringVar(&stepConfig.ProjectVersion, "projectVersion", os.Getenv("PIPER_projectVersion"), "The project version that is reported to SonarQube.") cmd.Flags().StringVar(&stepConfig.ProjectVersion, "projectVersion", os.Getenv("PIPER_projectVersion"), "The project version that is reported to SonarQube.")
cmd.Flags().StringSliceVar(&stepConfig.Options, "options", []string{}, "A list of options which are passed to the sonar-scanner.") cmd.Flags().StringSliceVar(&stepConfig.Options, "options", []string{}, "A list of options which are passed to the sonar-scanner.")
cmd.Flags().StringVar(&stepConfig.BranchName, "branchName", os.Getenv("PIPER_branchName"), "Non-Pull-Request only: Name of the SonarQube branch that should be used to report findings to.") cmd.Flags().StringVar(&stepConfig.BranchName, "branchName", os.Getenv("PIPER_branchName"), "Non-Pull-Request only: Name of the SonarQube branch that should be used to report findings to.")
cmd.Flags().StringVar(&stepConfig.ChangeID, "changeId", os.Getenv("PIPER_changeId"), "Pull-Request only: The id of the pull-request.") cmd.Flags().StringVar(&stepConfig.ChangeID, "changeId", os.Getenv("PIPER_changeId"), "Pull-Request only: The id of the pull-request.")
cmd.Flags().StringVar(&stepConfig.ChangeBranch, "changeBranch", os.Getenv("PIPER_changeBranch"), "Pull-Request only: The name of the pull-request branch.") cmd.Flags().StringVar(&stepConfig.ChangeBranch, "changeBranch", os.Getenv("PIPER_changeBranch"), "Pull-Request only: The name of the pull-request branch.")
cmd.Flags().StringVar(&stepConfig.ChangeTarget, "changeTarget", os.Getenv("PIPER_changeTarget"), "Pull-Request only: The name of the base branch.") cmd.Flags().StringVar(&stepConfig.ChangeTarget, "changeTarget", os.Getenv("PIPER_changeTarget"), "Pull-Request only: The name of the base branch.")
cmd.Flags().StringVar(&stepConfig.PullRequestProvider, "pullRequestProvider", "GitHub", "Pull-Request only: The scm provider.") cmd.Flags().StringVar(&stepConfig.PullRequestProvider, "pullRequestProvider", `GitHub`, "Pull-Request only: The scm provider.")
cmd.Flags().StringVar(&stepConfig.Owner, "owner", os.Getenv("PIPER_owner"), "Pull-Request only: The owner of the scm repository.") cmd.Flags().StringVar(&stepConfig.Owner, "owner", os.Getenv("PIPER_owner"), "Pull-Request only: The owner of the scm repository.")
cmd.Flags().StringVar(&stepConfig.Repository, "repository", os.Getenv("PIPER_repository"), "Pull-Request only: The scm repository.") cmd.Flags().StringVar(&stepConfig.Repository, "repository", os.Getenv("PIPER_repository"), "Pull-Request only: The scm repository.")
cmd.Flags().StringVar(&stepConfig.GithubToken, "githubToken", os.Getenv("PIPER_githubToken"), "Pull-Request only: Token for Github to set status on the Pull-Request.") cmd.Flags().StringVar(&stepConfig.GithubToken, "githubToken", os.Getenv("PIPER_githubToken"), "Pull-Request only: Token for Github to set status on the Pull-Request.")
cmd.Flags().BoolVar(&stepConfig.DisableInlineComments, "disableInlineComments", false, "Pull-Request only: Disables the pull-request decoration with inline comments. DEPRECATED: only supported in SonarQube < 7.2") cmd.Flags().BoolVar(&stepConfig.DisableInlineComments, "disableInlineComments", false, "Pull-Request only: Disables the pull-request decoration with inline comments. DEPRECATED: only supported in SonarQube < 7.2")
cmd.Flags().BoolVar(&stepConfig.LegacyPRHandling, "legacyPRHandling", false, "Pull-Request only: Activates the pull-request handling using the [GitHub Plugin](https://docs.sonarqube.org/display/PLUG/GitHub+Plugin). DEPRECATED: only supported in SonarQube < 7.2") cmd.Flags().BoolVar(&stepConfig.LegacyPRHandling, "legacyPRHandling", false, "Pull-Request only: Activates the pull-request handling using the [GitHub Plugin](https://docs.sonarqube.org/display/PLUG/GitHub+Plugin). DEPRECATED: only supported in SonarQube < 7.2")
cmd.Flags().StringVar(&stepConfig.GithubAPIURL, "githubApiUrl", "https://api.github.com", "Pull-Request only: The URL to the Github API. see [GitHub plugin docs](https://docs.sonarqube.org/display/PLUG/GitHub+Plugin#GitHubPlugin-Usage) DEPRECATED: only supported in SonarQube < 7.2") cmd.Flags().StringVar(&stepConfig.GithubAPIURL, "githubApiUrl", `https://api.github.com`, "Pull-Request only: The URL to the Github API. see [GitHub plugin docs](https://docs.sonarqube.org/display/PLUG/GitHub+Plugin#GitHubPlugin-Usage) DEPRECATED: only supported in SonarQube < 7.2")
} }

View File

@ -116,10 +116,10 @@ func XsDeployCommand() *cobra.Command {
func addXsDeployFlags(cmd *cobra.Command, stepConfig *xsDeployOptions) { func addXsDeployFlags(cmd *cobra.Command, stepConfig *xsDeployOptions) {
cmd.Flags().StringVar(&stepConfig.DeployOpts, "deployOpts", os.Getenv("PIPER_deployOpts"), "Additional options appended to the deploy command. Only needed for sophisticated cases. When provided it is the duty of the provider to ensure proper quoting / escaping.") cmd.Flags().StringVar(&stepConfig.DeployOpts, "deployOpts", os.Getenv("PIPER_deployOpts"), "Additional options appended to the deploy command. Only needed for sophisticated cases. When provided it is the duty of the provider to ensure proper quoting / escaping.")
cmd.Flags().StringVar(&stepConfig.OperationIDLogPattern, "operationIdLogPattern", "^.*xs bg-deploy -i (.*) -a.*$", "Regex pattern for retrieving the ID of the operation from the xs log.") cmd.Flags().StringVar(&stepConfig.OperationIDLogPattern, "operationIdLogPattern", `^.*xs bg-deploy -i (.*) -a.*$`, "Regex pattern for retrieving the ID of the operation from the xs log.")
cmd.Flags().StringVar(&stepConfig.MtaPath, "mtaPath", os.Getenv("PIPER_mtaPath"), "Path to deployable") cmd.Flags().StringVar(&stepConfig.MtaPath, "mtaPath", os.Getenv("PIPER_mtaPath"), "Path to deployable")
cmd.Flags().StringVar(&stepConfig.Action, "action", "NONE", "Used for finalizing the blue-green deployment.") cmd.Flags().StringVar(&stepConfig.Action, "action", `NONE`, "Used for finalizing the blue-green deployment.")
cmd.Flags().StringVar(&stepConfig.Mode, "mode", "DEPLOY", "Controls if there is a standard deployment or a blue green deployment. Values: 'DEPLOY', 'BG_DEPLOY'") cmd.Flags().StringVar(&stepConfig.Mode, "mode", `DEPLOY`, "Controls if there is a standard deployment or a blue green deployment. Values: 'DEPLOY', 'BG_DEPLOY'")
cmd.Flags().StringVar(&stepConfig.OperationID, "operationId", os.Getenv("PIPER_operationId"), "The operation ID. Used in case of bg-deploy in order to resume or abort a previously started deployment.") cmd.Flags().StringVar(&stepConfig.OperationID, "operationId", os.Getenv("PIPER_operationId"), "The operation ID. Used in case of bg-deploy in order to resume or abort a previously started deployment.")
cmd.Flags().StringVar(&stepConfig.APIURL, "apiUrl", os.Getenv("PIPER_apiUrl"), "The api url (e.g. https://example.org:12345") cmd.Flags().StringVar(&stepConfig.APIURL, "apiUrl", os.Getenv("PIPER_apiUrl"), "The api url (e.g. https://example.org:12345")
cmd.Flags().StringVar(&stepConfig.User, "user", os.Getenv("PIPER_user"), "User") cmd.Flags().StringVar(&stepConfig.User, "user", os.Getenv("PIPER_user"), "User")

View File

@ -0,0 +1,8 @@
# ${docGenStepName}
## ${docGenDescription}
## ${docGenParameters}
## ${docGenConfiguration}

12
go.mod
View File

@ -5,6 +5,9 @@ go 1.13
require ( require (
github.com/GoogleContainerTools/container-diff v0.15.0 github.com/GoogleContainerTools/container-diff v0.15.0
github.com/Jeffail/gabs/v2 v2.5.0 github.com/Jeffail/gabs/v2 v2.5.0
github.com/Masterminds/goutils v1.1.0 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible
github.com/Microsoft/hcsshim v0.8.7 // indirect github.com/Microsoft/hcsshim v0.8.7 // indirect
github.com/bmatcuk/doublestar v1.2.4 github.com/bmatcuk/doublestar v1.2.4
github.com/containerd/containerd v1.3.4 // indirect github.com/containerd/containerd v1.3.4 // indirect
@ -12,13 +15,20 @@ require (
github.com/getsentry/sentry-go v0.6.0 github.com/getsentry/sentry-go v0.6.0
github.com/ghodss/yaml v1.0.0 github.com/ghodss/yaml v1.0.0
github.com/go-git/go-git/v5 v5.0.0 github.com/go-git/go-git/v5 v5.0.0
github.com/go-openapi/runtime v0.19.11
github.com/go-openapi/strfmt v0.19.4
github.com/gogo/protobuf v1.3.1 // indirect github.com/gogo/protobuf v1.3.1 // indirect
github.com/golang/protobuf v1.4.0 // indirect github.com/golang/protobuf v1.4.0 // indirect
github.com/google/go-cmp v0.4.0 github.com/google/go-cmp v0.4.0
github.com/google/go-containerregistry v0.0.0-20200413145205-82d30a103c0a github.com/google/go-containerregistry v0.0.0-20200131185320-aec8da010de2
github.com/google/go-github/v28 v28.1.1 github.com/google/go-github/v28 v28.1.1
github.com/google/uuid v1.1.1 github.com/google/uuid v1.1.1
github.com/huandu/xstrings v1.3.0 // indirect
github.com/imdario/mergo v0.3.9 // indirect
github.com/magiconair/properties v1.8.0 github.com/magiconair/properties v1.8.0
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/motemen/go-nuts v0.0.0-20190725124253-1d2432db96b0
github.com/piper-validation/fortify-client-go v0.0.0-20200206215926-532b5b150d22
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.5.0 github.com/sirupsen/logrus v1.5.0
github.com/spf13/cobra v1.0.0 github.com/spf13/cobra v1.0.0

135
go.sum
View File

@ -32,6 +32,12 @@ github.com/Jeffail/gabs/v2 v2.5.0 h1:ERXffrksCEPjKVDWbZDBcOwrpXctXfeFGXxOQh1umOE
github.com/Jeffail/gabs/v2 v2.5.0/go.mod h1:xCn81vdHKxFUuWWAaD5jCTQDNPBMh5pPs9IJ+NcziBI= github.com/Jeffail/gabs/v2 v2.5.0/go.mod h1:xCn81vdHKxFUuWWAaD5jCTQDNPBMh5pPs9IJ+NcziBI=
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM=
github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA=
@ -42,20 +48,28 @@ github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEY
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.27.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
@ -162,6 +176,8 @@ github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/
github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
@ -178,17 +194,67 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
github.com/go-openapi/analysis v0.19.5 h1:8b2ZgKfKIUTVQpTb77MoRDIMEIwvDVw40o3aOXdfYzI=
github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
github.com/go-openapi/errors v0.19.3 h1:7MGZI1ibQDLasvAz8HuhvYk9eNJbJkCOXWsSjjMS+Zc=
github.com/go-openapi/errors v0.19.3/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI=
github.com/go-openapi/loads v0.19.4 h1:5I4CCSqoWzT+82bBkNIvmLc0UOsoKKQ4Fz+3VxOB7SY=
github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
github.com/go-openapi/runtime v0.19.11 h1:6J11dQiIV+BOLlMbk2YmM8RvGaOU38syeqy62qhh3W8=
github.com/go-openapi/runtime v0.19.11/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc=
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
github.com/go-openapi/strfmt v0.19.4 h1:eRvaqAhpL0IL6Trh5fDsGnGhiXndzHFuA05w6sXH6/g=
github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.7 h1:VRuXN2EnMSsZdauzdss6JBC29YotDqG59BZ+tdlIL1s=
github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo=
github.com/go-openapi/validate v0.19.6 h1:WsKw9J1WzYBVxWRYwLqEk3325RL6G0SSWksuamkk6q0=
github.com/go-openapi/validate v0.19.6/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
@ -197,6 +263,7 @@ github.com/go-redis/redis v6.15.7+incompatible h1:3skhDh95XQMpnqeqNftPkQD9jL9e5e
github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
@ -232,8 +299,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-containerregistry v0.0.0-20200413145205-82d30a103c0a h1:XNvUa41C0oOgPmTmZKrWKhOCCFPLnOwLfm+pWU75sNs= github.com/google/go-containerregistry v0.0.0-20200131185320-aec8da010de2 h1:/z0FoA29APs30PljxT6GoZQekF5c1cYhow2osFsj1XU=
github.com/google/go-containerregistry v0.0.0-20200413145205-82d30a103c0a/go.mod h1:pD1UFYs7MCAx+ZLShBdttcaOSbyc8F9Na/9IZLNwJeA= github.com/google/go-containerregistry v0.0.0-20200131185320-aec8da010de2/go.mod h1:Wtl/v6YdQxv397EREtzwgd9+Ud7Q5D8XMbi3Zazgkrs=
github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo= github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo=
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
@ -242,6 +309,7 @@ github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSN
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -272,7 +340,11 @@ github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uG
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.0 h1:gvV6jG9dTgFEncxo+AF7PH6MZXi/vZl25owA/8Dg8Wo=
github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
@ -327,8 +399,11 @@ github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgx
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@ -344,9 +419,14 @@ github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88J
github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg=
github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
@ -355,6 +435,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/motemen/go-nuts v0.0.0-20190725124253-1d2432db96b0 h1:CnSVrlMNAZMWI1+uH6ldpXRv2pe7t50IQX448EJrJhw=
github.com/motemen/go-nuts v0.0.0-20190725124253-1d2432db96b0/go.mod h1:vfh/NPxHgDwggXit20W1llPsXcz39xJ7I8vo7kVrOCk=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
@ -388,10 +470,13 @@ github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJ
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/piper-validation/fortify-client-go v0.0.0-20200206215926-532b5b150d22 h1:xSbcGENeXvuG+tu4suCmsr+Vm+p3peYNgJDDxUBeJa8=
github.com/piper-validation/fortify-client-go v0.0.0-20200206215926-532b5b150d22/go.mod h1:EZkdCgngw/tInYdidqDQlRIXvyM1fSbqn/vx83YNCcw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@ -416,6 +501,7 @@ github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@ -464,6 +550,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/testcontainers/testcontainers-go v0.4.0 h1:BpzZG0/I4s4oAVrkYhf9K3R0AKLvhE0bX1kVSqyUVx8= github.com/testcontainers/testcontainers-go v0.4.0 h1:BpzZG0/I4s4oAVrkYhf9K3R0AKLvhE0bX1kVSqyUVx8=
github.com/testcontainers/testcontainers-go v0.4.0/go.mod h1:BXwe1JilTOLT8cmVyPMDbIw7e+8UCGeAhxjBwguG5wQ= github.com/testcontainers/testcontainers-go v0.4.0/go.mod h1:BXwe1JilTOLT8cmVyPMDbIw7e+8UCGeAhxjBwguG5wQ=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
@ -477,7 +565,8 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC
github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/vdemeester/k8s-pkg-credentialprovider v1.17.4/go.mod h1:inCTmtUdr5KJbreVojo06krnTgaeAz/Z7lynpPk/Q2c= github.com/vdemeester/k8s-pkg-credentialprovider v0.0.0-20200107171650-7c61ffa44238/go.mod h1:JwQJCMWpUDqjZrB5jpw0f5VbN7U95zxFy1ZDpoEarGo=
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
@ -494,6 +583,10 @@ github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZ
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.2 h1:jxcFYjlkl8xaERsgLo+RNquI0epW6zuy/ZRQs6jnrFA=
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@ -505,11 +598,14 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -522,17 +618,21 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -572,6 +672,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -605,6 +706,7 @@ golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@ -615,9 +717,12 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200210192313-1ace956b0e17/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200115165105-de0b1760071a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
@ -664,6 +769,7 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
@ -695,14 +801,15 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI=
k8s.io/apiserver v0.17.4/go.mod h1:5ZDQ6Xr5MNBxyi3iUZXS84QOhZl+W7Oq2us/29c0j9I= k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
k8s.io/client-go v0.17.4/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc= k8s.io/apiserver v0.17.0/go.mod h1:ABM+9x/prjINN6iiffRVNCBR2Wk7uY4z+EtEGZD48cg=
k8s.io/cloud-provider v0.17.4/go.mod h1:XEjKDzfD+b9MTLXQFlDGkk6Ho8SGMpaU8Uugx/KNK9U= k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= k8s.io/cloud-provider v0.17.0/go.mod h1:Ze4c3w2C0bRsjkBUoHpFi+qWe3ob1wI2/7cUn+YQIDE=
k8s.io/component-base v0.17.4/go.mod h1:5BRqHMbbQPm2kKu35v3G+CpVq4K0RJKC7TRioF0I9lE= k8s.io/code-generator v0.17.1/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
k8s.io/csi-translation-lib v0.17.4/go.mod h1:CsxmjwxEI0tTNMzffIAcgR9lX4wOh6AKHdxQrT7L0oo= k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc=
k8s.io/csi-translation-lib v0.17.0/go.mod h1:HEF7MEz7pOLJCnxabi45IPkhSsE/KmxPQksuCrHKWls=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
@ -710,7 +817,7 @@ k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
k8s.io/legacy-cloud-providers v0.17.4/go.mod h1:FikRNoD64ECjkxO36gkDgJeiQWwyZTuBkhu+yxOc1Js= k8s.io/legacy-cloud-providers v0.17.0/go.mod h1:DdzaepJ3RtRy+e5YhNtrCYwlgyK87j/5+Yfp0L9Syp8=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=

View File

@ -3,6 +3,7 @@ package checkmarx
import ( import (
"bytes" "bytes"
"errors" "errors"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@ -53,6 +54,9 @@ func (sm *senderMock) UploadRequest(method, url, file, fieldName string, header
sm.header = header sm.header = header
return &http.Response{StatusCode: sm.httpStatusCode, Body: ioutil.NopCloser(bytes.NewReader([]byte(sm.responseBody)))}, nil return &http.Response{StatusCode: sm.httpStatusCode, Body: ioutil.NopCloser(bytes.NewReader([]byte(sm.responseBody)))}, nil
} }
func (sm *senderMock) Upload(_ piperHttp.UploadRequestData) (*http.Response, error) {
return &http.Response{}, fmt.Errorf("not implemented")
}
func (sm *senderMock) SetOptions(opts piperHttp.ClientOptions) { func (sm *senderMock) SetOptions(opts piperHttp.ClientOptions) {
sm.token = opts.Token sm.token = opts.Token
} }

741
pkg/fortify/fortify.go Normal file
View File

@ -0,0 +1,741 @@
package fortify
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
"time"
ff "github.com/piper-validation/fortify-client-go/fortify"
"github.com/piper-validation/fortify-client-go/fortify/artifact_of_project_version_controller"
"github.com/piper-validation/fortify-client-go/fortify/attribute_of_project_version_controller"
"github.com/piper-validation/fortify-client-go/fortify/auth_entity_of_project_version_controller"
"github.com/piper-validation/fortify-client-go/fortify/file_token_controller"
"github.com/piper-validation/fortify-client-go/fortify/filter_set_of_project_version_controller"
"github.com/piper-validation/fortify-client-go/fortify/issue_group_of_project_version_controller"
"github.com/piper-validation/fortify-client-go/fortify/issue_selector_set_of_project_version_controller"
"github.com/piper-validation/fortify-client-go/fortify/issue_statistics_of_project_version_controller"
"github.com/piper-validation/fortify-client-go/fortify/project_controller"
"github.com/piper-validation/fortify-client-go/fortify/project_version_controller"
"github.com/piper-validation/fortify-client-go/fortify/project_version_of_project_controller"
"github.com/piper-validation/fortify-client-go/fortify/saved_report_controller"
"github.com/piper-validation/fortify-client-go/models"
piperHttp "github.com/SAP/jenkins-library/pkg/http"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/go-openapi/runtime"
"github.com/go-openapi/strfmt"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// System is the interface abstraction of a specific SystemInstance
type System interface {
GetProjectByName(name string, autoCreate bool, projectVersion string) (*models.Project, error)
GetProjectVersionDetailsByProjectIDAndVersionName(id int64, name string, autoCreate bool, projectName string) (*models.ProjectVersion, error)
GetProjectVersionAttributesByProjectVersionID(id int64) ([]*models.Attribute, error)
SetProjectVersionAttributesByProjectVersionID(id int64, attributes []*models.Attribute) ([]*models.Attribute, error)
CreateProjectVersionIfNotExist(projectName, projectVersionName, description string) (*models.ProjectVersion, error)
LookupOrCreateProjectVersionDetailsForPullRequest(projectID int64, masterProjectVersion *models.ProjectVersion, pullRequestName string) (*models.ProjectVersion, error)
CreateProjectVersion(version *models.ProjectVersion) (*models.ProjectVersion, error)
ProjectVersionCopyFromPartial(sourceID, targetID int64) error
ProjectVersionCopyCurrentState(sourceID, targetID int64) error
ProjectVersionCopyPermissions(sourceID, targetID int64) error
CommitProjectVersion(id int64) (*models.ProjectVersion, error)
MergeProjectVersionStateOfPRIntoMaster(downloadEndpoint, uploadEndpoint string, masterProjectID, masterProjectVersionID int64, pullRequestName string) error
GetArtifactsOfProjectVersion(id int64) ([]*models.Artifact, error)
GetFilterSetOfProjectVersionByTitle(id int64, title string) (*models.FilterSet, error)
GetIssueFilterSelectorOfProjectVersionByName(id int64, names []string, options []string) (*models.IssueFilterSelectorSet, error)
GetFilterSetByDisplayName(issueFilterSelectorSet *models.IssueFilterSelectorSet, name string) *models.IssueFilterSelector
GetProjectIssuesByIDAndFilterSetGroupedBySelector(id int64, filter, filterSetGUID string, issueFilterSelectorSet *models.IssueFilterSelectorSet) ([]*models.ProjectVersionIssueGroup, error)
ReduceIssueFilterSelectorSet(issueFilterSelectorSet *models.IssueFilterSelectorSet, names []string, options []string) *models.IssueFilterSelectorSet
GetIssueStatisticsOfProjectVersion(id int64) ([]*models.IssueStatistics, error)
GenerateQGateReport(projectID, projectVersionID, reportTemplateID int64, projectName, projectVersionName, reportFormat string) (*models.SavedReport, error)
GetReportDetails(id int64) (*models.SavedReport, error)
UploadResultFile(endpoint, file string, projectVersionID int64) error
DownloadReportFile(endpoint string, projectVersionID int64) ([]byte, error)
DownloadResultFile(endpoint string, projectVersionID int64) ([]byte, error)
}
// SystemInstance is the specific instance
type SystemInstance struct {
timeout time.Duration
token string
serverURL string
client *ff.Fortify
httpClient *piperHttp.Client
logger *logrus.Entry
}
// NewSystemInstance - creates an returns a new SystemInstance
func NewSystemInstance(serverURL, apiEndpoint, authToken string, timeout time.Duration) *SystemInstance {
format := strfmt.Default
dateTimeFormat := models.Iso8601MilliDateTime{}
format.Add("datetime", &dateTimeFormat, models.IsDateTime)
clientInstance := ff.NewHTTPClientWithConfig(format, createTransportConfig(serverURL, apiEndpoint))
httpClientInstance := &piperHttp.Client{}
httpClientOptions := piperHttp.ClientOptions{Token: "FortifyToken " + authToken, TransportTimeout: timeout}
httpClientInstance.SetOptions(httpClientOptions)
return NewSystemInstanceForClient(clientInstance, httpClientInstance, serverURL, authToken, timeout)
}
func createTransportConfig(serverURL, apiEndpoint string) *ff.TransportConfig {
scheme, host := splitSchemeAndHost(serverURL)
host, hostEndpoint := splitHostAndEndpoint(host)
return &ff.TransportConfig{
Host: host,
Schemes: []string{scheme},
BasePath: fmt.Sprintf("%v/%v", hostEndpoint, apiEndpoint)}
}
func splitSchemeAndHost(url string) (scheme, host string) {
schemeEnd := strings.Index(url, "://")
if schemeEnd >= 0 {
scheme = url[0:schemeEnd]
host = url[schemeEnd+3:]
} else {
scheme = "https"
host = url
}
return
}
func splitHostAndEndpoint(urlWithoutScheme string) (host, endpoint string) {
hostEnd := strings.Index(urlWithoutScheme, "/")
if hostEnd >= 0 {
host = urlWithoutScheme[0:hostEnd]
endpoint = urlWithoutScheme[hostEnd+1:]
} else {
host = urlWithoutScheme
endpoint = ""
}
return
}
// NewSystemInstanceForClient - creates a new SystemInstance
func NewSystemInstanceForClient(clientInstance *ff.Fortify, httpClientInstance *piperHttp.Client, serverURL, authToken string, requestTimeout time.Duration) *SystemInstance {
return &SystemInstance{
timeout: requestTimeout,
token: authToken,
serverURL: serverURL,
client: clientInstance,
httpClient: httpClientInstance,
logger: log.Entry().WithField("package", "SAP/jenkins-library/pkg/fortify"),
}
}
// AuthenticateRequest authenticates the request
func (sys *SystemInstance) AuthenticateRequest(req runtime.ClientRequest, formats strfmt.Registry) error {
req.SetHeaderParam("Authorization", fmt.Sprintf("FortifyToken %v", sys.token))
return nil
}
// GetProjectByName returns the project identified by the name provided
// autoCreate and projectVersion parameters only used if autoCreate=true
func (sys *SystemInstance) GetProjectByName(projectName string, autoCreate bool, projectVersionName string) (*models.Project, error) {
nameParam := fmt.Sprintf("name=%v", projectName)
fullText := true
params := &project_controller.ListProjectParams{Q: &nameParam, Fulltextsearch: &fullText}
params.WithTimeout(sys.timeout)
result, err := sys.client.ProjectController.ListProject(params, sys)
if err != nil {
return nil, err
}
for _, project := range result.GetPayload().Data {
if *project.Name == projectName {
return project, nil
}
}
// Project with specified name was NOT found, check if autoCreate flag is set, if not stop otherwise create it automatically
if !autoCreate {
return nil, fmt.Errorf("Project with name %v not found in backend and automatic creation not enabled", projectName)
}
log.Entry().Debugf("No projects found with name: %v auto-creating one now...", projectName)
projectVersion, err := sys.CreateProjectVersionIfNotExist(projectName, projectVersionName, "Created by Go script")
if err != nil {
return nil, fmt.Errorf("failed to auto-create new project: %w", err)
}
log.Entry().Debugf("Finished creating project: %v", projectVersion)
return projectVersion.Project, nil
}
// GetProjectVersionDetailsByProjectIDAndVersionName returns the project version details of the project version identified by the id and project versionname
// projectName parameter is only used if autoCreate=true
func (sys *SystemInstance) GetProjectVersionDetailsByProjectIDAndVersionName(id int64, versionName string, autoCreate bool, projectName string) (*models.ProjectVersion, error) {
nameParam := fmt.Sprintf("name=%v", versionName)
fullText := true
params := &project_version_of_project_controller.ListProjectVersionOfProjectParams{ParentID: id, Q: &nameParam, Fulltextsearch: &fullText}
params.WithTimeout(sys.timeout)
result, err := sys.client.ProjectVersionOfProjectController.ListProjectVersionOfProject(params, sys)
if err != nil {
return nil, err
}
for _, projectVersion := range result.GetPayload().Data {
if *projectVersion.Name == versionName {
return projectVersion, nil
}
}
// projectVersion not found for specified project id and name, check if autoCreate is enabled
if !autoCreate {
return nil, errors.New(fmt.Sprintf("Project version with name %v not found in project with ID %v and automatic creation not enabled", versionName, id))
}
log.Entry().Debugf("Could not find project version with name %v under project %v auto-creating one now...", versionName, projectName)
version, err := sys.CreateProjectVersionIfNotExist(projectName, versionName, "Created by Go script")
if err != nil {
return nil, errors.Wrapf(err, "failed to auto-create project version: %v for project %v", versionName, projectName)
}
log.Entry().Debugf("Successfully created project version %v for project %v", versionName, projectName)
return version, nil
}
// CreateProjectVersionIfNotExist creates a new ProjectVersion if it does not already exist.
// If the projectName also does not exist, it will create that as well.
func (sys *SystemInstance) CreateProjectVersionIfNotExist(projectName, projectVersionName, description string) (*models.ProjectVersion, error) {
var projectID int64 = 0
// check if project with projectName exists
projectResp, err := sys.GetProjectByName(projectName, false, "")
if err == nil {
// project already exists, all we need to do is append a new ProjectVersion to it
// save the project id for later
projectID = projectResp.ID
}
issueTemplateID := "4c5799c9-1940-4abe-b57a-3bcad88eb041"
active := true
committed := true
projectVersionDto := &models.ProjectVersion{
Name: &projectVersionName,
Description: &description,
IssueTemplateID: &issueTemplateID,
Active: &active,
Committed: &committed,
Project: &models.Project{ID: projectID},
}
if projectVersionDto.Project.ID == 0 { // project does not exist, set one up
projectVersionDto.Project = &models.Project{
Name: &projectName,
Description: description,
IssueTemplateID: &issueTemplateID,
}
}
projectVersion, err := sys.CreateProjectVersion(projectVersionDto)
if err != nil {
return nil, errors.Wrapf(err, "Failed to create new project version %v for projectName %v", projectVersionName, projectName)
}
_, err = sys.CommitProjectVersion(projectVersion.ID)
if err != nil {
return nil, errors.Wrapf(err, "Failed to commit project version %v: %v", projectVersion.ID, err)
}
return projectVersion, nil
}
// LookupOrCreateProjectVersionDetailsForPullRequest looks up a project version for pull requests or creates it from scratch
func (sys *SystemInstance) LookupOrCreateProjectVersionDetailsForPullRequest(projectID int64, masterProjectVersion *models.ProjectVersion, pullRequestName string) (*models.ProjectVersion, error) {
projectVersion, _ := sys.GetProjectVersionDetailsByProjectIDAndVersionName(projectID, pullRequestName, false, "")
if nil != projectVersion {
return projectVersion, nil
}
newVersion := &models.ProjectVersion{}
newVersion.Name = &pullRequestName
newVersion.Description = masterProjectVersion.Description
newVersion.Active = masterProjectVersion.Active
newVersion.Committed = masterProjectVersion.Committed
newVersion.Project = &models.Project{}
newVersion.Project.Name = masterProjectVersion.Project.Name
newVersion.Project.Description = masterProjectVersion.Project.Description
newVersion.Project.ID = masterProjectVersion.Project.ID
newVersion.IssueTemplateID = masterProjectVersion.IssueTemplateID
projectVersion, err := sys.CreateProjectVersion(newVersion)
if err != nil {
return nil, errors.Wrapf(err, "Failed to create new project version for pull request %v", pullRequestName)
}
attributes, err := sys.GetProjectVersionAttributesByProjectVersionID(masterProjectVersion.ID)
if err != nil {
return nil, errors.Wrapf(err, "Failed to load project version attributes for master project version %v", masterProjectVersion.ID)
}
for _, attribute := range attributes {
attribute.ID = 0
}
_, err = sys.SetProjectVersionAttributesByProjectVersionID(projectVersion.ID, attributes)
if err != nil {
return nil, errors.Wrapf(err, "Failed to update project version attributes for pull request project version %v", projectVersion.ID)
}
err = sys.ProjectVersionCopyFromPartial(masterProjectVersion.ID, projectVersion.ID)
if err != nil {
return nil, errors.Wrapf(err, "Failed to copy from partial of project version %v to %v", masterProjectVersion.ID, projectVersion.ID)
}
_, err = sys.CommitProjectVersion(projectVersion.ID)
if err != nil {
return nil, errors.Wrapf(err, "Failed to commit project version %v: %v", projectVersion.ID, err)
}
err = sys.ProjectVersionCopyCurrentState(masterProjectVersion.ID, projectVersion.ID)
if err != nil {
return nil, errors.Wrapf(err, "Failed to copy current state of project version %v to %v", masterProjectVersion.ID, projectVersion.ID)
}
err = sys.ProjectVersionCopyPermissions(masterProjectVersion.ID, projectVersion.ID)
if err != nil {
return nil, errors.Wrapf(err, "Failed to copy permissions of project version %v to %v", masterProjectVersion.ID, projectVersion.ID)
}
return projectVersion, nil
}
// GetProjectVersionAttributesByProjectVersionID returns the project version attributes of the project version identified by the id
func (sys *SystemInstance) GetProjectVersionAttributesByProjectVersionID(id int64) ([]*models.Attribute, error) {
params := &attribute_of_project_version_controller.ListAttributeOfProjectVersionParams{ParentID: id}
params.WithTimeout(sys.timeout)
result, err := sys.client.AttributeOfProjectVersionController.ListAttributeOfProjectVersion(params, sys)
if err != nil {
return nil, err
}
return result.GetPayload().Data, nil
}
// SetProjectVersionAttributesByProjectVersionID sets the project version attributes of the project version identified by the id
func (sys *SystemInstance) SetProjectVersionAttributesByProjectVersionID(id int64, attributes []*models.Attribute) ([]*models.Attribute, error) {
params := &attribute_of_project_version_controller.UpdateCollectionAttributeOfProjectVersionParams{ParentID: id, Data: attributes}
params.WithTimeout(sys.timeout)
result, err := sys.client.AttributeOfProjectVersionController.UpdateCollectionAttributeOfProjectVersion(params, sys)
if err != nil {
return nil, err
}
return result.GetPayload().Data, nil
}
// CreateProjectVersion creates the project version with the provided details
func (sys *SystemInstance) CreateProjectVersion(version *models.ProjectVersion) (*models.ProjectVersion, error) {
params := &project_version_controller.CreateProjectVersionParams{Resource: version}
params.WithTimeout(sys.timeout)
result, err := sys.client.ProjectVersionController.CreateProjectVersion(params, sys)
if err != nil {
return nil, err
}
return result.GetPayload().Data, nil
}
// ProjectVersionCopyFromPartial copies parts of the source project version to the target project version identified by their ids
func (sys *SystemInstance) ProjectVersionCopyFromPartial(sourceID, targetID int64) error {
enable := true
settings := models.ProjectVersionCopyPartialRequest{
ProjectVersionID: &targetID,
PreviousProjectVersionID: &sourceID,
CopyAnalysisProcessingRules: &enable,
CopyBugTrackerConfiguration: &enable,
CopyCurrentStateFpr: &enable,
CopyCustomTags: &enable,
}
params := &project_version_controller.CopyProjectVersionParams{Resource: &settings}
params.WithTimeout(sys.timeout)
_, err := sys.client.ProjectVersionController.CopyProjectVersion(params, sys)
if err != nil {
return err
}
return nil
}
// ProjectVersionCopyCurrentState copies the project version state of sourceID into the new project version addressed by targetID
func (sys *SystemInstance) ProjectVersionCopyCurrentState(sourceID, targetID int64) error {
enable := true
settings := models.ProjectVersionCopyCurrentStateRequest{
ProjectVersionID: &targetID,
PreviousProjectVersionID: &sourceID,
CopyCurrentStateFpr: &enable,
}
params := &project_version_controller.CopyCurrentStateForProjectVersionParams{Resource: &settings}
params.WithTimeout(sys.timeout)
_, err := sys.client.ProjectVersionController.CopyCurrentStateForProjectVersion(params, sys)
if err != nil {
return err
}
return nil
}
func (sys *SystemInstance) getAuthEntityOfProjectVersion(id int64) ([]*models.AuthenticationEntity, error) {
embed := "roles"
params := &auth_entity_of_project_version_controller.ListAuthEntityOfProjectVersionParams{Embed: &embed, ParentID: id}
params.WithTimeout(sys.timeout)
result, err := sys.client.AuthEntityOfProjectVersionController.ListAuthEntityOfProjectVersion(params, sys)
if err != nil {
return nil, err
}
return result.GetPayload().Data, nil
}
func (sys *SystemInstance) updateCollectionAuthEntityOfProjectVersion(id int64, data []*models.AuthenticationEntity) error {
params := &auth_entity_of_project_version_controller.UpdateCollectionAuthEntityOfProjectVersionParams{ParentID: id, Data: data}
params.WithTimeout(sys.timeout)
_, err := sys.client.AuthEntityOfProjectVersionController.UpdateCollectionAuthEntityOfProjectVersion(params, sys)
if err != nil {
return err
}
return nil
}
// ProjectVersionCopyPermissions copies the authentication entity of the project version addressed by sourceID to the one of targetID
func (sys *SystemInstance) ProjectVersionCopyPermissions(sourceID, targetID int64) error {
result, err := sys.getAuthEntityOfProjectVersion(sourceID)
if err != nil {
return err
}
err = sys.updateCollectionAuthEntityOfProjectVersion(targetID, result)
if err != nil {
return err
}
return nil
}
func (sys *SystemInstance) updateProjectVersionDetails(id int64, details *models.ProjectVersion) (*models.ProjectVersion, error) {
params := &project_version_controller.UpdateProjectVersionParams{ID: id, Resource: details}
params.WithTimeout(sys.timeout)
result, err := sys.client.ProjectVersionController.UpdateProjectVersion(params, sys)
if err != nil {
return nil, err
}
return result.GetPayload().Data, nil
}
// CommitProjectVersion commits the project version with the provided id
func (sys *SystemInstance) CommitProjectVersion(id int64) (*models.ProjectVersion, error) {
enabled := true
update := models.ProjectVersion{Committed: &enabled}
return sys.updateProjectVersionDetails(id, &update)
}
func (sys *SystemInstance) inactivateProjectVersion(id int64) (*models.ProjectVersion, error) {
enabled := true
disabled := false
update := models.ProjectVersion{Committed: &enabled, Active: &disabled}
return sys.updateProjectVersionDetails(id, &update)
}
// GetArtifactsOfProjectVersion returns the list of artifacts related to the project version addressed with id
func (sys *SystemInstance) GetArtifactsOfProjectVersion(id int64) ([]*models.Artifact, error) {
scans := "scans"
params := &artifact_of_project_version_controller.ListArtifactOfProjectVersionParams{ParentID: id, Embed: &scans}
params.WithTimeout(sys.timeout)
result, err := sys.client.ArtifactOfProjectVersionController.ListArtifactOfProjectVersion(params, sys)
if err != nil {
return nil, err
}
return result.GetPayload().Data, nil
}
// MergeProjectVersionStateOfPRIntoMaster merges the PR project version's fpr result file into the master project version
func (sys *SystemInstance) MergeProjectVersionStateOfPRIntoMaster(downloadEndpoint, uploadEndpoint string, masterProjectID, masterProjectVersionID int64, pullRequestName string) error {
log.Entry().Debugf("Looking up project version with name '%v' to merge audit status into master version", pullRequestName)
prProjectVersion, _ := sys.GetProjectVersionDetailsByProjectIDAndVersionName(masterProjectID, pullRequestName, false, "")
if nil != prProjectVersion {
log.Entry().Debugf("Found project version with ID '%v', starting transfer", prProjectVersion.ID)
data, err := sys.DownloadResultFile(downloadEndpoint, prProjectVersion.ID)
if err != nil {
return errors.Wrapf(err, "Failed to download current state FPR of PR project version %v", prProjectVersion.ID)
}
err = sys.uploadResultFileContent(uploadEndpoint, "prMergeTransfer.fpr", bytes.NewReader(data), masterProjectVersionID)
if err != nil {
return errors.Wrapf(err, "Failed to upload PR project version state to master project version %v", masterProjectVersionID)
}
_, err = sys.inactivateProjectVersion(prProjectVersion.ID)
if err != nil {
log.Entry().Warnf("Failed to inactivate merged PR project version %v", prProjectVersion.ID)
}
} else {
log.Entry().Debug("No related project version found in SSC")
}
return nil
}
// GetFilterSetOfProjectVersionByTitle returns the filter set with the given title related to the project version addressed with id, if no title is provided the default filter set will be returned
func (sys *SystemInstance) GetFilterSetOfProjectVersionByTitle(id int64, title string) (*models.FilterSet, error) {
params := &filter_set_of_project_version_controller.ListFilterSetOfProjectVersionParams{ParentID: id}
params.WithTimeout(sys.timeout)
result, err := sys.client.FilterSetOfProjectVersionController.ListFilterSetOfProjectVersion(params, sys)
if err != nil {
return nil, err
}
var defaultFilterSet *models.FilterSet
for _, filterSet := range result.GetPayload().Data {
if len(title) > 0 && filterSet.Title == title {
return filterSet, nil
}
if filterSet.DefaultFilterSet {
defaultFilterSet = filterSet
}
}
if len(title) > 0 {
log.Entry().Warnf("Failed to load filter set with title '%v', falling back to default filter set", title)
}
if nil != defaultFilterSet {
return defaultFilterSet, nil
}
return nil, fmt.Errorf("Failed to identify requested filter set and default filter")
}
// GetIssueFilterSelectorOfProjectVersionByName returns the groupings with the given names related to the project version addressed with id
func (sys *SystemInstance) GetIssueFilterSelectorOfProjectVersionByName(id int64, names []string, options []string) (*models.IssueFilterSelectorSet, error) {
params := &issue_selector_set_of_project_version_controller.GetIssueSelectorSetOfProjectVersionParams{ParentID: id}
params.WithTimeout(sys.timeout)
result, err := sys.client.IssueSelectorSetOfProjectVersionController.GetIssueSelectorSetOfProjectVersion(params, sys)
if err != nil {
return nil, err
}
return sys.ReduceIssueFilterSelectorSet(result.GetPayload().Data, names, options), nil
}
// ReduceIssueFilterSelectorSet filters the set to the relevant filter display names
func (sys *SystemInstance) ReduceIssueFilterSelectorSet(issueFilterSelectorSet *models.IssueFilterSelectorSet, names []string, options []string) *models.IssueFilterSelectorSet {
groupingList := []*models.IssueSelector{}
if issueFilterSelectorSet.GroupBySet != nil {
for _, group := range issueFilterSelectorSet.GroupBySet {
if piperutils.ContainsString(names, *group.DisplayName) {
log.Entry().Debugf("adding new grouping '%v' to reduced list", *group.DisplayName)
groupingList = append(groupingList, group)
}
}
}
filterList := []*models.IssueFilterSelector{}
if issueFilterSelectorSet.FilterBySet != nil {
for _, filter := range issueFilterSelectorSet.FilterBySet {
if piperutils.ContainsString(names, filter.DisplayName) {
newFilter := &models.IssueFilterSelector{}
newFilter.DisplayName = filter.DisplayName
newFilter.Description = filter.Description
newFilter.EntityType = filter.EntityType
newFilter.FilterSelectorType = filter.FilterSelectorType
newFilter.GUID = filter.GUID
newFilter.Value = filter.Value
newFilter.SelectorOptions = []*models.SelectorOption{}
for _, option := range filter.SelectorOptions {
if (nil != options && piperutils.ContainsString(options, option.DisplayName)) || options == nil || len(options) == 0 {
log.Entry().Debugf("adding selector option '%v' to list for filter selector '%v'", option.DisplayName, newFilter.DisplayName)
newFilter.SelectorOptions = append(newFilter.SelectorOptions, option)
}
}
log.Entry().Debugf("adding new filter '%v' to reduced list with selector options '%v'", newFilter.DisplayName, newFilter.SelectorOptions)
filterList = append(filterList, newFilter)
}
}
}
return &models.IssueFilterSelectorSet{GroupBySet: groupingList, FilterBySet: filterList}
}
//GetFilterSetByDisplayName returns the set identified by the provided name or nil
func (sys *SystemInstance) GetFilterSetByDisplayName(issueFilterSelectorSet *models.IssueFilterSelectorSet, name string) *models.IssueFilterSelector {
if issueFilterSelectorSet.FilterBySet != nil {
for _, filter := range issueFilterSelectorSet.FilterBySet {
if filter.DisplayName == name {
return filter
}
}
}
return nil
}
func (sys *SystemInstance) getIssuesOfProjectVersion(id int64, filter, filterset, groupingtype string) ([]*models.ProjectVersionIssueGroup, error) {
enable := true
params := &issue_group_of_project_version_controller.ListIssueGroupOfProjectVersionParams{ParentID: id, Showsuppressed: &enable, Filterset: &filterset, Groupingtype: &groupingtype}
params.WithTimeout(sys.timeout)
if len(filter) > 0 {
params.WithFilter(&filter)
}
result, err := sys.client.IssueGroupOfProjectVersionController.ListIssueGroupOfProjectVersion(params, sys)
if err != nil {
return nil, err
}
return result.GetPayload().Data, nil
}
// GetProjectIssuesByIDAndFilterSetGroupedBySelector returns issues of the project version addressed with id filtered with the respective set and grouped by the issue filter selector grouping
func (sys *SystemInstance) GetProjectIssuesByIDAndFilterSetGroupedBySelector(id int64, filter, filterSetGUID string, issueFilterSelectorSet *models.IssueFilterSelectorSet) ([]*models.ProjectVersionIssueGroup, error) {
groupingTypeGUID := ""
if issueFilterSelectorSet != nil {
groupingTypeGUID = *issueFilterSelectorSet.GroupBySet[0].GUID
}
result, err := sys.getIssuesOfProjectVersion(id, filter, filterSetGUID, groupingTypeGUID)
if err != nil {
return nil, err
}
return result, nil
}
// GetIssueStatisticsOfProjectVersion returns the issue statistics related to the project version addressed with id
func (sys *SystemInstance) GetIssueStatisticsOfProjectVersion(id int64) ([]*models.IssueStatistics, error) {
params := &issue_statistics_of_project_version_controller.ListIssueStatisticsOfProjectVersionParams{ParentID: id}
params.WithTimeout(sys.timeout)
result, err := sys.client.IssueStatisticsOfProjectVersionController.ListIssueStatisticsOfProjectVersion(params, sys)
if err != nil {
return nil, err
}
return result.GetPayload().Data, nil
}
// GenerateQGateReport returns the issue statistics related to the project version addressed with id
func (sys *SystemInstance) GenerateQGateReport(projectID, projectVersionID, reportTemplateID int64, projectName, projectVersionName, reportFormat string) (*models.SavedReport, error) {
paramIdentifier := "projectVersionId"
paramType := "SINGLE_PROJECT"
paramName := "Q-gate-report"
reportType := "PORTFOLIO"
inputReportParameters := []*models.InputReportParameter{&models.InputReportParameter{Name: &paramName, Identifier: &paramIdentifier, ParamValue: projectVersionID, Type: &paramType}}
reportProjectVersions := []*models.ReportProjectVersion{&models.ReportProjectVersion{ID: projectVersionID, Name: projectVersionName}}
reportProjects := []*models.ReportProject{&models.ReportProject{ID: projectID, Name: projectName, Versions: reportProjectVersions}}
report := models.SavedReport{Name: fmt.Sprintf("FortifyReport: %v:%v", projectName, projectVersionName), Type: &reportType, ReportDefinitionID: &reportTemplateID, Format: &reportFormat, Projects: reportProjects, InputReportParameters: inputReportParameters}
params := &saved_report_controller.CreateSavedReportParams{Resource: &report}
params.WithTimeout(sys.timeout)
result, err := sys.client.SavedReportController.CreateSavedReport(params, sys)
if err != nil {
return nil, err
}
return result.GetPayload().Data, nil
}
// GetReportDetails returns the details of the report addressed with id
func (sys *SystemInstance) GetReportDetails(id int64) (*models.SavedReport, error) {
params := &saved_report_controller.ReadSavedReportParams{ID: id}
params.WithTimeout(sys.timeout)
result, err := sys.client.SavedReportController.ReadSavedReport(params, sys)
if err != nil {
return nil, err
}
return result.GetPayload().Data, nil
}
func (sys *SystemInstance) invalidateFileTokens() error {
log.Entry().Debug("invalidating file tokens")
params := &file_token_controller.MultiDeleteFileTokenParams{}
params.WithTimeout(sys.timeout)
_, err := sys.client.FileTokenController.MultiDeleteFileToken(params, sys)
return err
}
func (sys *SystemInstance) getFileToken(tokenType string) (*models.FileToken, error) {
token := models.FileToken{FileTokenType: &tokenType}
params := &file_token_controller.CreateFileTokenParams{Resource: &token}
params.WithTimeout(sys.timeout)
result, err := sys.client.FileTokenController.CreateFileToken(params, sys)
if err != nil {
return nil, err
}
return result.GetPayload().Data, nil
}
func (sys *SystemInstance) getFileUploadToken() (*models.FileToken, error) {
log.Entry().Debug("fetching upload token")
return sys.getFileToken("UPLOAD")
}
func (sys *SystemInstance) getFileDownloadToken() (*models.FileToken, error) {
log.Entry().Debug("fetching download token")
return sys.getFileToken("DOWNLOAD")
}
func (sys *SystemInstance) getReportFileToken() (*models.FileToken, error) {
log.Entry().Debug("fetching report download token")
return sys.getFileToken("REPORT_FILE")
}
// UploadResultFile uploads a fpr file to the fortify backend
func (sys *SystemInstance) UploadResultFile(endpoint, file string, projectVersionID int64) error {
fileHandle, err := os.Open(file)
if err != nil {
return errors.Wrapf(err, "Unable to locate file %v", file)
}
defer fileHandle.Close()
return sys.uploadResultFileContent(endpoint, file, fileHandle, projectVersionID)
}
func (sys *SystemInstance) uploadResultFileContent(endpoint, file string, fileContent io.Reader, projectVersionID int64) error {
token, err := sys.getFileUploadToken()
if err != nil {
return err
}
defer sys.invalidateFileTokens()
header := http.Header{}
header.Add("Cache-Control", "no-cache, no-store, must-revalidate")
header.Add("Pragma", "no-cache")
formFields := map[string]string{}
formFields["entityId"] = fmt.Sprintf("%v", projectVersionID)
_, err = sys.httpClient.Upload(piperHttp.UploadRequestData{
Method: http.MethodPost,
URL: fmt.Sprintf("%v%v?mat=%v", sys.serverURL, endpoint, token.Token),
File: file,
FileFieldName: "file",
FormFields: formFields,
FileContent: fileContent,
Header: header,
})
return err
}
// DownloadFile downloads a file from Fortify backend
func (sys *SystemInstance) downloadFile(endpoint, method, acceptType, downloadToken string, projectVersionID int64) ([]byte, error) {
header := http.Header{}
header.Add("Cache-Control", "no-cache, no-store, must-revalidate")
header.Add("Pragma", "no-cache")
header.Add("Accept", acceptType)
header.Add("Content-Type", "application/form-data")
body := url.Values{
"id": {fmt.Sprintf("%v", projectVersionID)},
"mat": {downloadToken},
}
var response *http.Response
var err error
if method == http.MethodGet {
response, err = sys.httpClient.SendRequest(method, fmt.Sprintf("%v%v?%v", sys.serverURL, endpoint, body.Encode()), nil, header, nil)
} else {
response, err = sys.httpClient.SendRequest(method, fmt.Sprintf("%v%v", sys.serverURL, endpoint), strings.NewReader(body.Encode()), header, nil)
}
if err != nil {
return nil, err
}
data, err := ioutil.ReadAll(response.Body)
defer response.Body.Close()
if err != nil {
return nil, errors.Wrap(err, "Error reading the response data")
}
return data, nil
}
// DownloadReportFile downloads a report file from Fortify backend
func (sys *SystemInstance) DownloadReportFile(endpoint string, projectVersionID int64) ([]byte, error) {
token, err := sys.getReportFileToken()
if err != nil {
return nil, errors.Wrap(err, "Error fetching report download token")
}
defer sys.invalidateFileTokens()
data, err := sys.downloadFile(endpoint, http.MethodGet, "application/octet-stream", token.Token, projectVersionID)
if err != nil {
return nil, errors.Wrap(err, "Error downloading report file")
}
return data, nil
}
// DownloadResultFile downloads a result file from Fortify backend
func (sys *SystemInstance) DownloadResultFile(endpoint string, projectVersionID int64) ([]byte, error) {
token, err := sys.getFileDownloadToken()
if err != nil {
return nil, errors.Wrap(err, "Error fetching result file download token")
}
defer sys.invalidateFileTokens()
data, err := sys.downloadFile(endpoint, http.MethodGet, "application/zip", token.Token, projectVersionID)
if err != nil {
return nil, errors.Wrap(err, "Error downloading result file")
}
return data, nil
}

1330
pkg/fortify/fortify_test.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,9 +7,12 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"strings" "strings"
"text/template" "text/template"
"github.com/Masterminds/sprig"
"github.com/SAP/jenkins-library/pkg/config" "github.com/SAP/jenkins-library/pkg/config"
"github.com/SAP/jenkins-library/pkg/piperutils" "github.com/SAP/jenkins-library/pkg/piperutils"
) )
@ -56,8 +59,13 @@ import (
) )
type {{ .StepName }}Options struct { type {{ .StepName }}Options struct {
{{- range $key, $value := .StepParameters }} {{- $names := list ""}}
{{ $value.Name | golangName }} {{ $value.Type }} ` + "`json:\"{{$value.Name}},omitempty\"`" + `{{end}} {{- range $key, $value := uniqueName .StepParameters }}
{{ if ne (has $value.Name $names) true -}}
{{ $names | last }}{{ $value.Name | golangName }} {{ $value.Type }} ` + "`json:\"{{$value.Name}},omitempty\"`" + `
{{- else -}}
{{- $names = append $names $value.Name }} {{ end -}}
{{ end }}
} }
{{ range $notused, $oRes := .OutputResources }} {{ range $notused, $oRes := .OutputResources }}
@ -124,7 +132,7 @@ func {{.CobraCmdFuncName}}() *cobra.Command {
} }
func {{.FlagsFunc}}(cmd *cobra.Command, stepConfig *{{.StepName}}Options) { func {{.FlagsFunc}}(cmd *cobra.Command, stepConfig *{{.StepName}}Options) {
{{- range $key, $value := .StepParameters }} {{- range $key, $value := uniqueName .StepParameters }}
cmd.Flags().{{ $value.Type | flagType }}(&stepConfig.{{ $value.Name | golangName }}, "{{ $value.Name }}", {{ $value.Default }}, "{{ $value.Description }}"){{ end }} cmd.Flags().{{ $value.Type | flagType }}(&stepConfig.{{ $value.Name | golangName }}, "{{ $value.Name }}", {{ $value.Default }}, "{{ $value.Description }}"){{ end }}
{{- printf "\n" }} {{- printf "\n" }}
{{- range $key, $value := .StepParameters }}{{ if $value.Mandatory }} {{- range $key, $value := .StepParameters }}{{ if $value.Mandatory }}
@ -300,9 +308,9 @@ func setDefaultParameters(stepData *config.StepData) (bool, error) {
case "int": case "int":
param.Default = fmt.Sprintf("%v", param.Default) param.Default = fmt.Sprintf("%v", param.Default)
case "string": case "string":
param.Default = fmt.Sprintf("\"%v\"", param.Default) param.Default = fmt.Sprintf("`%v`", param.Default)
case "[]string": case "[]string":
param.Default = fmt.Sprintf("[]string{\"%v\"}", strings.Join(getStringSliceFromInterface(param.Default), "\", \"")) param.Default = fmt.Sprintf("[]string{`%v`}", strings.Join(getStringSliceFromInterface(param.Default), "`, `"))
default: default:
return false, fmt.Errorf("Meta data type not set or not known: '%v'", param.Type) return false, fmt.Errorf("Meta data type not set or not known: '%v'", param.Type)
} }
@ -432,12 +440,12 @@ func MetadataFiles(sourceDirectory string) ([]string, error) {
func stepTemplate(myStepInfo stepInfo) []byte { func stepTemplate(myStepInfo stepInfo) []byte {
funcMap := template.FuncMap{ funcMap := sprig.HermeticTxtFuncMap()
"flagType": flagType, funcMap["flagType"] = flagType
"golangName": golangNameTitle, funcMap["golangName"] = golangNameTitle
"title": strings.Title, funcMap["title"] = strings.Title
"longName": longName, funcMap["longName"] = longName
} funcMap["uniqueName"] = mustUniqName
tmpl, err := template.New("step").Funcs(funcMap).Parse(stepGoTemplate) tmpl, err := template.New("step").Funcs(funcMap).Parse(stepGoTemplate)
checkError(err) checkError(err)
@ -451,11 +459,11 @@ func stepTemplate(myStepInfo stepInfo) []byte {
func stepTestTemplate(myStepInfo stepInfo) []byte { func stepTestTemplate(myStepInfo stepInfo) []byte {
funcMap := template.FuncMap{ funcMap := sprig.HermeticTxtFuncMap()
"flagType": flagType, funcMap["flagType"] = flagType
"golangName": golangNameTitle, funcMap["golangName"] = golangNameTitle
"title": strings.Title, funcMap["title"] = strings.Title
} funcMap["uniqueName"] = mustUniqName
tmpl, err := template.New("stepTest").Funcs(funcMap).Parse(stepTestGoTemplate) tmpl, err := template.New("stepTest").Funcs(funcMap).Parse(stepTestGoTemplate)
checkError(err) checkError(err)
@ -469,9 +477,9 @@ func stepTestTemplate(myStepInfo stepInfo) []byte {
func stepImplementation(myStepInfo stepInfo) []byte { func stepImplementation(myStepInfo stepInfo) []byte {
funcMap := template.FuncMap{ funcMap := sprig.HermeticTxtFuncMap()
"title": strings.Title, funcMap["title"] = strings.Title
} funcMap["uniqueName"] = mustUniqName
tmpl, err := template.New("impl").Funcs(funcMap).Parse(stepGoImplementationTemplate) tmpl, err := template.New("impl").Funcs(funcMap).Parse(stepGoImplementationTemplate)
checkError(err) checkError(err)
@ -536,3 +544,27 @@ func getStringSliceFromInterface(iSlice interface{}) []string {
return s return s
} }
func mustUniqName(list []config.StepParameters) ([]config.StepParameters, error) {
tp := reflect.TypeOf(list).Kind()
switch tp {
case reflect.Slice, reflect.Array:
l2 := reflect.ValueOf(list)
l := l2.Len()
names := []string{}
dest := []config.StepParameters{}
var item config.StepParameters
for i := 0; i < l; i++ {
item = l2.Index(i).Interface().(config.StepParameters)
if !piperutils.ContainsString(names, item.Name) {
names = append(names, item.Name)
dest = append(dest, item)
}
}
return dest, nil
default:
return nil, fmt.Errorf("Cannot find uniq on type %s", tp)
}
}

View File

@ -145,11 +145,11 @@ func TestSetDefaultParameters(t *testing.T) {
} }
expected := []string{ expected := []string{
"\"val0\"", "`val0`",
"os.Getenv(\"PIPER_param1\")", "os.Getenv(\"PIPER_param1\")",
"true", "true",
"false", "false",
"[]string{\"val4_1\", \"val4_2\"}", "[]string{`val4_1`, `val4_2`}",
"[]string{}", "[]string{}",
"0", "0",
"1", "1",

View File

@ -149,7 +149,7 @@ func TestStepCommand() *cobra.Command {
} }
func addTestStepFlags(cmd *cobra.Command, stepConfig *testStepOptions) { func addTestStepFlags(cmd *cobra.Command, stepConfig *testStepOptions) {
cmd.Flags().StringVar(&stepConfig.Param0, "param0", "val0", "param0 description") cmd.Flags().StringVar(&stepConfig.Param0, "param0", `val0`, "param0 description")
cmd.Flags().StringVar(&stepConfig.Param1, "param1", os.Getenv("PIPER_param1"), "param1 description") cmd.Flags().StringVar(&stepConfig.Param1, "param1", os.Getenv("PIPER_param1"), "param1 description")
cmd.Flags().StringVar(&stepConfig.Param2, "param2", os.Getenv("PIPER_param2"), "param1 description") cmd.Flags().StringVar(&stepConfig.Param2, "param2", os.Getenv("PIPER_param2"), "param1 description")

View File

@ -148,7 +148,7 @@ func TestStepCommand() *cobra.Command {
} }
func addTestStepFlags(cmd *cobra.Command, stepConfig *testStepOptions) { func addTestStepFlags(cmd *cobra.Command, stepConfig *testStepOptions) {
cmd.Flags().StringVar(&stepConfig.Param0, "param0", "val0", "param0 description") cmd.Flags().StringVar(&stepConfig.Param0, "param0", `val0`, "param0 description")
cmd.Flags().StringVar(&stepConfig.Param1, "param1", os.Getenv("PIPER_param1"), "param1 description") cmd.Flags().StringVar(&stepConfig.Param1, "param1", os.Getenv("PIPER_param1"), "param1 description")
cmd.Flags().StringVar(&stepConfig.Param2, "param2", os.Getenv("PIPER_param2"), "param1 description") cmd.Flags().StringVar(&stepConfig.Param2, "param2", os.Getenv("PIPER_param2"), "param1 description")

View File

@ -2,6 +2,7 @@ package http
import ( import (
"bytes" "bytes"
"context"
"fmt" "fmt"
"io" "io"
"mime/multipart" "mime/multipart"
@ -12,19 +13,22 @@ import (
"time" "time"
"github.com/SAP/jenkins-library/pkg/log" "github.com/SAP/jenkins-library/pkg/log"
"github.com/motemen/go-nuts/roundtime"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
// Client defines an http client object // Client defines an http client object
type Client struct { type Client struct {
maxRequestDuration time.Duration maxRequestDuration time.Duration
transportTimeout time.Duration transportTimeout time.Duration
username string username string
password string password string
token string token string
logger *logrus.Entry logger *logrus.Entry
cookieJar http.CookieJar cookieJar http.CookieJar
doLogRequestBodyOnDebug bool
doLogResponseBodyOnDebug bool
} }
// ClientOptions defines the options to be set on the client // ClientOptions defines the options to be set on the client
@ -36,12 +40,39 @@ type ClientOptions struct {
MaxRequestDuration time.Duration MaxRequestDuration time.Duration
// TransportTimeout defaults to 10 seconds, if not specified. It is // TransportTimeout defaults to 10 seconds, if not specified. It is
// used for the transport layer and duration of handshakes and such. // used for the transport layer and duration of handshakes and such.
TransportTimeout time.Duration TransportTimeout time.Duration
Username string Username string
Password string Password string
Token string Token string
Logger *logrus.Entry Logger *logrus.Entry
CookieJar http.CookieJar CookieJar http.CookieJar
DoLogRequestBodyOnDebug bool
DoLogResponseBodyOnDebug bool
}
// TransportWrapper is a wrapper for central logging capabilities
type TransportWrapper struct {
Transport http.RoundTripper
doLogRequestBodyOnDebug bool
doLogResponseBodyOnDebug bool
}
// UploadRequestData encapsulates the parameters for calling uploader.Upload()
type UploadRequestData struct {
// Method is the HTTP method used for the request. Must be one of http.MethodPost or http.MethodPut.
Method string
// URL for the request
URL string
// File path to be stored in the created form field.
File string
// Form field name under which the file name will be stored.
FileFieldName string
// Additional form fields which will be added to the request if not nil.
FormFields map[string]string
// Reader from which the file contents will be read.
FileContent io.Reader
Header http.Header
Cookies []*http.Cookie
} }
// Sender provides an interface to the piper http client for uid/pwd and token authenticated requests // Sender provides an interface to the piper http client for uid/pwd and token authenticated requests
@ -55,18 +86,36 @@ type Uploader interface {
Sender Sender
UploadRequest(method, url, file, fieldName string, header http.Header, cookies []*http.Cookie) (*http.Response, error) UploadRequest(method, url, file, fieldName string, header http.Header, cookies []*http.Cookie) (*http.Response, error)
UploadFile(url, file, fieldName string, header http.Header, cookies []*http.Cookie) (*http.Response, error) UploadFile(url, file, fieldName string, header http.Header, cookies []*http.Cookie) (*http.Response, error)
Upload(data UploadRequestData) (*http.Response, error)
} }
// UploadFile uploads a file's content as multipart-form POST request to the specified URL // UploadFile uploads a file's content as multipart-form POST request to the specified URL
func (c *Client) UploadFile(url, file, fieldName string, header http.Header, cookies []*http.Cookie) (*http.Response, error) { func (c *Client) UploadFile(url, file, fileFieldName string, header http.Header, cookies []*http.Cookie) (*http.Response, error) {
return c.UploadRequest(http.MethodPost, url, file, fieldName, header, cookies) return c.UploadRequest(http.MethodPost, url, file, fileFieldName, header, cookies)
} }
// UploadRequest uploads a file's content as multipart-form with given http method request to the specified URL // UploadRequest uploads a file's content as multipart-form with given http method request to the specified URL
func (c *Client) UploadRequest(method, url, file, fieldName string, header http.Header, cookies []*http.Cookie) (*http.Response, error) { func (c *Client) UploadRequest(method, url, file, fileFieldName string, header http.Header, cookies []*http.Cookie) (*http.Response, error) {
fileHandle, err := os.Open(file)
if err != nil {
return &http.Response{}, errors.Wrapf(err, "unable to locate file %v", file)
}
defer fileHandle.Close()
return c.Upload(UploadRequestData{
Method: method,
URL: url,
File: file,
FileFieldName: fileFieldName,
FileContent: fileHandle,
Header: header,
Cookies: cookies,
})
}
if method != http.MethodPost && method != http.MethodPut { // Upload uploads a file's content as multipart-form with given http method request to the specified URL
return nil, errors.New(fmt.Sprintf("Http method %v is not allowed. Possible values are %v or %v", method, http.MethodPost, http.MethodPut)) func (c *Client) Upload(data UploadRequestData) (*http.Response, error) {
if data.Method != http.MethodPost && data.Method != http.MethodPut {
return nil, errors.New(fmt.Sprintf("Http method %v is not allowed. Possible values are %v or %v", data.Method, http.MethodPost, http.MethodPut))
} }
httpClient := c.initialize() httpClient := c.initialize()
@ -74,27 +123,30 @@ func (c *Client) UploadRequest(method, url, file, fieldName string, header http.
bodyBuffer := &bytes.Buffer{} bodyBuffer := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuffer) bodyWriter := multipart.NewWriter(bodyBuffer)
fileWriter, err := bodyWriter.CreateFormFile(fieldName, file) if data.FormFields != nil {
if err != nil { for fieldName, fieldValue := range data.FormFields {
return &http.Response{}, errors.Wrapf(err, "error creating form file %v for field %v", file, fieldName) err := bodyWriter.WriteField(fieldName, fieldValue)
if err != nil {
return &http.Response{}, errors.Wrapf(err, "error writing form field %v with value %v", fieldName, fieldValue)
}
}
} }
fileHandle, err := os.Open(file) fileWriter, err := bodyWriter.CreateFormFile(data.FileFieldName, data.File)
if err != nil { if err != nil {
return &http.Response{}, errors.Wrapf(err, "unable to locate file %v", file) return &http.Response{}, errors.Wrapf(err, "error creating form file %v for field %v", data.File, data.FileFieldName)
} }
defer fileHandle.Close()
_, err = io.Copy(fileWriter, fileHandle) _, err = io.Copy(fileWriter, data.FileContent)
if err != nil { if err != nil {
return &http.Response{}, errors.Wrapf(err, "unable to copy file content of %v into request body", file) return &http.Response{}, errors.Wrapf(err, "unable to copy file content of %v into request body", data.File)
} }
err = bodyWriter.Close() err = bodyWriter.Close()
request, err := c.createRequest(method, url, bodyBuffer, &header, cookies) request, err := c.createRequest(data.Method, data.URL, bodyBuffer, &data.Header, data.Cookies)
if err != nil { if err != nil {
c.logger.Debugf("New %v request to %v", method, url) c.logger.Debugf("New %v request to %v", data.Method, data.URL)
return &http.Response{}, errors.Wrapf(err, "error creating %v request to %v", method, url) return &http.Response{}, errors.Wrapf(err, "error creating %v request to %v", data.Method, data.URL)
} }
startBoundary := strings.Index(bodyWriter.FormDataContentType(), "=") + 1 startBoundary := strings.Index(bodyWriter.FormDataContentType(), "=") + 1
@ -105,7 +157,7 @@ func (c *Client) UploadRequest(method, url, file, fieldName string, header http.
response, err := httpClient.Do(request) response, err := httpClient.Do(request)
if err != nil { if err != nil {
return response, errors.Wrapf(err, "HTTP %v request to %v failed with error", method, url) return response, errors.Wrapf(err, "HTTP %v request to %v failed with error", data.Method, data.URL)
} }
return c.handleResponse(response) return c.handleResponse(response)
@ -117,13 +169,12 @@ func (c *Client) SendRequest(method, url string, body io.Reader, header http.Hea
request, err := c.createRequest(method, url, body, &header, cookies) request, err := c.createRequest(method, url, body, &header, cookies)
if err != nil { if err != nil {
c.logger.Debugf("New %v request to %v", method, url)
return &http.Response{}, errors.Wrapf(err, "error creating %v request to %v", method, url) return &http.Response{}, errors.Wrapf(err, "error creating %v request to %v", method, url)
} }
response, err := httpClient.Do(request) response, err := httpClient.Do(request)
if err != nil { if err != nil {
return response, errors.Wrapf(err, "error opening %v", url) return response, errors.Wrapf(err, "error calling %v", url)
} }
return c.handleResponse(response) return c.handleResponse(response)
@ -131,6 +182,8 @@ func (c *Client) SendRequest(method, url string, body io.Reader, header http.Hea
// SetOptions sets options used for the http client // SetOptions sets options used for the http client
func (c *Client) SetOptions(options ClientOptions) { func (c *Client) SetOptions(options ClientOptions) {
c.doLogRequestBodyOnDebug = options.DoLogRequestBodyOnDebug
c.doLogResponseBodyOnDebug = options.DoLogResponseBodyOnDebug
c.transportTimeout = options.TransportTimeout c.transportTimeout = options.TransportTimeout
c.maxRequestDuration = options.MaxRequestDuration c.maxRequestDuration = options.MaxRequestDuration
c.username = options.Username c.username = options.Username
@ -149,15 +202,18 @@ func (c *Client) initialize() *http.Client {
c.applyDefaults() c.applyDefaults()
c.logger = log.Entry().WithField("package", "SAP/jenkins-library/pkg/http") c.logger = log.Entry().WithField("package", "SAP/jenkins-library/pkg/http")
var transport = &http.Transport{ var transport = &TransportWrapper{
DialContext: (&net.Dialer{ Transport: &http.Transport{
Timeout: c.transportTimeout, DialContext: (&net.Dialer{
}).DialContext, Timeout: c.transportTimeout,
ResponseHeaderTimeout: c.transportTimeout, }).DialContext,
ExpectContinueTimeout: c.transportTimeout, ResponseHeaderTimeout: c.transportTimeout,
TLSHandshakeTimeout: c.transportTimeout, ExpectContinueTimeout: c.transportTimeout,
TLSHandshakeTimeout: c.transportTimeout,
},
doLogRequestBodyOnDebug: c.doLogRequestBodyOnDebug,
doLogResponseBodyOnDebug: c.doLogResponseBodyOnDebug,
} }
var httpClient = &http.Client{ var httpClient = &http.Client{
Timeout: c.maxRequestDuration, Timeout: c.maxRequestDuration,
Transport: transport, Transport: transport,
@ -168,8 +224,71 @@ func (c *Client) initialize() *http.Client {
return httpClient return httpClient
} }
type contextKey struct {
name string
}
var contextKeyRequestStart = &contextKey{"RequestStart"}
// RoundTrip is the core part of this module and implements http.RoundTripper.
// Executes HTTP request with request/response logging.
func (t *TransportWrapper) RoundTrip(req *http.Request) (*http.Response, error) {
ctx := context.WithValue(req.Context(), contextKeyRequestStart, time.Now())
req = req.WithContext(ctx)
t.logRequest(req)
resp, err := t.Transport.RoundTrip(req)
t.logResponse(resp)
return resp, err
}
func (t *TransportWrapper) logRequest(req *http.Request) {
log.Entry().Debug("--------------------------------")
log.Entry().Debugf("--> %v request to %v", req.Method, req.URL)
log.Entry().Debugf("headers: %v", req.Header)
log.Entry().Debugf("cookies: %v", transformCookies(req.Cookies()))
if t.doLogRequestBodyOnDebug {
log.Entry().Debugf("body: %v", transformBody(req.Body))
}
log.Entry().Debug("--------------------------------")
}
func (t *TransportWrapper) logResponse(resp *http.Response) {
if resp != nil {
ctx := resp.Request.Context()
if start, ok := ctx.Value(contextKeyRequestStart).(time.Time); ok {
log.Entry().Debugf("<-- response %v %v (%v)", resp.StatusCode, resp.Request.URL, roundtime.Duration(time.Now().Sub(start), 2))
} else {
log.Entry().Debugf("<-- response %v %v", resp.StatusCode, resp.Request.URL)
}
if t.doLogResponseBodyOnDebug {
log.Entry().Debugf("body: %v", transformBody(resp.Body))
}
} else {
log.Entry().Debug("response <nil>")
}
log.Entry().Debug("--------------------------------")
}
func transformCookies(cookies []*http.Cookie) string {
result := ""
for _, c := range cookies {
result = fmt.Sprintf("%v %v", result, c.String())
}
return result
}
func transformBody(body io.ReadCloser) string {
if body == nil {
return ""
}
buf := new(bytes.Buffer)
buf.ReadFrom(body)
return buf.String()
}
func (c *Client) createRequest(method, url string, body io.Reader, header *http.Header, cookies []*http.Cookie) (*http.Request, error) { func (c *Client) createRequest(method, url string, body io.Reader, header *http.Header, cookies []*http.Cookie) (*http.Request, error) {
c.logger.Debugf("New %v request to %v", method, url)
request, err := http.NewRequest(method, url, body) request, err := http.NewRequest(method, url, body)
if err != nil { if err != nil {
return &http.Request{}, err return &http.Request{}, err
@ -215,7 +334,7 @@ func (c *Client) handleResponse(response *http.Response) (*http.Response, error)
case http.StatusNotFound: case http.StatusNotFound:
c.logger.WithField("HTTP Error", "404 (Not Found)").Error("Requested resource could not be found") c.logger.WithField("HTTP Error", "404 (Not Found)").Error("Requested resource could not be found")
case http.StatusInternalServerError: case http.StatusInternalServerError:
c.logger.WithField("HTTP Error", "500 (Internal Server Error)").Error("Unknown error occured.") c.logger.WithField("HTTP Error", "500 (Internal Server Error)").Error("Unknown error occurred.")
} }
return response, fmt.Errorf("Request to %v returned with response %v", response.Request.URL, response.Status) return response, fmt.Errorf("Request to %v returned with response %v", response.Request.URL, response.Status)

View File

@ -24,6 +24,16 @@ func ContainsString(s []string, e string) bool {
return false return false
} }
//ContainsStringPart check wether the element is contained as part of one of the elements of the slice
func ContainsStringPart(s []string, part string) bool {
for _, a := range s {
if strings.Contains(a, part) {
return true
}
}
return false
}
//Prefix adds a prefix to each element of the slice //Prefix adds a prefix to each element of the slice
func Prefix(in []string, prefix string) []string { func Prefix(in []string, prefix string) []string {
return _prefix(in, prefix, true) return _prefix(in, prefix, true)

View File

@ -0,0 +1,30 @@
package piperutils
import (
"bytes"
"fmt"
"text/template"
)
// ExecuteTemplate parses the provided template, substitutes values and returns the output
func ExecuteTemplate(txtTemplate string, context interface{}) (string, error) {
return ExecuteTemplateFunctions(txtTemplate, nil, context)
}
// ExecuteTemplateFunctions parses the provided template, applies the transformation functions, substitutes values and returns the output
func ExecuteTemplateFunctions(txtTemplate string, functionMap template.FuncMap, context interface{}) (string, error) {
template := template.New("tmp")
if functionMap != nil {
template = template.Funcs(functionMap)
}
template, err := template.Parse(txtTemplate)
if err != nil {
return "<nil>", fmt.Errorf("Failed to parse template defintion %v: %w", txtTemplate, err)
}
var output bytes.Buffer
err = template.Execute(&output, context)
if err != nil {
return "<nil>", fmt.Errorf("Failed to transform template defintion %v: %w", txtTemplate, err)
}
return output.String(), nil
}

View File

@ -0,0 +1,46 @@
package piperutils
import (
"github.com/stretchr/testify/assert"
"testing"
"text/template"
)
type SomeDescriptor struct {
GroupID string
ArtifactID string
Version string
}
func TestExecuteTemplate(t *testing.T) {
t.Run("test success", func(t *testing.T) {
context := SomeDescriptor{GroupID: "com.sap.cp.jenkins", ArtifactID: "piper", Version: "1.2.3"}
result, err := ExecuteTemplate("{{ .GroupID }}-{{ .ArtifactID }}:{{ .Version}}", context)
assert.NoError(t, err, "Didn't expect error but got one")
assert.Equal(t, "com.sap.cp.jenkins-piper:1.2.3", result, "Expected different result")
})
t.Run("test template error", func(t *testing.T) {
context := SomeDescriptor{GroupID: "com.sap.cp.jenkins", ArtifactID: "piper", Version: "1.2.3"}
_, err := ExecuteTemplate("{{ $+++.+++GroupID }}-{{ .ArtifactID }}:{{ .Version}}", context)
assert.Error(t, err, "Expected error but got none")
})
t.Run("test functions", func(t *testing.T) {
functions := template.FuncMap{
"testFunc": reverse,
}
context := SomeDescriptor{GroupID: "com.sap.cp.jenkins", ArtifactID: "piper", Version: "1.2.3"}
result, err := ExecuteTemplateFunctions("{{ testFunc .GroupID }}-{{ .ArtifactID }}:{{ .Version}}", functions, context)
assert.NoError(t, err, "Didn't expect error but got one")
assert.Equal(t, "sniknej.pc.pas.moc-piper:1.2.3", result, "Expected different result")
})
}
func reverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}

22
pkg/piperutils/testdata/2_setup.py vendored Normal file
View File

@ -0,0 +1,22 @@
from setuptools import setup, find_packages
from codecs import open
from os import path
def get_version():
with open('version.txt') as ver_file:
version_str = ver_file.readline().rstrip()
return version_str
def get_install_requires():
with open('requirements.txt') as reqs_file:
reqs = [line.rstrip() for line in reqs_file.readlines()]
return reqs
setup(name="some-test",
version="1.0.0",
python_requires='>=3',
packages=find_packages(exclude=['contrib', 'docs', 'tests*', 'coverage', 'bin']),
description="test",
install_requires=get_install_requires(),
)

1
pkg/piperutils/testdata/VERSION.TXT vendored Normal file
View File

@ -0,0 +1 @@
1.0.0-SNAPSHOT

22
pkg/piperutils/testdata/setup.py vendored Normal file
View File

@ -0,0 +1,22 @@
from setuptools import setup, find_packages
from codecs import open
from os import path
def get_version():
with open('version.txt') as ver_file:
version_str = ver_file.readline().rstrip()
return version_str
def get_install_requires():
with open('requirements.txt') as reqs_file:
reqs = [line.rstrip() for line in reqs_file.readlines()]
return reqs
setup(name="some-test",
version=get_version(),
python_requires='>=3',
packages=find_packages(exclude=['contrib', 'docs', 'tests*', 'coverage', 'bin']),
description="test",
install_requires=get_install_requires(),
)

15
pkg/piperutils/testdata/test2_pom.xml vendored Normal file
View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<artifactId>parent-inherit-test</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<parent>
<groupId>com.sap.ldi</groupId>
<artifactId>ldi-parent-root</artifactId>
<version>7.4.0</version>
</parent>
</project>

9
pkg/piperutils/testdata/test_pom.xml vendored Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>test.groupID</groupId>
<artifactId>test-articatID</artifactId>
<version>1.0.0</version>
</project>

View File

@ -0,0 +1,47 @@
package versioning
import (
"github.com/Masterminds/sprig"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperutils"
)
const (
// SchemeMajorVersion is the versioning scheme based on the major version only
SchemeMajorVersion = `{{(split "." (split "-" .Version)._0)._0}}`
// SchemeMajorMinorVersion is the versioning scheme based on the major version only
SchemeMajorMinorVersion = `{{(split "." (split "-" .Version)._0)._0}}.{{(split "." (split "-" .Version)._0)._1}}`
// SchemeSemanticVersion is the versioning scheme based on the major.minor.micro version
SchemeSemanticVersion = `{{(split "-" .Version)._0}}`
// SchemeFullVersion is the versioning scheme based on the full version
SchemeFullVersion = "{{.Version}}"
)
// DetermineProjectCoordinates resolve the coordinates of the project for use in 3rd party scan tools
func DetermineProjectCoordinates(nameTemplate, versionScheme string, gav Coordinates) (string, string) {
projectName, err := piperutils.ExecuteTemplateFunctions(nameTemplate, sprig.HermeticTxtFuncMap(), gav)
if err != nil {
log.Entry().Warnf("Unable to resolve fortify project name: %v", err)
}
var versionTemplate string
if versionScheme == "full" {
versionTemplate = SchemeFullVersion
}
if versionScheme == "major" {
versionTemplate = SchemeMajorVersion
}
if versionScheme == "major-minor" {
versionTemplate = SchemeMajorMinorVersion
}
if versionScheme == "semantic" {
versionTemplate = SchemeSemanticVersion
}
projectVersion, err := piperutils.ExecuteTemplateFunctions(versionTemplate, sprig.HermeticTxtFuncMap(), gav)
if err != nil {
log.Entry().Warnf("Unable to resolve fortify project version: %v", err)
}
return projectName, projectVersion
}

View File

@ -0,0 +1,92 @@
package versioning
import (
"github.com/stretchr/testify/assert"
"testing"
)
type mavenMock struct {
groupID string
artifactID string
version string
packaging string
}
func (m *mavenMock) VersioningScheme() string {
return "maven"
}
func (m *mavenMock) GetVersion() (string, error) {
return m.version, nil
}
func (m *mavenMock) SetVersion(v string) error {
m.version = v
return nil
}
func (m *mavenMock) GetCoordinates() (Coordinates, error) {
return &MavenDescriptor{GroupID: m.groupID, ArtifactID: m.artifactID, Version: m.version, Packaging: m.packaging}, nil
}
type pipMock struct {
artifactID string
version string
}
func (p *pipMock) VersioningScheme() string {
return "pep440"
}
func (p *pipMock) GetVersion() (string, error) {
return p.version, nil
}
func (p *pipMock) SetVersion(v string) error {
p.version = v
return nil
}
func (p *pipMock) GetCoordinates() (Coordinates, error) {
return &PipDescriptor{ArtifactID: p.artifactID, Version: p.version}, nil
}
func TestDetermineProjectCoordinates(t *testing.T) {
nameTemplate := `{{list .GroupID .ArtifactID | join "-" | trimAll "-"}}`
t.Run("maven", func(t *testing.T) {
gav, _ := (&mavenMock{groupID: "com.test.pkg", artifactID: "analyzer", version: "1.2.3"}).GetCoordinates()
name, version := DetermineProjectCoordinates(nameTemplate, "major", gav)
assert.Equal(t, "com.test.pkg-analyzer", name, "Expected different project name")
assert.Equal(t, "1", version, "Expected different project version")
})
t.Run("maven major-minor", func(t *testing.T) {
gav, _ := (&mavenMock{groupID: "com.test.pkg", artifactID: "analyzer", version: "1.2.3"}).GetCoordinates()
name, version := DetermineProjectCoordinates(nameTemplate, "major-minor", gav)
assert.Equal(t, "com.test.pkg-analyzer", name, "Expected different project name")
assert.Equal(t, "1.2", version, "Expected different project version")
})
t.Run("maven full", func(t *testing.T) {
gav, _ := (&mavenMock{groupID: "com.test.pkg", artifactID: "analyzer", version: "1.2.3-7864387648746"}).GetCoordinates()
name, version := DetermineProjectCoordinates(nameTemplate, "full", gav)
assert.Equal(t, "com.test.pkg-analyzer", name, "Expected different project name")
assert.Equal(t, "1.2.3-7864387648746", version, "Expected different project version")
})
t.Run("maven semantic", func(t *testing.T) {
gav, _ := (&mavenMock{groupID: "com.test.pkg", artifactID: "analyzer", version: "1.2.3-7864387648746"}).GetCoordinates()
name, version := DetermineProjectCoordinates(nameTemplate, "semantic", gav)
assert.Equal(t, "com.test.pkg-analyzer", name, "Expected different project name")
assert.Equal(t, "1.2.3", version, "Expected different project version")
})
t.Run("maven empty", func(t *testing.T) {
gav, _ := (&mavenMock{groupID: "com.test.pkg", artifactID: "analyzer", version: "0-SNAPSHOT"}).GetCoordinates()
name, version := DetermineProjectCoordinates(nameTemplate, "snapshot", gav)
assert.Equal(t, "com.test.pkg-analyzer", name, "Expected different project name")
assert.Equal(t, "", version, "Expected different project version")
})
t.Run("python", func(t *testing.T) {
gav, _ := (&pipMock{artifactID: "python-test", version: "2.2.3"}).GetCoordinates()
name, version := DetermineProjectCoordinates(nameTemplate, "major", gav)
assert.Equal(t, "python-test", name, "Expected different project name")
assert.Equal(t, "2", version, "Expected different project version")
})
}

View File

@ -141,3 +141,8 @@ func (d *Docker) versionFromBaseImageTag() string {
} }
return "" return ""
} }
// GetCoordinates returns the coordinates
func (d *Docker) GetCoordinates() (Coordinates, error) {
return nil, nil
}

View File

@ -85,3 +85,8 @@ func (i *INIfile) SetVersion(version string) error {
} }
return nil return nil
} }
// GetCoordinates returns the coordinates
func (i *INIfile) GetCoordinates() (Coordinates, error) {
return nil, nil
}

View File

@ -76,3 +76,8 @@ func (j *JSONfile) SetVersion(version string) error {
return nil return nil
} }
// GetCoordinates returns the coordinates
func (j *JSONfile) GetCoordinates() (Coordinates, error) {
return nil, nil
}

View File

@ -21,6 +21,14 @@ type mavenRunner interface {
Evaluate(string, string, mavenExecRunner) (string, error) Evaluate(string, string, mavenExecRunner) (string, error)
} }
// MavenDescriptor holds the unique identifier combination for Maven built Java artifacts
type MavenDescriptor struct {
GroupID string
ArtifactID string
Version string
Packaging string
}
// Maven defines a maven artifact used for versioning // Maven defines a maven artifact used for versioning
type Maven struct { type Maven struct {
pomPath string pomPath string
@ -46,6 +54,62 @@ func (m *Maven) VersioningScheme() string {
return "maven" return "maven"
} }
// GetCoordinates reads the coordinates from the maven pom.xml descriptor file
func (m *Maven) GetCoordinates() (Coordinates, error) {
result := &MavenDescriptor{}
var err error
result.GroupID, err = m.GetGroupID()
if err != nil {
return nil, err
}
result.ArtifactID, err = m.GetArtifactID()
if err != nil {
return nil, err
}
result.Version, err = m.GetVersion()
if err != nil {
return nil, err
}
result.Packaging, err = m.GetPackaging()
if err != nil {
return nil, err
}
return result, nil
}
// GetPackaging returns the current ID of the Group
func (m *Maven) GetPackaging() (string, error) {
m.init()
packaging, err := m.runner.Evaluate(m.pomPath, "project.packaging", m.execRunner)
if err != nil {
return "", errors.Wrap(err, "Maven - getting packaging failed")
}
return packaging, nil
}
// GetGroupID returns the current ID of the Group
func (m *Maven) GetGroupID() (string, error) {
m.init()
groupID, err := m.runner.Evaluate(m.pomPath, "project.groupId", m.execRunner)
if err != nil {
return "", errors.Wrap(err, "Maven - getting groupId failed")
}
return groupID, nil
}
// GetArtifactID returns the current ID of the artifact
func (m *Maven) GetArtifactID() (string, error) {
m.init()
artifactID, err := m.runner.Evaluate(m.pomPath, "project.artifactId", m.execRunner)
if err != nil {
return "", errors.Wrap(err, "Maven - getting artifactId failed")
}
return artifactID, nil
}
// GetVersion returns the current version of the artifact // GetVersion returns the current version of the artifact
func (m *Maven) GetVersion() (string, error) { func (m *Maven) GetVersion() (string, error) {
m.init() m.init()

146
pkg/versioning/pip.go Normal file
View File

@ -0,0 +1,146 @@
package versioning
import (
"fmt"
"io/ioutil"
"os"
"regexp"
"strings"
"github.com/pkg/errors"
)
const (
// NameRegex is used to match the pip descriptor artifact name
NameRegex = "(?s)(.*)name=['\"](.*?)['\"](.*)"
// VersionRegex is used to match the pip descriptor artifact version
VersionRegex = "(?s)(.*)version=['\"](.*?)['\"](.*)"
)
// PipDescriptor holds the unique identifier combination for pip built Python artifacts
type PipDescriptor struct {
GroupID string
ArtifactID string
Version string
Packaging string
}
// Pip utility to interact with Python specific versioning
type Pip struct {
path string
readFile func(string) ([]byte, error)
writeFile func(string, []byte, os.FileMode) error
fileExists func(string) (bool, error)
buildDescriptorContent string
}
func (p *Pip) init() error {
if p.readFile == nil {
p.readFile = ioutil.ReadFile
}
if p.writeFile == nil {
p.writeFile = ioutil.WriteFile
}
if len(p.buildDescriptorContent) > 0 {
content, err := p.readFile(p.path)
if err != nil {
return errors.Wrapf(err, "failed to read file '%v'", p.path)
}
p.buildDescriptorContent = string(content)
}
return nil
}
// GetVersion returns the Pip descriptor version property
func (p *Pip) GetVersion() (string, error) {
buildDescriptorFilePath := p.path
var err error
if strings.Contains(p.path, "setup.py") {
buildDescriptorFilePath, err = searchDescriptor([]string{"version.txt", "VERSION"}, p.fileExists)
if err != nil {
err = p.init()
if err != nil {
return "", errors.Wrapf(err, "failed to read file '%v'", p.path)
}
if evaluateResult(p.buildDescriptorContent, VersionRegex) {
compile := regexp.MustCompile(VersionRegex)
values := compile.FindStringSubmatch(p.buildDescriptorContent)
return values[2], nil
}
return "", errors.Wrap(err, "failed to retrieve version")
}
}
artifact := &Versionfile{
path: buildDescriptorFilePath,
versioningScheme: p.VersioningScheme(),
}
return artifact.GetVersion()
}
// SetVersion sets the Pip descriptor version property
func (p *Pip) SetVersion(v string) error {
buildDescriptorFilePath := p.path
var err error
if strings.Contains(p.path, "setup.py") {
buildDescriptorFilePath, err = searchDescriptor([]string{"version.txt", "VERSION"}, p.fileExists)
if err != nil {
err = p.init()
if err != nil {
return errors.Wrapf(err, "failed to read file '%v'", p.path)
}
if evaluateResult(p.buildDescriptorContent, VersionRegex) {
compile := regexp.MustCompile(VersionRegex)
values := compile.FindStringSubmatch(p.buildDescriptorContent)
p.buildDescriptorContent = strings.ReplaceAll(p.buildDescriptorContent, fmt.Sprintf("version='%v'", values[2]), fmt.Sprintf("version='%v'", v))
p.buildDescriptorContent = strings.ReplaceAll(p.buildDescriptorContent, fmt.Sprintf("version=\"%v\"", values[2]), fmt.Sprintf("version=\"%v\"", v))
p.writeFile(p.path, []byte(p.buildDescriptorContent), 0600)
} else {
return errors.Wrap(err, "failed to retrieve version")
}
}
}
artifact := &Versionfile{
path: buildDescriptorFilePath,
versioningScheme: p.VersioningScheme(),
}
return artifact.SetVersion(v)
}
// VersioningScheme returns the relevant versioning scheme
func (p *Pip) VersioningScheme() string {
return "pep440"
}
// GetCoordinates returns the pip build descriptor coordinates
func (p *Pip) GetCoordinates() (Coordinates, error) {
err := p.init()
if err != nil {
return nil, err
}
descriptor := &PipDescriptor{}
if evaluateResult(p.buildDescriptorContent, NameRegex) {
compile := regexp.MustCompile(NameRegex)
values := compile.FindStringSubmatch(p.buildDescriptorContent)
descriptor.ArtifactID = values[2]
} else {
descriptor.ArtifactID = ""
}
descriptor.Version, err = p.GetVersion()
if err != nil {
return nil, errors.Wrap(err, "failed to retrieve coordinates")
}
return descriptor, nil
}
func evaluateResult(value, regex string) bool {
if len(value) > 0 {
match, _ := regexp.MatchString(regex, value)
return match
}
return true
}

View File

@ -0,0 +1,3 @@
package versioning
import ()

View File

@ -60,3 +60,8 @@ func (v *Versionfile) SetVersion(version string) error {
return nil return nil
} }
// GetCoordinates returns the coordinates
func (v *Versionfile) GetCoordinates() (Coordinates, error) {
return nil, nil
}

View File

@ -9,11 +9,15 @@ import (
"github.com/SAP/jenkins-library/pkg/maven" "github.com/SAP/jenkins-library/pkg/maven"
) )
// Coordinates to address the artifact
type Coordinates interface{}
// Artifact defines the versioning operations for various build tools // Artifact defines the versioning operations for various build tools
type Artifact interface { type Artifact interface {
VersioningScheme() string VersioningScheme() string
GetVersion() (string, error) GetVersion() (string, error)
SetVersion(string) error SetVersion(string) error
GetCoordinates() (Coordinates, error)
} }
// Options define build tool specific settings in order to properly retrieve e.g. the version of an artifact // Options define build tool specific settings in order to properly retrieve e.g. the version of an artifact
@ -109,14 +113,14 @@ func GetArtifact(buildTool, buildDescriptorFilePath string, opts *Options, execR
case "pip": case "pip":
if len(buildDescriptorFilePath) == 0 { if len(buildDescriptorFilePath) == 0 {
var err error var err error
buildDescriptorFilePath, err = searchDescriptor([]string{"version.txt", "VERSION"}, fileExists) buildDescriptorFilePath, err = searchDescriptor([]string{"version.txt", "VERSION", "setup.py"}, fileExists)
if err != nil { if err != nil {
return artifact, err return artifact, err
} }
} }
artifact = &Versionfile{ artifact = &Pip{
path: buildDescriptorFilePath, path: buildDescriptorFilePath,
versioningScheme: "pep440", fileExists: fileExists,
} }
case "sbt": case "sbt":
if len(buildDescriptorFilePath) == 0 { if len(buildDescriptorFilePath) == 0 {

View File

@ -112,7 +112,7 @@ func TestGetArtifact(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
theType, ok := pip.(*Versionfile) theType, ok := pip.(*Pip)
assert.True(t, ok) assert.True(t, ok)
assert.Equal(t, "version.txt", theType.path) assert.Equal(t, "version.txt", theType.path)
assert.Equal(t, "pep440", pip.VersioningScheme()) assert.Equal(t, "pep440", pip.VersioningScheme())
@ -122,7 +122,7 @@ func TestGetArtifact(t *testing.T) {
fileExists = func(string) (bool, error) { return false, nil } fileExists = func(string) (bool, error) { return false, nil }
_, err := GetArtifact("pip", "", &Options{}, nil) _, err := GetArtifact("pip", "", &Options{}, nil)
assert.EqualError(t, err, "no build descriptor available, supported: [version.txt VERSION]") assert.EqualError(t, err, "no build descriptor available, supported: [version.txt VERSION setup.py]")
}) })
t.Run("sbt", func(t *testing.T) { t.Run("sbt", func(t *testing.T) {

View File

@ -77,3 +77,8 @@ func (y *YAMLfile) SetVersion(version string) error {
return nil return nil
} }
// GetCoordinates returns the coordinates
func (y *YAMLfile) GetCoordinates() (Coordinates, error) {
return nil, nil
}

View File

@ -0,0 +1,472 @@
metadata:
name: fortifyExecuteScan
description: This BETA step executes a Fortify scan on the specified project to perform static code analysis and check the source code for security flaws.
longDescription: |-
This step executes a Fortify scan on the specified project to perform static code analysis and check the source code for security flaws.
The Fortify step triggers a scan locally on your Jenkins within a docker container so finally you have to supply a docker image with a Fortify SCA
and Java plus Maven or alternatively Python installed into it for being able to perform any scans.
DISCLAIMER: The step has not yet been tested on a wide variaty of projects, and is therefore considered of BETA quality.
spec:
inputs:
secrets:
- name: fortifyCredentialsId
description: Jenkins 'Secret text' credentials ID containing token to authenticate to Fortify SSC.
type: jenkins
- name: githubTokenCredentialsId
description: Jenkins 'Secret text' credentials ID containing token to authenticate to GitHub.
type: jenkins
resources:
- name: commonPipelineEnvironment
resourceSpec:
type: piperEnvironment
- name: buildDescriptor
type: stash
- name: deployDescriptor
type: stash
- name: tests
type: stash
- name: opensourceConfiguration
type: stash
params:
- name: authToken
type: string
description: The FortifyToken to use for authentication
scope:
- PARAMETERS
- STAGES
- STEPS
mandatory: true
secret: true
- name: githubToken
description: GitHub personal access token as per https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
type: string
secret: true
- name: autoCreate
type: bool
description: Whether Fortify project and project version shall be implicitly auto created in case they cannot be found in the backend
scope:
- PARAMETERS
- STAGES
- STEPS
- name: mvnCustomArgs
type: string
description: Allows providing additional Maven command line parameters
scope:
- PARAMETERS
- STAGES
- STEPS
default: ''
- name: modulePath
type: string
description: Allows providing the path for the module to scan
scope:
- PARAMETERS
- STAGES
- STEPS
default: './'
- name: pythonRequirementsFile
type: string
description: 'The requirements file used in `buildTool: ''pip''` to populate
the build environment with the necessary dependencies'
scope:
- PARAMETERS
- STAGES
- STEPS
- name: autodetectClasspath
type: bool
description: Whether the classpath is automatically determined via build tool i.e. maven or pip or not at all
scope:
- PARAMETERS
- STAGES
- STEPS
default: true
- name: mustAuditIssueGroups
type: string
description: Comma separated list of issue groups that must be audited completely
scope:
- PARAMETERS
- STAGES
- STEPS
default: 'Corporate Security Requirements, Audit All'
- name: spotAuditIssueGroups
type: string
description: 'Comma separated list of issue groups that are spot checked and for which `spotCheckMinimum` audited issues are enforced'
scope:
- PARAMETERS
- STAGES
- STEPS
default: 'Spot Checks of Each Category'
- name: pythonRequirementsInstallSuffix
type: string
description: 'The suffix for the command used to install the requirements file in `buildTool: ''pip''` to populate
the build environment with the necessary dependencies'
scope:
- PARAMETERS
- STAGES
- STEPS
- name: pythonVersion
type: string
description: 'Python version to be used in `buildTool: ''pip''`'
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
default: python3
- name: uploadResults
type: bool
description: Whether results shall be uploaded or not
scope:
- PARAMETERS
- STAGES
- STEPS
default: true
- name: buildDescriptorFile
type: string
description: 'Path to the build descriptor file addressing the module/folder
to be scanned. Defaults are for buildTool=`maven`: `./pom.xml`, buildTool=`pip`:
`./setup.py`.'
scope:
- PARAMETERS
- STAGES
- STEPS
- name: commitId
description: 'Set the Git commit ID for identifying artifacts throughout the scan.'
resourceRef:
- name: commonPipelineEnvironment
param: git/commitId
scope:
- PARAMETERS
- STAGES
- STEPS
type: string
- name: commitMessage
description: 'Set the Git commit message for identifying pull request merges throughout the scan.'
resourceRef:
- name: commonPipelineEnvironment
param: git/commitMessage
scope:
- PARAMETERS
- STAGES
- STEPS
type: string
- name: githubApiUrl
description: Set the GitHub API url.
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
type: string
default: https://api.github.com
- name: owner
aliases:
- name: githubOrg
description: 'Set the GitHub organization.'
resourceRef:
- name: commonPipelineEnvironment
param: github/owner
scope:
- PARAMETERS
- STAGES
- STEPS
type: string
- name: repository
aliases:
- name: githubRepo
description: 'Set the GitHub repository.'
resourceRef:
- name: commonPipelineEnvironment
param: github/repository
scope:
- PARAMETERS
- STAGES
- STEPS
type: string
- name: memory
type: string
description: The amount of memory granted to the translate/scan executions
scope:
- PARAMETERS
- STAGES
- STEPS
default: -Xmx4G -Xms512M
- name: updateRulePack
type: bool
description: Whether the rule pack shall be updated and pulled from Fortify SSC before scanning or not
scope:
- PARAMETERS
- STAGES
- STEPS
default: true
- name: pythonExcludes
type: string
description: 'The excludes pattern used in `buildTool: ''pip''` for excluding
specific .py files i.e. tests'
scope:
- PARAMETERS
- STAGES
- STEPS
default: -exclude ./**/tests/**/*;./**/setup.py
deprecated: true
- name: reportDownloadEndpoint
aliases:
- name: fortifyReportDownloadEndpoint
type: string
description: Fortify SSC endpoint for Report downloads
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
default: /transfer/reportDownload.html
- name: pollingMinutes
type: int
description: The number of minutes for which an uploaded FPR artifact's status is being polled to finish queuing/processing, if exceeded polling will be stopped and an error will be thrown
scope:
- PARAMETERS
- STAGES
- STEPS
default: 30
- name: quickScan
type: bool
description: Whether a quick scan should be performed, please consult the related Fortify documentation on JAM on the impact of this setting
scope:
- PARAMETERS
- STAGES
- STEPS
default: false
- name: translate
type: string
description: JSON string of list of maps with required key `'src'`, and optional keys `'exclude'`, `'libDirs'`, `'aspnetcore'`, and `'dotNetCoreVersion'`
scope:
- PARAMETERS
- STAGES
- STEPS
- name: apiEndpoint
aliases:
- name: fortifyApiEndpoint
type: string
description: Fortify SSC endpoint used for uploading the scan results and checking the audit state
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
default: /api/v1
- name: reportType
type: string
description: The type of report to be generated
scope:
- PARAMETERS
- STAGES
- STEPS
default: PDF
- name: pythonAdditionalPath
type: string
description: 'The addional path which can be used in `buildTool: ''pip''` for
customization purposes'
scope:
- PARAMETERS
- STAGES
- STEPS
default: ./lib;.
deprecated: true
- name: artifactUrl
type: string
description: 'Path/Url pointing to an additional artifact repository for resolution of additional artifacts during the build'
scope:
- PARAMETERS
- STAGES
- STEPS
- name: considerSuspicious
type: bool
description: Whether suspicious issues should trigger the check to fail or not
scope:
- PARAMETERS
- STAGES
- STEPS
default: true
- name: fprUploadEndpoint
aliases:
- name: fortifyFprUploadEndpoint
type: string
description: Fortify SSC endpoint for FPR uploads
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
default: /upload/resultFileUpload.html
- name: projectName
aliases:
- name: fortifyProjectName
type: string
description: The project used for reporting results in SSC
scope:
- PARAMETERS
- STAGES
- STEPS
default: '{{list .GroupID .ArtifactID | join "-" | trimAll "-"}}'
- name: pythonIncludes
type: string
description: 'The includes pattern used in `buildTool: ''pip''` for including
.py files'
scope:
- PARAMETERS
- STAGES
- STEPS
default: ./**/*
deprecated: true
- name: reporting
type: bool
description: Influences whether a report is generated or not
scope:
- PARAMETERS
- STAGES
- STEPS
default: false
- name: serverUrl
aliases:
- name: fortifyServerUrl
- name: sscUrl
deprecated: true
type: string
description: Fortify SSC Url to be used for accessing the APIs
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
- name: buildDescriptorExcludeList
type: string
description: Build descriptor files to exclude modules from being scanned
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
default: []
- name: pullRequestMessageRegexGroup
type: int
description: The group number for extracting the pull request id in `pullRequestMessageRegex`
scope:
- PARAMETERS
- STAGES
- STEPS
default: 1
- name: deltaMinutes
type: int
description: The number of minutes for which an uploaded FPR artifact is considered to be recent and healthy, if exceeded an error will be thrown
scope:
- PARAMETERS
- STAGES
- STEPS
default: 5
- name: spotCheckMinimum
type: int
description: The minimum number of issues that must be audited per category in the `Spot Checks of each Category` folder to avoid an error being thrown
scope:
- PARAMETERS
- STAGES
- STEPS
default: 1
- name: fprDownloadEndpoint
aliases:
- name: fortifyFprDownloadEndpoint
type: string
description: Fortify SSC endpoint for FPR downloads
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
default: /download/currentStateFprDownload.html
- name: defaultVersioningModel
type: string
description: The default project versioning model used in case `projectVersion` parameter is empty for creating the version based on the build descriptor version to report results in SSC, can be one of `'major'`, `'major-minor'`, `'semantic'`, `'full'`
scope:
- PARAMETERS
- STAGES
- STEPS
default: 'major'
- name: pythonInstallCommand
type: string
description: 'Additional install command that can be run when `buildTool: ''pip''`
is used which allows further customizing the execution environment of the
scan'
scope:
- PARAMETERS
- STAGES
- STEPS
default: "{{.Pip}} install --user ."
- name: reportTemplateId
type: int
description: Report template ID to be used for generating the Fortify report
scope:
- PARAMETERS
- STAGES
- STEPS
default: 18
- name: filterSetTitle
type: string
description: Title of the filter set to use for analysing the results
scope:
- PARAMETERS
- STAGES
- STEPS
default: "SAP"
- name: pullRequestName
type: string
description: The name of the pull request branch which will trigger creation of a new version in Fortify SSC based on the master branch version
scope:
- PARAMETERS
- STAGES
- STEPS
- name: pullRequestMessageRegex
type: string
description: Regex used to identify the PR-XXX reference within the merge commit message
scope:
- PARAMETERS
- STAGES
- STEPS
default: '.*Merge pull request #(\\d+) from.*'
- name: buildTool
type: string
description: Scan type used for the step which can be `'maven'`, `'pip'`
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
default: maven
containers:
- image: ppiper/fortify
workingDir: /home/piper
outputs:
resources:
- name: influx
type: influx
params:
- name: fortify_data
fields:
- name: projectName
- name: projectVersion
- name: violations
- name: corporateTotal
- name: corporateAudited
- name: auditAllTotal
- name: auditAllAudited
- name: spotChecksTotal
- name: spotChecksAudited
- name: spotChecksGap
- name: suspicious
- name: exploitable
- name: suppressed

View File

@ -136,6 +136,7 @@ public class CommonStepsTest extends BasePiperTest{
'abapEnvironmentRunATCCheck', //implementing new golang pattern without fields 'abapEnvironmentRunATCCheck', //implementing new golang pattern without fields
'sonarExecuteScan', //implementing new golang pattern without fields 'sonarExecuteScan', //implementing new golang pattern without fields
'gctsCreateRepository', //implementing new golang pattern without fields 'gctsCreateRepository', //implementing new golang pattern without fields
'fortifyExecuteScan', //implementing new golang pattern without fields
'gctsDeploy', //implementing new golang pattern without fields 'gctsDeploy', //implementing new golang pattern without fields
] ]

View File

@ -0,0 +1,17 @@
import com.sap.piper.DownloadCacheUtils
import groovy.transform.Field
import static com.sap.piper.Prerequisites.checkScript
@Field String STEP_NAME = getClass().getName()
@Field String METADATA_FILE = 'metadata/fortify.yaml'
//Metadata maintained in file project://resources/metadata/fortify.yaml
void call(Map parameters = [:]) {
final script = checkScript(this, parameters) ?: this
parameters = DownloadCacheUtils.injectDownloadCacheInMavenParameters(script, parameters)
List credentials = [[type: 'token', id: 'fortifyCredentialsId', env: ['PIPER_authToken']]]
piperExecuteBin(parameters, STEP_NAME, METADATA_FILE, credentials)
}