Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: (backend): API to list scan fields and add custom fields in Jira #1602

Merged
merged 2 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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