diff options
Diffstat (limited to 'fprox.go')
| -rw-r--r-- | fprox.go | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/fprox.go b/fprox.go new file mode 100644 index 0000000..214ef9a --- /dev/null +++ b/fprox.go @@ -0,0 +1,257 @@ +package app + +import ( + "encoding/json" + "fmt" + "io" + "net/http" +) + +// CveHasExploits fetches the fprox report for the given CVE and reports +// whether it contains any proof-of-concept or exploitation artifacts. +func CveHasExploits(cve string) (bool, error) { + url := fmt.Sprintf("https://fprox.hrbrmstr.app/cve/%s", cve) + resp, err := http.Get(url) + if err != nil { + return false, err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return false, err + } + + r, err := UnmarshalFproxResponse(body) + if err != nil { + return false, err + } + + if len(r.Props.PageProps.CveInfo.ProofOfConcepts) > 0 { + return true, nil + } + + if len(r.Props.PageProps.CveInfo.ExploitedAt) > 0 { + return true, nil + } + + return false, nil +} + +func UnmarshalFproxResponse(data []byte) (FproxResponse, error) { + var r FproxResponse + err := json.Unmarshal(data, &r) + return r, err +} + +func (r *FproxResponse) Marshal() ([]byte, error) { + return json.Marshal(r) +} + +type FproxResponse struct { + Props Props `json:"props"` + Page string `json:"page"` + Query Query `json:"query"` + BuildID string `json:"buildId"` + AssetPrefix string `json:"assetPrefix"` + IsFallback bool `json:"isFallback"` + IsExperimentalCompile bool `json:"isExperimentalCompile"` + Gsp bool `json:"gsp"` + ScriptLoader []interface{} `json:"scriptLoader"` +} + +type Props struct { + PageProps PageProps `json:"pageProps"` + NSsg bool `json:"__N_SSG"` +} + +type PageProps struct { + AdvisoryEntry Entry `json:"advisoryEntry"` + Categories []Category `json:"categories"` + ChatterEntries []Entry `json:"chatterEntries"` + CveInfo CveInfo `json:"cveInfo"` + Events []Event `json:"events"` + GraphMarkup string `json:"graphMarkup"` + ReferenceEntries []interface{} `json:"referenceEntries"` + TotalChatterEntries int64 `json:"totalChatterEntries"` + TotalReferenceEntries int64 `json:"totalReferenceEntries"` +} + +type Entry struct { + Crawled int64 `json:"crawled"` + ID string `json:"id"` + Origin Origin `json:"origin"` + Title string `json:"title"` + Description string `json:"description"` + SourceLink string `json:"sourceLink"` + Visual *Visual `json:"visual,omitempty"` +} + +type Origin struct { + StreamID string `json:"streamId"` + Title string `json:"title"` + HTMLURL string `json:"htmlUrl"` +} + +type Visual struct { + URL string `json:"url"` + ContentType *string `json:"contentType,omitempty"` + Height *int64 `json:"height,omitempty"` + Processor *string `json:"processor,omitempty"` + Width *int64 `json:"width,omitempty"` +} + +type Category struct { + NumSimilarVulnerabilities int64 `json:"numSimilarVulnerabilities"` + Label string `json:"label"` + URI string `json:"uri"` +} + +type CveInfo struct { + Cveid string `json:"cveid"` + ID string `json:"id"` + Type string `json:"type"` + Label string `json:"label"` + HasSalience bool `json:"hasSalience"` + AdvisoryURL string `json:"advisoryUrl"` + CveStatus string `json:"cveStatus"` + Description string `json:"description"` + CvssCategoryEstimate string `json:"cvssCategoryEstimate"` + CvssV3 CvssV3 `json:"cvssV3"` + CweIDS []CweID `json:"cweIds"` + SmallGraphURL string `json:"smallGraphUrl"` + GraphURL string `json:"graphUrl"` + EpssScore string `json:"epssScore"` + PatchDetails []PatchDetail `json:"patchDetails"` + Patched bool `json:"patched"` + DetectedBy []DetectedBy `json:"detectedBy"` + FeedlyInsertedDate string `json:"feedlyInsertedDate"` + FeedlyUpdatedDate string `json:"feedlyUpdatedDate"` + PublishedDate string `json:"publishedDate"` + PublicationDateInfo []PublicationDateInfo `json:"publicationDateInfo"` + AffectedProductsEstimate []AffectedProductsEstimate `json:"affectedProductsEstimate"` + IDMapping []interface{} `json:"idMapping"` + Timeline []Timeline `json:"timeline"` + ExecutiveSummary ExecutiveSummary `json:"executiveSummary"` + Stats Stats `json:"stats"` + ExploitedAt []interface{} `json:"exploitedAt"` + ProofOfConcepts []interface{} `json:"proofOfConcepts"` + Products []VendorElement `json:"products"` + Vendors []VendorElement `json:"vendors"` +} + +type AffectedProductsEstimate struct { + Products []AffectedProductsEstimateProduct `json:"products"` + Vendor string `json:"vendor"` +} + +type AffectedProductsEstimateProduct struct { + Name string `json:"name"` +} + +type CvssV3 struct { + PrivilegesRequired string `json:"privilegesRequired"` + VectorString string `json:"vectorString"` + BaseScore float64 `json:"baseScore"` + Scope string `json:"scope"` + UserInteraction string `json:"userInteraction"` + ConfidentialityImpact string `json:"confidentialityImpact"` + AvailabilityImpact string `json:"availabilityImpact"` + AttackComplexity string `json:"attackComplexity"` + AttackVector string `json:"attackVector"` + Version string `json:"version"` + IntegrityImpact string `json:"integrityImpact"` +} + +type CweID struct { + CweID string `json:"cweID"` + Name string `json:"name"` +} + +type DetectedBy struct { + ScannerName string `json:"scannerName"` + DetectionID string `json:"detectionId"` +} + +type ExecutiveSummary struct { + Description string `json:"description"` + Patch string `json:"patch"` + Mitigation string `json:"mitigation"` + Exploitation string `json:"exploitation"` + Impact string `json:"impact"` +} + +type PatchDetail struct { + Title string `json:"title"` + Source string `json:"source"` + PatchAddedDate string `json:"patchAddedDate"` + URL string `json:"url"` + FeedlyPatchAddedDate string `json:"feedlyPatchAddedDate"` +} + +type VendorElement struct { + ID string `json:"id"` + Label string `json:"label"` + Origin string `json:"origin"` +} + +type PublicationDateInfo struct { + FeedlyUpdatedDate *string `json:"feedlyUpdatedDate,omitempty"` + Source string `json:"source"` + PublishedDate string `json:"publishedDate"` + FeedlyInsertedDate *string `json:"feedlyInsertedDate,omitempty"` + LastModifiedDate string `json:"lastModifiedDate"` +} + +type Stats struct { + FirstEntryID string `json:"firstEntryId"` + TimeSeries map[string][]TimeSery `json:"timeSeries"` + AdvisoryEntryID string `json:"advisoryEntryId"` + FirstTimestamp int64 `json:"firstTimestamp"` +} + +type TimeSery struct { + URL string `json:"url"` + FirstEntryID string `json:"firstEntryId"` + Age int64 `json:"age"` + Timestamp int64 `json:"timestamp"` + SourceType []string `json:"sourceType,omitempty"` +} + +type Timeline struct { + Event string `json:"event"` + Date string `json:"date"` + Source string `json:"source"` +} + +type Event struct { + ID string `json:"id"` + Type string `json:"type"` + Ts int64 `json:"ts"` + Updated int64 `json:"updated"` + EntryID *string `json:"entryId,omitempty"` + SourceName *string `json:"sourceName,omitempty"` + CvssCategoryEstimate *string `json:"cvssCategoryEstimate,omitempty"` + Score *float64 `json:"score,omitempty"` + Update *bool `json:"update,omitempty"` + Percentile *float64 `json:"percentile,omitempty"` + URL *string `json:"url,omitempty"` + VendorName *string `json:"vendorName,omitempty"` + ModuleID *string `json:"moduleId,omitempty"` + ScannerName *string `json:"scannerName,omitempty"` + GroupedEvents []GroupedEvent `json:"groupedEvents,omitempty"` + AdvisoryID *string `json:"advisoryId,omitempty"` +} + +type GroupedEvent struct { + ID string `json:"id"` + Type string `json:"type"` + Ts int64 `json:"ts"` + Updated int64 `json:"updated"` + ModuleID string `json:"moduleId"` + ScannerName string `json:"scannerName"` +} + +type Query struct { + CveID string `json:"cveId"` +} |
