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  }