code-intelligence.com/cifuzz@v0.40.0/internal/api/finding.go (about) 1 package api 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "net/url" 8 "time" 9 10 "github.com/pkg/errors" 11 12 "code-intelligence.com/cifuzz/pkg/finding" 13 "code-intelligence.com/cifuzz/pkg/log" 14 ) 15 16 type Findings struct { 17 Findings []Finding `json:"findings"` 18 } 19 20 type Finding struct { 21 Name string `json:"name"` 22 DisplayName string `json:"display_name"` 23 FuzzTarget string `json:"fuzz_target"` 24 FuzzingRun string `json:"fuzzing_run"` 25 CampaignRun string `json:"campaign_run"` 26 ErrorReport ErrorReport `json:"error_report"` 27 Timestamp string `json:"timestamp"` 28 } 29 30 type ErrorReport struct { 31 Logs []string `json:"logs"` 32 Details string `json:"details"` 33 Type string `json:"type,omitempty"` 34 InputData []byte `json:"input_data,omitempty"` 35 36 DebuggingInfo *DebuggingInfo `json:"debugging_info,omitempty"` 37 HumanReadableInput string `json:"human_readable_input,omitempty"` 38 MoreDetails *finding.ErrorDetails `json:"more_details,omitempty"` 39 Tag string `json:"tag,omitempty"` 40 ShortDescription string `json:"short_description,omitempty"` 41 } 42 43 type DebuggingInfo struct { 44 ExecutablePath string `json:"executable_path,omitempty"` 45 RunArguments []string `json:"run_arguments,omitempty"` 46 BreakPoints []BreakPoint `json:"break_points,omitempty"` 47 Environment []Environment `json:"environment,omitempty"` 48 } 49 50 type BreakPoint struct { 51 SourceFilePath string `json:"source_file_path,omitempty"` 52 Location *FindingLocation `json:"location,omitempty"` 53 Function string `json:"function,omitempty"` 54 } 55 56 type FindingLocation struct { 57 Line uint32 `json:"line,omitempty"` 58 Column uint32 `json:"column,omitempty"` 59 } 60 61 type Environment struct { 62 Name string `json:"name,omitempty"` 63 Value string `json:"value,omitempty"` 64 } 65 66 type Severity struct { 67 Description string `json:"description,omitempty"` 68 Score float32 `json:"score,omitempty"` 69 } 70 71 func (client *APIClient) UploadFinding(project string, fuzzTarget string, campaignRunName string, fuzzingRunName string, finding *finding.Finding, token string) error { 72 // loop through the stack trace and create a list of breakpoints 73 breakPoints := []BreakPoint{} 74 for _, stackFrame := range finding.StackTrace { 75 breakPoints = append(breakPoints, BreakPoint{ 76 SourceFilePath: stackFrame.SourceFile, 77 Location: &FindingLocation{ 78 Line: stackFrame.Line, 79 Column: stackFrame.Column, 80 }, 81 Function: stackFrame.Function, 82 }) 83 } 84 85 errorDetails, err := client.GetErrorDetails(token) 86 if err != nil { 87 var connErr *ConnectionError 88 if !errors.As(err, &connErr) { 89 return err 90 } else { 91 log.Warn("Connection to API failed. Skipping error details.") 92 log.Debugf("Connection error: %v (continuing gracefully)", err) 93 return nil 94 } 95 } 96 97 finding.EnhanceWithErrorDetails(&errorDetails) 98 99 findings := &Findings{ 100 Findings: []Finding{ 101 { 102 Name: project + "/findings/cifuzz-" + finding.Name, 103 DisplayName: finding.Name, 104 FuzzTarget: fuzzTarget, 105 FuzzingRun: fuzzingRunName, 106 CampaignRun: campaignRunName, 107 ErrorReport: ErrorReport{ 108 Logs: finding.Logs, 109 Details: finding.Details, 110 Type: string(finding.Type), 111 InputData: finding.InputData, 112 DebuggingInfo: &DebuggingInfo{ 113 BreakPoints: breakPoints, 114 }, 115 MoreDetails: finding.MoreDetails, 116 Tag: fmt.Sprint(finding.Tag), 117 ShortDescription: finding.ShortDescription(), 118 }, 119 Timestamp: time.Now().Format(time.RFC3339), 120 }, 121 }, 122 } 123 124 body, err := json.Marshal(findings) 125 if err != nil { 126 return errors.WithStack(err) 127 } 128 129 url, err := url.JoinPath("/v1", project, "findings") 130 if err != nil { 131 return errors.WithStack(err) 132 } 133 resp, err := client.sendRequest("POST", url, bytes.NewReader(body), token) 134 if err != nil { 135 return err 136 } 137 defer resp.Body.Close() 138 139 if resp.StatusCode != 200 { 140 return responseToAPIError(resp) 141 } 142 143 return nil 144 }