mirror of
https://github.com/j3ssie/osmedeus.git
synced 2026-08-17 21:25:49 +02:00
Major features: - Add run registry for tracking active runs with PID management - Add API-based run cancellation with process termination - Add event trigger input vars syntax for multi-variable extraction - Add filter_functions with utility function support in triggers - Add event envelope injection for full event context in workflows - Add write coordinator for batched database operations API improvements: - Add logout endpoint and diffs endpoints for assets/vulnerabilities - Add step-results listing endpoint - Update schedule model with target, workspace, params fields - Change run_id to run_uuid across API responses Performance: - Add compiled JS program caching for 60-80% faster loop conditions - Add parallel shard rendering for 20-40% faster workflow startup - Add memory-mapped I/O for large file line counting - Add efficient output buffer combining in runners - Add mtime-based cache invalidation for workflow loader Other changes: - Rename trigger field from trigger to triggers in workflow YAML - Disable pongo2 HTML autoescape for shell command templates - Update JWT expiration default to 1440 minutes (1 day) - Change CORS default to reflect-origin for credentials support - Add source_type field to events (run, eval, api) - Skip copying core Unix tools to external-binaries
205 lines
5.9 KiB
Go
205 lines
5.9 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"`
|
|
}
|