mirror of
https://github.com/j3ssie/osmedeus.git
synced 2026-08-17 21:25:49 +02:00
- Add LLM executor supporting OpenAI vision, tool calling, embeddings, and structured outputs - Introduce event emitter/receiver workflows with deduplication and filtering (generate_event functions) - Add workflow extends/override system enabling inheritance chains and step merge modes - Update function naming to snake_case across all testdata (fileExists→file_exists, etc.) - Add comprehensive test fixtures for linter, events, CDN, step dependencies, and extends workflows
205 lines
5.8 KiB
Go
205 lines
5.8 KiB
Go
package database
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// AssetDiff represents the difference between two scans
|
|
type AssetDiff struct {
|
|
WorkspaceName string `json:"workspace_name"`
|
|
FromTime time.Time `json:"from_time"`
|
|
ToTime time.Time `json:"to_time"`
|
|
Added []Asset `json:"added"`
|
|
Removed []Asset `json:"removed"`
|
|
Changed []AssetChange `json:"changed"`
|
|
Summary DiffSummary `json:"summary"`
|
|
}
|
|
|
|
// AssetChange represents a changed asset with field-level diffs
|
|
type AssetChange struct {
|
|
AssetID int64 `json:"asset_id"`
|
|
AssetValue string `json:"asset_value"`
|
|
URL string `json:"url"`
|
|
Changes []FieldChange `json:"changes"`
|
|
}
|
|
|
|
// FieldChange represents a single field that changed
|
|
type FieldChange struct {
|
|
Field string `json:"field"`
|
|
OldValue any `json:"old_value"`
|
|
NewValue any `json:"new_value"`
|
|
}
|
|
|
|
// DiffSummary provides counts
|
|
type DiffSummary struct {
|
|
TotalAdded int `json:"total_added"`
|
|
TotalRemoved int `json:"total_removed"`
|
|
TotalChanged int `json:"total_changed"`
|
|
}
|
|
|
|
// VulnerabilityDiff represents the difference between two scans for vulnerabilities
|
|
type VulnerabilityDiff struct {
|
|
WorkspaceName string `json:"workspace_name"`
|
|
FromTime time.Time `json:"from_time"`
|
|
ToTime time.Time `json:"to_time"`
|
|
Added []Vulnerability `json:"added"`
|
|
Removed []Vulnerability `json:"removed"`
|
|
Changed []VulnerabilityChange `json:"changed"`
|
|
Summary DiffSummary `json:"summary"`
|
|
}
|
|
|
|
// VulnerabilityChange represents a changed vulnerability
|
|
type VulnerabilityChange struct {
|
|
VulnID int64 `json:"vuln_id"`
|
|
VulnInfo string `json:"vuln_info"`
|
|
AssetValue string `json:"asset_value"`
|
|
Changes []FieldChange `json:"changes"`
|
|
}
|
|
|
|
// GetAssetDiff calculates the difference between two time points
|
|
func GetAssetDiff(ctx context.Context, workspace string, fromTime, toTime time.Time) (*AssetDiff, error) {
|
|
if db == nil {
|
|
return nil, fmt.Errorf("database not connected")
|
|
}
|
|
|
|
diff := &AssetDiff{
|
|
WorkspaceName: workspace,
|
|
FromTime: fromTime,
|
|
ToTime: toTime,
|
|
Added: []Asset{},
|
|
Removed: []Asset{},
|
|
Changed: []AssetChange{},
|
|
}
|
|
|
|
// Added: created_at >= fromTime AND created_at <= toTime
|
|
err := db.NewSelect().Model(&diff.Added).
|
|
Where("workspace = ?", workspace).
|
|
Where("created_at >= ?", fromTime).
|
|
Where("created_at <= ?", toTime).
|
|
Scan(ctx)
|
|
if err != nil {
|
|
// Not a fatal error, continue with empty slice
|
|
diff.Added = []Asset{}
|
|
}
|
|
|
|
// Removed: last_seen_at < fromTime AND last_seen_at is not zero
|
|
// These are assets that haven't been seen since before the time window
|
|
err = db.NewSelect().Model(&diff.Removed).
|
|
Where("workspace = ?", workspace).
|
|
Where("last_seen_at < ?", fromTime).
|
|
Where("last_seen_at != ?", time.Time{}).
|
|
Where("created_at < ?", fromTime).
|
|
Scan(ctx)
|
|
if err != nil {
|
|
diff.Removed = []Asset{}
|
|
}
|
|
|
|
// Changed: updated_at within range AND created_at before range
|
|
var changedAssets []Asset
|
|
err = db.NewSelect().Model(&changedAssets).
|
|
Where("workspace = ?", workspace).
|
|
Where("updated_at >= ?", fromTime).
|
|
Where("updated_at <= ?", toTime).
|
|
Where("created_at < ?", fromTime).
|
|
Scan(ctx)
|
|
if err != nil {
|
|
changedAssets = []Asset{}
|
|
}
|
|
|
|
// Convert to AssetChange (field-level diffs would require storing previous values)
|
|
for _, a := range changedAssets {
|
|
diff.Changed = append(diff.Changed, AssetChange{
|
|
AssetID: a.ID,
|
|
AssetValue: a.AssetValue,
|
|
URL: a.URL,
|
|
// Note: Without historical storage, we can only indicate it changed
|
|
Changes: []FieldChange{},
|
|
})
|
|
}
|
|
|
|
diff.Summary = DiffSummary{
|
|
TotalAdded: len(diff.Added),
|
|
TotalRemoved: len(diff.Removed),
|
|
TotalChanged: len(diff.Changed),
|
|
}
|
|
|
|
return diff, nil
|
|
}
|
|
|
|
// GetVulnerabilityDiff calculates the difference between two time points for vulnerabilities
|
|
func GetVulnerabilityDiff(ctx context.Context, workspace string, fromTime, toTime time.Time) (*VulnerabilityDiff, error) {
|
|
if db == nil {
|
|
return nil, fmt.Errorf("database not connected")
|
|
}
|
|
|
|
diff := &VulnerabilityDiff{
|
|
WorkspaceName: workspace,
|
|
FromTime: fromTime,
|
|
ToTime: toTime,
|
|
Added: []Vulnerability{},
|
|
Removed: []Vulnerability{},
|
|
Changed: []VulnerabilityChange{},
|
|
}
|
|
|
|
// Added: created_at >= fromTime AND created_at <= toTime
|
|
err := db.NewSelect().Model(&diff.Added).
|
|
Where("workspace = ?", workspace).
|
|
Where("created_at >= ?", fromTime).
|
|
Where("created_at <= ?", toTime).
|
|
Scan(ctx)
|
|
if err != nil {
|
|
diff.Added = []Vulnerability{}
|
|
}
|
|
|
|
// Removed: last_seen_at < fromTime AND last_seen_at is not zero
|
|
err = db.NewSelect().Model(&diff.Removed).
|
|
Where("workspace = ?", workspace).
|
|
Where("last_seen_at < ?", fromTime).
|
|
Where("last_seen_at != ?", time.Time{}).
|
|
Where("created_at < ?", fromTime).
|
|
Scan(ctx)
|
|
if err != nil {
|
|
diff.Removed = []Vulnerability{}
|
|
}
|
|
|
|
// Changed: updated_at within range AND created_at before range
|
|
var changedVulns []Vulnerability
|
|
err = db.NewSelect().Model(&changedVulns).
|
|
Where("workspace = ?", workspace).
|
|
Where("updated_at >= ?", fromTime).
|
|
Where("updated_at <= ?", toTime).
|
|
Where("created_at < ?", fromTime).
|
|
Scan(ctx)
|
|
if err != nil {
|
|
changedVulns = []Vulnerability{}
|
|
}
|
|
|
|
// Convert to VulnerabilityChange
|
|
for _, v := range changedVulns {
|
|
diff.Changed = append(diff.Changed, VulnerabilityChange{
|
|
VulnID: v.ID,
|
|
VulnInfo: v.VulnInfo,
|
|
AssetValue: v.AssetValue,
|
|
Changes: []FieldChange{},
|
|
})
|
|
}
|
|
|
|
diff.Summary = DiffSummary{
|
|
TotalAdded: len(diff.Added),
|
|
TotalRemoved: len(diff.Removed),
|
|
TotalChanged: len(diff.Changed),
|
|
}
|
|
|
|
return diff, nil
|
|
}
|
|
|
|
// ImportStats tracks statistics from import operations
|
|
type ImportStats struct {
|
|
New int `json:"new"`
|
|
Updated int `json:"updated"`
|
|
Unchanged int `json:"unchanged"`
|
|
Errors int `json:"errors"`
|
|
}
|