Skip to content

Commit

Permalink
feat: (backend): API to list scan fields and add custom fields in Jira (
Browse files Browse the repository at this point in the history
#1602)

* feat: (backend): API to list scan fields and add custom fields in Jira
* put /deepfence/scan/results/fields under common in swagger
  • Loading branch information
Harshvardhan Karn authored Sep 21, 2023
1 parent 94b44ef commit d719113
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 8 deletions.
4 changes: 4 additions & 0 deletions deepfence_server/apiDocs/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,10 @@ func (d *OpenApiDocs) AddIngestersOperations() {
}

func (d *OpenApiDocs) AddScansOperations() {
// List scan result fields
d.AddOperation("getScanReportFields", http.MethodGet, "/deepfence/scan/results/fields",
"Get Scan Report Fields", "Get all the fields available in all the scan reports",
http.StatusOK, []string{tagCommon}, bearerToken, nil, new(ScanReportFieldsResponse))
// Start scan
d.AddOperation("startVulnerabilityScan", http.MethodPost, "/deepfence/scan/start/vulnerability",
"Start Vulnerability Scan", "Start Vulnerability Scan on agent or registry",
Expand Down
56 changes: 56 additions & 0 deletions deepfence_server/handler/scan_reports.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"net/http"
"net/url"
"path"
"reflect"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -951,6 +952,61 @@ func (h *Handler) listScansHandler(w http.ResponseWriter, r *http.Request, scan_
httpext.JSON(w, http.StatusOK, infos)
}

func (h *Handler) GetScanReportFields(w http.ResponseWriter, r *http.Request) {
// iterate over empty struct "model.Vulnerability" fields
// and push the field json tag name to an array
vulnerabilityFields := []string{}
vulnerability := model.Vulnerability{}
vulnerabilityType := reflect.TypeOf(vulnerability)
for i := 0; i < vulnerabilityType.NumField(); i++ {
fieldString := vulnerabilityType.Field(i).Tag.Get("json")
fields := strings.Split(fieldString, ",")
vulnerabilityFields = append(vulnerabilityFields, fields[0])
}

// iterate over empty struct "model.Secret" fields
// and push the field json tag name to an array
secretFields := []string{}
secret := model.Secret{}
secretType := reflect.TypeOf(secret)
for i := 0; i < secretType.NumField(); i++ {
fieldString := vulnerabilityType.Field(i).Tag.Get("json")
fields := strings.Split(fieldString, ",")
secretFields = append(secretFields, fields[0])
}

// iterate over empty struct "model.Compliance" fields
// and push the field json tag name to an array
complianceFields := []string{}
compliance := model.Compliance{}
complianceType := reflect.TypeOf(compliance)
for i := 0; i < complianceType.NumField(); i++ {
fieldString := vulnerabilityType.Field(i).Tag.Get("json")
fields := strings.Split(fieldString, ",")
complianceFields = append(complianceFields, fields[0])
}

// iterate over empty struct "model.Malware" fields
// and push the field json tag name to an array
malwareFields := []string{}
malware := model.Malware{}
malwareType := reflect.TypeOf(malware)
for i := 0; i < malwareType.NumField(); i++ {
fieldString := vulnerabilityType.Field(i).Tag.Get("json")
fields := strings.Split(fieldString, ",")
malwareFields = append(malwareFields, fields[0])
}

response := model.ScanReportFieldsResponse{
Vulnerability: vulnerabilityFields,
Secret: secretFields,
Compliance: complianceFields,
Malware: malwareFields,
}

httpext.JSON(w, http.StatusOK, response)
}

func (h *Handler) ListVulnerabilityScanResultsHandler(w http.ResponseWriter, r *http.Request) {
entries, common, err := listScanResultsHandler[model.Vulnerability](w, r, utils.NEO4J_VULNERABILITY_SCAN)
if err != nil {
Expand Down
7 changes: 7 additions & 0 deletions deepfence_server/model/scans.go
Original file line number Diff line number Diff line change
Expand Up @@ -574,3 +574,10 @@ func (v CloudCompliance) GetCategory() string {
func (CloudCompliance) GetJsonCategory() string {
return "severity"
}

type ScanReportFieldsResponse struct {
Vulnerability []string `json:"vulnerability"`
Secret []string `json:"secret"`
Malware []string `json:"malware"`
Compliance []string `json:"compliance"`
}
29 changes: 29 additions & 0 deletions deepfence_server/pkg/integration/jira/jira.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,35 @@ func (j Jira) SendNotification(ctx context.Context, message string, extras map[s
}
log.Info().Msgf("jira issue created id %s link %s", issue.ID, issue.Self)

// parse message in case of custom fields
var msgWithCustomFields []map[string]interface{}
if len(j.Config.CustomFields) > 0 {
var msg []map[string]interface{}
err = json.Unmarshal([]byte(message), &msg)
if err != nil {
log.Error().Msgf(err.Error())
return err
}

for _, m := range msg {
customFields := make(map[string]interface{})
for _, f := range j.Config.CustomFields {
if value, ok := m[f]; ok {
customFields[f] = value
}
}
msgWithCustomFields = append(msgWithCustomFields, customFields)
}

finalByte, err := json.MarshalIndent(msgWithCustomFields, "", " ")
if err != nil {
log.Error().Msgf(err.Error())
return err
}

message = string(finalByte)
}

attachment, resp, err := client.Issue.PostAttachment(issue.ID, strings.NewReader(message), "scan-results.json")
if err != nil {
log.Error().Msgf(err.Error())
Expand Down
17 changes: 9 additions & 8 deletions deepfence_server/pkg/integration/jira/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ type Jira struct {
}

type Config struct {
JiraSiteUrl string `json:"jiraSiteUrl" validate:"required,url" required:"true"`
Username string `json:"username" validate:"required,min=1" required:"true"`
Password string `json:"password" validate:"omitempty,min=8,max=100,jira_auth_key"`
JiraProjectKey string `json:"jiraProjectKey" validate:"required,min=1" required:"true"`
JiraAssignee string `json:"jiraAssignee" validate:"omitempty,min=1"`
IssueType string `json:"issueType" validate:"required,min=1" required:"true"`
IsAuthToken bool `json:"isAuthToken" validate:"required" required:"true"`
APIToken string `json:"api_token" validate:"omitempty,min=32,max=300,jira_auth_key"`
JiraSiteUrl string `json:"jiraSiteUrl" validate:"required,url" required:"true"`
Username string `json:"username" validate:"required,min=1" required:"true"`
Password string `json:"password" validate:"omitempty,min=8,max=100,jira_auth_key"`
JiraProjectKey string `json:"jiraProjectKey" validate:"required,min=1" required:"true"`
JiraAssignee string `json:"jiraAssignee" validate:"omitempty,min=1"`
IssueType string `json:"issueType" validate:"required,min=1" required:"true"`
IsAuthToken bool `json:"isAuthToken" validate:"required" required:"true"`
APIToken string `json:"api_token" validate:"omitempty,min=32,max=300,jira_auth_key"`
CustomFields []string `json:"custom_fields"`
}

func (j Jira) ValidateConfig(validate *validator.Validate) error {
Expand Down
2 changes: 2 additions & 0 deletions deepfence_server/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,8 @@ func SetupRoutes(r *chi.Mux, serverPort string, serveOpenapiDocs bool, ingestC c
r.Post("/cloud-compliance", dfHandler.AuthHandler(ResourceScanReport, PermissionRead, dfHandler.ListCloudComplianceScansHandler))
})
r.Route("/scan/results", func(r chi.Router) {
r.Get("/fields", dfHandler.AuthHandler(ResourceScanReport, PermissionRead, dfHandler.GetScanReportFields))

r.Post("/vulnerability", dfHandler.AuthHandler(ResourceScanReport, PermissionRead, dfHandler.ListVulnerabilityScanResultsHandler))

r.Route("/secret", func(r chi.Router) {
Expand Down

0 comments on commit d719113

Please sign in to comment.