github.com/anchore/syft@v1.38.2/syft/format/syftjson/model/source.go (about)

     1  package model
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"reflect"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/anchore/syft/internal/sourcemetadata"
    11  	"github.com/anchore/syft/syft/source"
    12  )
    13  
    14  // Source represents the artifact that was analyzed to generate this SBOM, such as a container image, directory, or file archive.
    15  // The Supplier field can be provided by users to fulfill NTIA minimum elements requirements.
    16  type Source struct {
    17  	// ID is a unique identifier for the analyzed source artifact.
    18  	ID string `json:"id"`
    19  
    20  	// Name is the name of the analyzed artifact (e.g., image name, directory path).
    21  	Name string `json:"name"`
    22  
    23  	// Version is the version of the analyzed artifact (e.g., image tag).
    24  	Version string `json:"version"`
    25  
    26  	// Supplier is supplier information, which can be user-provided for NTIA minimum elements compliance.
    27  	Supplier string `json:"supplier,omitempty"`
    28  
    29  	// Type is the source type (e.g., "image", "directory", "file").
    30  	Type string `json:"type"`
    31  
    32  	// Metadata contains additional source-specific metadata.
    33  	Metadata interface{} `json:"metadata"`
    34  }
    35  
    36  // sourceUnpacker is used to unmarshal Source objects
    37  type sourceUnpacker struct {
    38  	ID       string          `json:"id,omitempty"`
    39  	Name     string          `json:"name"`
    40  	Version  string          `json:"version"`
    41  	Supplier string          `json:"supplier,omitempty"`
    42  	Type     string          `json:"type"`
    43  	Metadata json.RawMessage `json:"metadata"`
    44  	Target   json.RawMessage `json:"target"` // pre-v9 schema support
    45  }
    46  
    47  // UnmarshalJSON populates a source object from JSON bytes.
    48  func (s *Source) UnmarshalJSON(b []byte) error {
    49  	var unpacker sourceUnpacker
    50  	err := json.Unmarshal(b, &unpacker)
    51  	if err != nil {
    52  		return err
    53  	}
    54  
    55  	s.Name = unpacker.Name
    56  	s.Version = unpacker.Version
    57  	s.Supplier = unpacker.Supplier
    58  	s.Type = unpacker.Type
    59  	s.ID = unpacker.ID
    60  
    61  	if len(unpacker.Target) > 0 {
    62  		s.Type = cleanPreSchemaV9MetadataType(s.Type)
    63  		s.Metadata, err = extractPreSchemaV9Metadata(s.Type, unpacker.Target)
    64  		if err != nil {
    65  			return fmt.Errorf("unable to extract pre-schema-v9 source metadata: %w", err)
    66  		}
    67  		return nil
    68  	}
    69  
    70  	return unpackSrcMetadata(s, unpacker)
    71  }
    72  
    73  func unpackSrcMetadata(s *Source, unpacker sourceUnpacker) error {
    74  	rt := sourcemetadata.ReflectTypeFromJSONName(s.Type)
    75  	if rt == nil {
    76  		// in cases where we are converting from an SBOM without any source information, we don't want this to be fatal
    77  		return nil
    78  	}
    79  
    80  	val := reflect.New(rt).Interface()
    81  	if len(unpacker.Metadata) > 0 {
    82  		if err := json.Unmarshal(unpacker.Metadata, val); err != nil {
    83  			return err
    84  		}
    85  	}
    86  
    87  	s.Metadata = reflect.ValueOf(val).Elem().Interface()
    88  
    89  	return nil
    90  }
    91  
    92  func cleanPreSchemaV9MetadataType(t string) string {
    93  	t = strings.ToLower(t)
    94  	if t == "dir" {
    95  		return "directory"
    96  	}
    97  	return t
    98  }
    99  
   100  func extractPreSchemaV9Metadata(t string, target []byte) (interface{}, error) {
   101  	switch t {
   102  	case "directory", "dir":
   103  		cleanTarget, err := strconv.Unquote(string(target))
   104  		if err != nil {
   105  			cleanTarget = string(target)
   106  		}
   107  
   108  		return source.DirectoryMetadata{
   109  			Path: cleanTarget,
   110  		}, nil
   111  
   112  	case "file":
   113  		cleanTarget, err := strconv.Unquote(string(target))
   114  		if err != nil {
   115  			cleanTarget = string(target)
   116  		}
   117  
   118  		return source.FileMetadata{
   119  			Path: cleanTarget,
   120  		}, nil
   121  
   122  	case "image":
   123  		var payload source.ImageMetadata
   124  		if err := json.Unmarshal(target, &payload); err != nil {
   125  			return nil, err
   126  		}
   127  		return payload, nil
   128  
   129  	default:
   130  		return nil, fmt.Errorf("unsupported package metadata type: %+v", t)
   131  	}
   132  }