Files
osmedeus/internal/functions/db_merge.go
T
j3ssie 459d5939fb feat: add cloud infrastructure and nmap/tmux function support
- Implement cloud provider infrastructure (DigitalOcean, AWS, GCP, Linode, Azure) with Pulumi integration for distributed scanning
- Add nmap and tmux utility functions for port scanning results processing and long-running background session management
- Introduce webhook-triggered run execution with unique UUID and authentication key support for external integrations
2026-02-16 15:23:24 +07:00

179 lines
6.4 KiB
Go

package functions
import "github.com/j3ssie/osmedeus/v5/internal/database"
// mergeString returns incoming if non-empty, otherwise existing.
func mergeString(existing, incoming string) string {
if incoming != "" {
return incoming
}
return existing
}
// mergeInt returns incoming if non-zero, otherwise existing.
func mergeInt(existing, incoming int) int {
if incoming != 0 {
return incoming
}
return existing
}
// mergeInt64 returns incoming if non-zero, otherwise existing.
func mergeInt64(existing, incoming int64) int64 {
if incoming != 0 {
return incoming
}
return existing
}
// mergeBool returns true if either value is true (once true, stays true).
func mergeBool(existing, incoming bool) bool {
return existing || incoming
}
// mergeStringSlice returns incoming if non-nil and non-empty, otherwise existing.
func mergeStringSlice(existing, incoming []string) []string {
if len(incoming) > 0 {
return incoming
}
return existing
}
// mergeAssetFields merges incoming asset fields into existing.
// Non-zero incoming fields win; zero incoming fields preserve existing values.
// ID, CreatedAt, Workspace, and AssetValue are always taken from existing.
func mergeAssetFields(existing, incoming *database.Asset) {
// Preserve identity fields from existing
incoming.ID = existing.ID
incoming.CreatedAt = existing.CreatedAt
incoming.Workspace = existing.Workspace
incoming.AssetValue = existing.AssetValue
// HTTP data
incoming.URL = mergeString(existing.URL, incoming.URL)
incoming.Input = mergeString(existing.Input, incoming.Input)
incoming.Scheme = mergeString(existing.Scheme, incoming.Scheme)
incoming.Method = mergeString(existing.Method, incoming.Method)
incoming.Path = mergeString(existing.Path, incoming.Path)
// Response data
incoming.StatusCode = mergeInt(existing.StatusCode, incoming.StatusCode)
incoming.ContentType = mergeString(existing.ContentType, incoming.ContentType)
incoming.ContentLength = mergeInt64(existing.ContentLength, incoming.ContentLength)
incoming.Title = mergeString(existing.Title, incoming.Title)
incoming.Words = mergeInt(existing.Words, incoming.Words)
incoming.Lines = mergeInt(existing.Lines, incoming.Lines)
// Network data
incoming.HostIP = mergeString(existing.HostIP, incoming.HostIP)
incoming.DnsRecords = mergeStringSlice(existing.DnsRecords, incoming.DnsRecords)
incoming.TLS = mergeString(existing.TLS, incoming.TLS)
// Metadata
incoming.AssetType = mergeString(existing.AssetType, incoming.AssetType)
incoming.Technologies = mergeStringSlice(existing.Technologies, incoming.Technologies)
incoming.ResponseTime = mergeString(existing.ResponseTime, incoming.ResponseTime)
incoming.Remarks = mergeStringSlice(existing.Remarks, incoming.Remarks)
incoming.Source = mergeString(existing.Source, incoming.Source)
incoming.RawJsonData = mergeString(existing.RawJsonData, incoming.RawJsonData)
incoming.RawResponse = mergeString(existing.RawResponse, incoming.RawResponse)
incoming.ScreenshotBase64Data = mergeString(existing.ScreenshotBase64Data, incoming.ScreenshotBase64Data)
// CDN/WAF classification
incoming.IsCDN = mergeBool(existing.IsCDN, incoming.IsCDN)
incoming.IsCloud = mergeBool(existing.IsCloud, incoming.IsCloud)
incoming.IsWAF = mergeBool(existing.IsWAF, incoming.IsWAF)
// Repository/file assets
incoming.Language = mergeString(existing.Language, incoming.Language)
incoming.Size = mergeInt64(existing.Size, incoming.Size)
incoming.LOC = mergeInt64(existing.LOC, incoming.LOC)
incoming.ExternalURL = mergeString(existing.ExternalURL, incoming.ExternalURL)
incoming.BlobContent = mergeString(existing.BlobContent, incoming.BlobContent)
}
// mergeVulnFields merges incoming vulnerability fields into existing.
// Non-zero incoming fields win; zero incoming fields preserve existing values.
// ID, CreatedAt, and Workspace are always taken from existing.
func mergeVulnFields(existing, incoming *database.Vulnerability) {
// Preserve identity fields from existing
incoming.ID = existing.ID
incoming.CreatedAt = existing.CreatedAt
incoming.Workspace = existing.Workspace
// Core vulnerability fields
incoming.VulnInfo = mergeString(existing.VulnInfo, incoming.VulnInfo)
incoming.VulnTitle = mergeString(existing.VulnTitle, incoming.VulnTitle)
incoming.VulnDesc = mergeString(existing.VulnDesc, incoming.VulnDesc)
incoming.VulnPOC = mergeString(existing.VulnPOC, incoming.VulnPOC)
incoming.Severity = mergeString(existing.Severity, incoming.Severity)
incoming.Confidence = mergeString(existing.Confidence, incoming.Confidence)
incoming.AssetType = mergeString(existing.AssetType, incoming.AssetType)
incoming.AssetValue = mergeString(existing.AssetValue, incoming.AssetValue)
incoming.Tags = mergeStringSlice(existing.Tags, incoming.Tags)
// Detail fields
incoming.DetailHTTPRequest = mergeString(existing.DetailHTTPRequest, incoming.DetailHTTPRequest)
incoming.DetailHTTPResponse = mergeString(existing.DetailHTTPResponse, incoming.DetailHTTPResponse)
incoming.RawVulnJSON = mergeString(existing.RawVulnJSON, incoming.RawVulnJSON)
}
// mergePortAssetFields merges port-specific fields from incoming to existing
func mergePortAssetFields(existing, incoming *database.Asset) {
// Preserve identity fields
incoming.ID = existing.ID
incoming.CreatedAt = existing.CreatedAt
incoming.Workspace = existing.Workspace
incoming.AssetValue = existing.AssetValue
// Merge simple fields (non-zero incoming wins)
if incoming.HostIP == "" {
incoming.HostIP = existing.HostIP
}
if incoming.Source == "" {
incoming.Source = existing.Source
}
// Keep existing OpenPorts if incoming is empty
if len(incoming.OpenPorts) == 0 {
incoming.OpenPorts = existing.OpenPorts
}
// Keep existing PortJsonData if incoming is empty
if incoming.PortJsonData == "" {
incoming.PortJsonData = existing.PortJsonData
}
}
// hasPortAssetChanged checks if port-related fields changed
func hasPortAssetChanged(existing, new *database.Asset) bool {
// Check if HostIP changed
if existing.HostIP != new.HostIP {
return true
}
// Check if PortJsonData changed
if existing.PortJsonData != new.PortJsonData {
return true
}
// Check if OpenPorts array changed
if len(existing.OpenPorts) != len(new.OpenPorts) {
return true
}
// Deep compare OpenPorts
existingPortsSet := make(map[string]bool)
for _, p := range existing.OpenPorts {
existingPortsSet[p] = true
}
for _, p := range new.OpenPorts {
if !existingPortsSet[p] {
return true
}
}
return false
}