Skip to content

Commit 1fc9bd2

Browse files
authored
Send detected secrets by email (#438)
1 parent 55c9196 commit 1fc9bd2

37 files changed

+1229
-253
lines changed

commands/scanpullrequest/scanpullrequest.go

+76-31
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func verifyGitHubFrogbotEnvironment(client vcsclient.VcsClient, repoConfig *util
8282
// a. Audit the dependencies of the source and the target branches.
8383
// b. Compare the vulnerabilities found in source and target branches, and show only the new vulnerabilities added by the pull request.
8484
// Otherwise, only the source branch is scanned and all found vulnerabilities are being displayed.
85-
func scanPullRequest(repo *utils.Repository, client vcsclient.VcsClient) error {
85+
func scanPullRequest(repo *utils.Repository, client vcsclient.VcsClient) (err error) {
8686
pullRequestDetails := repo.PullRequestDetails
8787
log.Info(fmt.Sprintf("Scanning Pull Request #%d (from source branch: <%s/%s/%s> to target branch: <%s/%s/%s>)",
8888
pullRequestDetails.ID,
@@ -91,33 +91,47 @@ func scanPullRequest(repo *utils.Repository, client vcsclient.VcsClient) error {
9191
log.Info("-----------------------------------------------------------")
9292

9393
// Audit PR code
94-
vulnerabilitiesRows, iacRows, err := auditPullRequest(repo, client, pullRequestDetails)
94+
vulnerabilitiesRows, iacRows, secretsRows, err := auditPullRequest(repo, client, pullRequestDetails)
9595
if err != nil {
96-
return err
96+
return
97+
}
98+
99+
shouldSendExposedSecretsEmail := len(secretsRows) > 0 && repo.SmtpServer != ""
100+
if shouldSendExposedSecretsEmail {
101+
prSourceDetails := pullRequestDetails.Source
102+
secretsEmailDetails := utils.NewSecretsEmailDetails(
103+
client, repo.GitProvider,
104+
prSourceDetails.Owner, prSourceDetails.Repository,
105+
prSourceDetails.Name, pullRequestDetails.URL,
106+
secretsRows, repo.EmailDetails)
107+
if err = utils.AlertSecretsExposed(secretsEmailDetails); err != nil {
108+
return
109+
}
97110
}
98111

99112
// Delete previous Frogbot pull request message if exists
100113
if err = deleteExistingPullRequestComment(repo, client); err != nil {
101-
return err
114+
return
102115
}
103116

104117
// Create a pull request message
105118
message := createPullRequestMessage(vulnerabilitiesRows, iacRows, repo.OutputWriter)
106119

107120
// Add comment to the pull request
108121
if err = client.AddPullRequestComment(context.Background(), repo.RepoOwner, repo.RepoName, message, int(pullRequestDetails.ID)); err != nil {
109-
return errors.New("couldn't add pull request comment: " + err.Error())
122+
err = errors.New("couldn't add pull request comment: " + err.Error())
123+
return
110124
}
111125

112126
// Fail the Frogbot task if a security issue is found and Frogbot isn't configured to avoid the failure.
113127
if repo.FailOnSecurityIssues != nil && *repo.FailOnSecurityIssues && len(vulnerabilitiesRows) > 0 {
114128
err = errors.New(securityIssueFoundErr)
115129
}
116-
return err
130+
return
117131
}
118132

119133
// Downloads Pull Requests branches code and audits them
120-
func auditPullRequest(repoConfig *utils.Repository, client vcsclient.VcsClient, pullRequestDetails vcsclient.PullRequestInfo) (vulnerabilitiesRows []formats.VulnerabilityOrViolationRow, iacRows []formats.IacSecretsRow, err error) {
134+
func auditPullRequest(repoConfig *utils.Repository, client vcsclient.VcsClient, pullRequestDetails vcsclient.PullRequestInfo) (vulnerabilitiesRows []formats.VulnerabilityOrViolationRow, iacRows []formats.IacSecretsRow, secretsRows []formats.IacSecretsRow, err error) {
121135
// Download source branch
122136
sourceBranchInfo := pullRequestDetails.Source
123137
sourceBranchWd, cleanupSource, err := utils.DownloadRepoToTempDir(client, sourceBranchInfo.Owner, sourceBranchInfo.Repository, sourceBranchInfo.Name)
@@ -157,8 +171,8 @@ func auditPullRequest(repoConfig *utils.Repository, client vcsclient.VcsClient,
157171
}
158172

159173
// Set JAS output flags
160-
extendedScanResults := sourceResults.ExtendedScanResults
161-
repoConfig.OutputWriter.SetJasOutputFlags(extendedScanResults.EntitledForJas, len(extendedScanResults.ApplicabilityScanResults) > 0)
174+
sourceScanResults := sourceResults.ExtendedScanResults
175+
repoConfig.OutputWriter.SetJasOutputFlags(sourceScanResults.EntitledForJas, len(sourceScanResults.ApplicabilityScanResults) > 0)
162176

163177
// Get all issues that were found in the source branch
164178
if repoConfig.IncludeAllVulnerabilities {
@@ -169,7 +183,8 @@ func auditPullRequest(repoConfig *utils.Repository, client vcsclient.VcsClient,
169183
return
170184
}
171185
vulnerabilitiesRows = append(vulnerabilitiesRows, allIssuesRows...)
172-
iacRows = append(iacRows, xrayutils.PrepareIacs(sourceResults.ExtendedScanResults.IacScanResults)...)
186+
iacRows = append(iacRows, xrayutils.PrepareIacs(sourceScanResults.IacScanResults)...)
187+
secretsRows = append(secretsRows, xrayutils.PrepareSecrets(sourceScanResults.SecretsScanResults)...)
173188
continue
174189
}
175190

@@ -180,35 +195,62 @@ func auditPullRequest(repoConfig *utils.Repository, client vcsclient.VcsClient,
180195
if err != nil {
181196
return
182197
}
183-
var newIssuesRows []formats.VulnerabilityOrViolationRow
184-
newIssuesRows, err = createNewIssuesRows(targetResults, sourceResults)
185-
if err != nil {
198+
199+
// Get new issues
200+
var newVulnerabilities []formats.VulnerabilityOrViolationRow
201+
var newIacs, newSecrets []formats.IacSecretsRow
202+
if newVulnerabilities, newIacs, newSecrets, err = getNewIssues(targetResults, sourceResults); err != nil {
186203
return
187204
}
188-
vulnerabilitiesRows = append(vulnerabilitiesRows, newIssuesRows...)
189-
iacRows = append(iacRows, createNewIacRows(targetResults.ExtendedScanResults.IacScanResults, sourceResults.ExtendedScanResults.IacScanResults)...)
205+
vulnerabilitiesRows = append(vulnerabilitiesRows, newVulnerabilities...)
206+
iacRows = append(iacRows, newIacs...)
207+
secretsRows = append(secretsRows, newSecrets...)
190208
}
191209
return
192210
}
193211

194-
func createNewIacRows(targetIacResults, sourceIacResults []xrayutils.IacOrSecretResult) []formats.IacSecretsRow {
195-
targetIacRows := xrayutils.PrepareIacs(targetIacResults)
196-
sourceIacRows := xrayutils.PrepareIacs(sourceIacResults)
197-
targetIacVulnerabilitiesKeys := datastructures.MakeSet[string]()
198-
for _, row := range targetIacRows {
199-
targetIacVulnerabilitiesKeys.Add(row.File + row.Text)
200-
}
201-
var addedIacVulnerabilities []formats.IacSecretsRow
202-
for _, row := range sourceIacRows {
203-
if !targetIacVulnerabilitiesKeys.Exists(row.File + row.Text) {
204-
addedIacVulnerabilities = append(addedIacVulnerabilities, row)
212+
func getNewIssues(targetResults, sourceResults *audit.Results) ([]formats.VulnerabilityOrViolationRow, []formats.IacSecretsRow, []formats.IacSecretsRow, error) {
213+
var newVulnerabilities []formats.VulnerabilityOrViolationRow
214+
var err error
215+
if len(sourceResults.ExtendedScanResults.XrayResults) > 0 {
216+
if newVulnerabilities, err = createNewVulnerabilitiesRows(targetResults, sourceResults); err != nil {
217+
return nil, nil, nil, err
218+
}
219+
}
220+
221+
var newIacs []formats.IacSecretsRow
222+
if len(sourceResults.ExtendedScanResults.IacScanResults) > 0 {
223+
targetIacRows := xrayutils.PrepareIacs(targetResults.ExtendedScanResults.IacScanResults)
224+
sourceIacRows := xrayutils.PrepareIacs(sourceResults.ExtendedScanResults.IacScanResults)
225+
newIacs = createNewIacOrSecretsRows(targetIacRows, sourceIacRows)
226+
}
227+
228+
var newSecrets []formats.IacSecretsRow
229+
if len(sourceResults.ExtendedScanResults.SecretsScanResults) > 0 {
230+
targetSecretsRows := xrayutils.PrepareSecrets(targetResults.ExtendedScanResults.SecretsScanResults)
231+
sourceSecretsRows := xrayutils.PrepareSecrets(sourceResults.ExtendedScanResults.SecretsScanResults)
232+
newSecrets = createNewIacOrSecretsRows(targetSecretsRows, sourceSecretsRows)
233+
}
234+
235+
return newVulnerabilities, newIacs, newSecrets, nil
236+
}
237+
238+
func createNewIacOrSecretsRows(targetResults, sourceResults []formats.IacSecretsRow) []formats.IacSecretsRow {
239+
targetIacOrSecretsVulnerabilitiesKeys := datastructures.MakeSet[string]()
240+
for _, row := range targetResults {
241+
targetIacOrSecretsVulnerabilitiesKeys.Add(row.File + row.Text)
242+
}
243+
var addedIacOrSecretsVulnerabilities []formats.IacSecretsRow
244+
for _, row := range sourceResults {
245+
if !targetIacOrSecretsVulnerabilitiesKeys.Exists(row.File + row.Text) {
246+
addedIacOrSecretsVulnerabilities = append(addedIacOrSecretsVulnerabilities, row)
205247
}
206248
}
207-
return addedIacVulnerabilities
249+
return addedIacOrSecretsVulnerabilities
208250
}
209251

210252
// Create vulnerabilities rows. The rows should contain only the new issues added by this PR
211-
func createNewIssuesRows(targetResults, sourceResults *audit.Results) (vulnerabilitiesRows []formats.VulnerabilityOrViolationRow, err error) {
253+
func createNewVulnerabilitiesRows(targetResults, sourceResults *audit.Results) (vulnerabilitiesRows []formats.VulnerabilityOrViolationRow, err error) {
212254
targetScanAggregatedResults := aggregateScanResults(targetResults.ExtendedScanResults.XrayResults)
213255
sourceScanAggregatedResults := aggregateScanResults(sourceResults.ExtendedScanResults.XrayResults)
214256

@@ -305,9 +347,12 @@ func createPullRequestMessage(vulnerabilitiesRows []formats.VulnerabilityOrViola
305347

306348
func deleteExistingPullRequestComment(repository *utils.Repository, client vcsclient.VcsClient) error {
307349
log.Debug("Looking for an existing Frogbot pull request comment. Deleting it if it exists...")
308-
comments, err := utils.GetSortedPullRequestComments(client, repository.RepoOwner, repository.RepoName, int(repository.PullRequestDetails.ID))
350+
prDetails := repository.PullRequestDetails
351+
comments, err := utils.GetSortedPullRequestComments(client, prDetails.Target.Owner, prDetails.Target.Repository, int(prDetails.ID))
309352
if err != nil {
310-
return err
353+
return fmt.Errorf(
354+
"failed to get comments. the following details were used in order to fetch the comments: <%s/%s> pull request #%d. the error received: %s",
355+
repository.RepoOwner, repository.RepoName, int(repository.PullRequestDetails.ID), err.Error())
311356
}
312357

313358
commentID := frogbotCommentNotFound
@@ -320,7 +365,7 @@ func deleteExistingPullRequestComment(repository *utils.Repository, client vcscl
320365
}
321366

322367
if commentID != frogbotCommentNotFound {
323-
err = client.DeletePullRequestComment(context.Background(), repository.RepoOwner, repository.RepoName, int(repository.PullRequestDetails.ID), commentID)
368+
err = client.DeletePullRequestComment(context.Background(), prDetails.Target.Owner, prDetails.Target.Repository, int(prDetails.ID), commentID)
324369
}
325370

326371
return err

0 commit comments

Comments
 (0)