aboutsummaryrefslogtreecommitdiff
path: root/fprox.go
diff options
context:
space:
mode:
Diffstat (limited to 'fprox.go')
-rw-r--r--fprox.go257
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"`
+}