Files
core/pkg/adapters/adapters.go

148 lines
4.2 KiB
Go
Raw Permalink Normal View History

2026-04-02 10:57:36 -04:00
package adapters
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"log"
"net/http"
"strconv"
"strings"
domain2 "epigas.gitea.cloud/RiskRancher/core/pkg/domain"
)
func (h *Handler) HandleGetAdapters(w http.ResponseWriter, r *http.Request) {
adapters, err := h.Store.GetAdapters(r.Context())
if err != nil {
http.Error(w, "Database error", http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(adapters)
}
func (h *Handler) HandleCreateAdapter(w http.ResponseWriter, r *http.Request) {
var adapter domain2.Adapter
if err := json.NewDecoder(r.Body).Decode(&adapter); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
if err := h.Store.SaveAdapter(r.Context(), adapter); err != nil {
http.Error(w, "Failed to save adapter", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusCreated)
}
func (h *Handler) HandleDeleteAdapter(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "Invalid adapter ID", http.StatusBadRequest)
return
}
if err := h.Store.DeleteAdapter(r.Context(), id); err != nil {
http.Error(w, "Failed to delete adapter", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusNoContent)
}
func getJSONValue(data interface{}, path string) interface{} {
if path == "" || path == "." {
return data // The root IS the array
}
keys := strings.Split(path, ".")
current := data
for _, key := range keys {
if m, ok := current.(map[string]interface{}); ok {
current = m[key]
} else {
return nil // Path broke
}
}
return current
}
func interfaceToString(val interface{}) string {
if val == nil {
return ""
}
if str, ok := val.(string); ok {
return str
}
return "" // Could expand this to handle ints/floats if needed
}
// HandleAdapterIngest dynamically maps deeply nested JSON arrays into Tickets
func (h *Handler) HandleAdapterIngest(w http.ResponseWriter, r *http.Request) {
adapterName := r.PathValue("name")
adapter, err := h.Store.GetAdapterByName(r.Context(), adapterName)
if err != nil {
http.Error(w, "Adapter not found", http.StatusNotFound)
return
}
var rawData interface{}
if err := json.NewDecoder(r.Body).Decode(&rawData); err != nil {
http.Error(w, "Invalid JSON payload", http.StatusBadRequest)
return
}
findingsNode := getJSONValue(rawData, adapter.FindingsPath)
findingsArray, ok := findingsNode.([]interface{})
if !ok {
http.Error(w, "Findings path did not resolve to a JSON array", http.StatusBadRequest)
return
}
type groupKey struct {
Source string
Asset string
}
groupedTickets := make(map[groupKey][]domain2.Ticket)
for _, item := range findingsArray {
finding, ok := item.(map[string]interface{})
if !ok {
continue
}
ticket := domain2.Ticket{
Source: adapter.SourceName,
Status: "Waiting to be Triaged", // Explicitly set status
Title: interfaceToString(finding[adapter.MappingTitle]),
AssetIdentifier: interfaceToString(finding[adapter.MappingAsset]),
Severity: interfaceToString(finding[adapter.MappingSeverity]),
Description: interfaceToString(finding[adapter.MappingDescription]),
RecommendedRemediation: interfaceToString(finding[adapter.MappingRemediation]),
}
if ticket.Title != "" && ticket.AssetIdentifier != "" {
hashInput := ticket.Source + "|" + ticket.AssetIdentifier + "|" + ticket.Title
hash := sha256.Sum256([]byte(hashInput))
ticket.DedupeHash = hex.EncodeToString(hash[:])
key := groupKey{Source: ticket.Source, Asset: ticket.AssetIdentifier}
groupedTickets[key] = append(groupedTickets[key], ticket)
}
}
for key, batch := range groupedTickets {
err := h.Store.ProcessIngestionBatch(r.Context(), key.Source, key.Asset, batch)
if err != nil {
log.Printf("🔥 JSON Ingestion Error for Asset %s: %v", key.Asset, err)
// 🚀 LOG THE BATCH FAILURE
h.Store.LogSync(r.Context(), key.Source, "Failed", len(batch), err.Error())
http.Error(w, "Database error processing JSON batch", http.StatusInternalServerError)
return
} else {
// 🚀 LOG THE SUCCESS
h.Store.LogSync(r.Context(), key.Source, "Success", len(batch), "")
}
}
w.WriteHeader(http.StatusCreated)
}