// Package api provides HTTP handlers for the Security Scanner package api import ( "net/http" "github.com/breakpilot/compliance-sdk/services/security-scanner/internal/scanner" "github.com/gin-gonic/gin" ) // ScanRequest represents a scan request type ScanRequest struct { Tools []string `json:"tools"` TargetPath string `json:"target_path"` ExcludePaths []string `json:"exclude_paths"` } // StartScan starts a new security scan func StartScan(manager *scanner.Manager) gin.HandlerFunc { return func(c *gin.Context) { var req ScanRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } tools := req.Tools if len(tools) == 0 { tools = manager.AvailableTools() } scan := manager.StartScan(tools, req.TargetPath) c.JSON(http.StatusAccepted, gin.H{ "scan_id": scan.ID, "status": scan.Status, "tools": scan.Tools, "started_at": scan.StartedAt, }) } } // GetScanStatus returns the status of a scan func GetScanStatus(manager *scanner.Manager) gin.HandlerFunc { return func(c *gin.Context) { scanID := c.Param("scanId") scan := manager.GetScan(scanID) if scan == nil { c.JSON(http.StatusNotFound, gin.H{"error": "Scan not found"}) return } c.JSON(http.StatusOK, gin.H{ "scan_id": scan.ID, "status": scan.Status, "started_at": scan.StartedAt, "completed_at": scan.CompletedAt, "summary": scan.Summary, }) } } // GetScanResults returns the results of a scan func GetScanResults(manager *scanner.Manager) gin.HandlerFunc { return func(c *gin.Context) { scanID := c.Param("scanId") scan := manager.GetScan(scanID) if scan == nil { c.JSON(http.StatusNotFound, gin.H{"error": "Scan not found"}) return } c.JSON(http.StatusOK, scan) } } // GetFindings returns all findings func GetFindings(manager *scanner.Manager) gin.HandlerFunc { return func(c *gin.Context) { findings := manager.GetAllFindings() severity := c.Query("severity") tool := c.Query("tool") status := c.Query("status") // Filter findings filtered := []scanner.Finding{} for _, f := range findings { if severity != "" && f.Severity != severity { continue } if tool != "" && f.Tool != tool { continue } if status != "" && f.Status != status { continue } filtered = append(filtered, f) } c.JSON(http.StatusOK, gin.H{ "findings": filtered, "total": len(filtered), }) } } // GetFinding returns a specific finding func GetFinding(manager *scanner.Manager) gin.HandlerFunc { return func(c *gin.Context) { findingID := c.Param("findingId") findings := manager.GetAllFindings() for _, f := range findings { if f.ID == findingID { c.JSON(http.StatusOK, f) return } } c.JSON(http.StatusNotFound, gin.H{"error": "Finding not found"}) } } // UpdateFindingStatus updates the status of a finding func UpdateFindingStatus(manager *scanner.Manager) gin.HandlerFunc { return func(c *gin.Context) { findingID := c.Param("findingId") var req struct { Status string `json:"status" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if manager.UpdateFindingStatus(findingID, req.Status) { c.JSON(http.StatusOK, gin.H{ "id": findingID, "status": req.Status, }) } else { c.JSON(http.StatusNotFound, gin.H{"error": "Finding not found"}) } } } // GenerateSBOM generates a Software Bill of Materials func GenerateSBOM(manager *scanner.Manager) gin.HandlerFunc { return func(c *gin.Context) { var req struct { TargetPath string `json:"target_path"` } c.ShouldBindJSON(&req) sbom := manager.GenerateSBOM(req.TargetPath) c.JSON(http.StatusOK, sbom) } } // GetSBOM returns an SBOM by ID func GetSBOM(manager *scanner.Manager) gin.HandlerFunc { return func(c *gin.Context) { // In production, retrieve from storage sbom := manager.GenerateSBOM("") c.JSON(http.StatusOK, sbom) } } // ExportSBOM exports an SBOM in the specified format func ExportSBOM(manager *scanner.Manager) gin.HandlerFunc { return func(c *gin.Context) { format := c.Param("format") sbom := manager.GenerateSBOM("") sbom.Format = format var contentType string switch format { case "cyclonedx": contentType = "application/vnd.cyclonedx+json" case "spdx": contentType = "application/spdx+json" default: c.JSON(http.StatusBadRequest, gin.H{"error": "Unsupported format"}) return } c.Header("Content-Type", contentType) c.Header("Content-Disposition", "attachment; filename=sbom."+format+".json") c.JSON(http.StatusOK, sbom) } } // GetRecommendations returns security recommendations func GetRecommendations(manager *scanner.Manager) gin.HandlerFunc { return func(c *gin.Context) { recs := manager.GetRecommendations() c.JSON(http.StatusOK, gin.H{ "recommendations": recs, "total": len(recs), }) } } // GetToolsStatus returns the status of all tools func GetToolsStatus(manager *scanner.Manager) gin.HandlerFunc { return func(c *gin.Context) { tools := manager.GetTools() c.JSON(http.StatusOK, gin.H{"tools": tools}) } }