github.com/wolfi-dev/wolfictl@v0.16.11/pkg/configs/advisory/v2/migrate.go (about) 1 package v2 2 3 import ( 4 "fmt" 5 "slices" 6 "sort" 7 8 "github.com/openvex/go-vex/pkg/vex" 9 v1 "github.com/wolfi-dev/wolfictl/pkg/configs/advisory/v1" 10 ) 11 12 func MigrateV1Document(v1Doc *v1.Document) (*Document, error) { 13 if v1Doc == nil { 14 return nil, fmt.Errorf("v1Doc cannot be nil") 15 } 16 17 advisories, err := migrateV1Advisories(v1Doc.Advisories) 18 if err != nil { 19 return nil, fmt.Errorf("failed to migrate V1 advisories: %w", err) 20 } 21 22 doc := &Document{ 23 SchemaVersion: SchemaVersion, 24 Package: Package{ 25 Name: v1Doc.Package.Name, 26 }, 27 Advisories: advisories, 28 } 29 30 return doc, nil 31 } 32 33 func migrateV1Advisories(v1Advisories v1.Advisories) (Advisories, error) { 34 if v1Advisories == nil { 35 return nil, fmt.Errorf("v1Advisories cannot be nil") 36 } 37 38 advisories := make(Advisories, 0, len(v1Advisories)) 39 40 for id, entries := range v1Advisories { 41 advisory, err := migrateV1Advisory(entries, id) 42 if err != nil { 43 return nil, fmt.Errorf("failed to migrate V1 advisory (ID: %s): %w", id, err) 44 } 45 46 advisories = append(advisories, *advisory) 47 } 48 49 // Ensure the advisory list is sorted before returning it. 50 sort.Sort(advisories) 51 52 return advisories, nil 53 } 54 55 func migrateV1Advisory(v1Advisory []v1.Entry, advisoryID string) (*Advisory, error) { 56 events := make([]Event, 0, len(v1Advisory)) 57 for i, v1Entry := range v1Advisory { 58 event, err := migrateV1Entry(v1Entry) 59 if err != nil { 60 return nil, fmt.Errorf("failed to migrate V1 entry (index: %d): %w", i, err) 61 } 62 63 events = append(events, *event) 64 } 65 66 return &Advisory{ 67 ID: advisoryID, 68 Events: events, 69 }, nil 70 } 71 72 func migrateV1Entry(v1Entry v1.Entry) (*Event, error) { 73 switch v1Entry.Status { 74 case vex.StatusUnderInvestigation: 75 return &Event{ 76 Type: EventTypeDetection, 77 Timestamp: Timestamp(v1Entry.Timestamp), 78 Data: Detection{ 79 Type: DetectionTypeManual, 80 }, 81 }, nil 82 83 case vex.StatusAffected: 84 var data interface{} 85 action := v1Entry.ActionStatement 86 if action != "" { 87 data = TruePositiveDetermination{ 88 Note: action, 89 } 90 } 91 return &Event{ 92 Type: EventTypeTruePositiveDetermination, 93 Timestamp: Timestamp(v1Entry.Timestamp), 94 Data: data, 95 }, nil 96 97 case vex.StatusFixed: 98 return &Event{ 99 Type: EventTypeFixed, 100 Timestamp: Timestamp(v1Entry.Timestamp), 101 Data: Fixed{ 102 FixedVersion: v1Entry.FixedVersion, 103 }, 104 }, nil 105 106 case vex.StatusNotAffected: 107 fpType, err := migrateV1Justification(v1Entry.Justification) 108 if err != nil { 109 return nil, fmt.Errorf("failed to migrate V1 justification: %w", err) 110 } 111 112 return &Event{ 113 Type: EventTypeFalsePositiveDetermination, 114 Timestamp: Timestamp(v1Entry.Timestamp), 115 Data: FalsePositiveDetermination{ 116 Type: fpType, 117 Note: v1Entry.ImpactStatement, 118 }, 119 }, nil 120 } 121 122 return nil, fmt.Errorf("unexpected VEX status: %s", v1Entry.Status) 123 } 124 125 func migrateV1Justification(j vex.Justification) (string, error) { 126 switch j { 127 case vex.ComponentNotPresent: 128 return FPTypeComponentVulnerabilityMismatch, nil 129 130 case vex.VulnerableCodeNotPresent: 131 return "", fmt.Errorf("%s isn't allowed because it's ambiguous. Please use a v2 FPType as the justification instead to enable the migration to continue", j) 132 133 case vex.VulnerableCodeNotInExecutePath: 134 return FPTypeVulnerableCodeNotInExecutionPath, nil 135 136 case vex.VulnerableCodeCannotBeControlledByAdversary: 137 return FPTypeVulnerableCodeCannotBeControlledByAdversary, nil 138 139 case vex.InlineMitigationsAlreadyExist: 140 return FPTypeInlineMitigationsExist, nil 141 } 142 143 // To ease the migration to v2 via pre-migration review of existing data. 144 if fpType := string(j); slices.Contains(FPTypes, fpType) { 145 return fpType, nil 146 } 147 148 return "", fmt.Errorf("unrecognized VEX justification: %s", j) 149 }