package handlers import ( "net/http" "strconv" "github.com/go-chi/chi/v5" "xorm.io/xorm" "github.com/forgeo/forgebucket/internal/domain/vulnscan" "github.com/forgeo/forgebucket/internal/models" ) type VulnScanHandler struct { db *xorm.Engine scanner *vulnscan.Scanner } func NewVulnScanHandler(db *xorm.Engine, scanner *vulnscan.Scanner) *VulnScanHandler { return &VulnScanHandler{db: db, scanner: scanner} } // List returns all active vulnerability findings for a repo. // GET /api/v1/repos/{owner}/{repo}/vulnerabilities func (h *VulnScanHandler) List(w http.ResponseWriter, r *http.Request) { repoID, ok := resolveRepoID(h.db, w, r) if !ok { return } findings, err := h.scanner.ListFindings(repoID) if err != nil { jsonError(w, "database error", http.StatusInternalServerError) return } jsonOK(w, findings) } // Scan triggers a full vulnerability scan against the latest SBOM. // POST /api/v1/repos/{owner}/{repo}/vulnerabilities/scan func (h *VulnScanHandler) Scan(w http.ResponseWriter, r *http.Request) { repoID, ok := resolveRepoID(h.db, w, r) if !ok { return } findings, err := h.scanner.ScanSBOM(repoID) if err != nil { jsonError(w, "scan failed: "+err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusCreated) jsonOK(w, findings) } // Dismiss acknowledges a vulnerability finding. // POST /api/v1/repos/{owner}/{repo}/vulnerabilities/{findingID}/dismiss func (h *VulnScanHandler) Dismiss(w http.ResponseWriter, r *http.Request) { repoID, ok := resolveRepoID(h.db, w, r) if !ok { return } _ = repoID findingID, err := strconv.ParseInt(chi.URLParam(r, "findingID"), 10, 64) if err != nil { jsonError(w, "invalid finding ID", http.StatusBadRequest) return } username := r.Context().Value("user").(string) if err := h.scanner.DismissFindings(findingID, username); err != nil { jsonError(w, err.Error(), http.StatusNotFound) return } jsonOK(w, map[string]string{"status": "dismissed"}) } // ListAll returns active findings across all repos. // GET /api/v1/vulnerabilities func (h *VulnScanHandler) ListAll(w http.ResponseWriter, r *http.Request) { var findings []models.VulnerabilityFinding if err := h.db.Where("dismissed = ?", false). OrderBy("cvss_score DESC, detected_at DESC").Find(&findings); err != nil { jsonError(w, "database error", http.StatusInternalServerError) return } if findings == nil { findings = []models.VulnerabilityFinding{} } jsonOK(w, findings) }