github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/syft/format/syftjson/decoder.go (about)

     1  package syftjson
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"strings"
     8  
     9  	"github.com/Masterminds/semver"
    10  
    11  	"github.com/anchore/syft/syft/format/syftjson/model"
    12  	"github.com/anchore/syft/syft/sbom"
    13  	"github.com/lineaje-labs/syft/internal"
    14  	"github.com/lineaje-labs/syft/internal/log"
    15  )
    16  
    17  var _ sbom.FormatDecoder = (*decoder)(nil)
    18  
    19  type decoder struct{}
    20  
    21  func NewFormatDecoder() sbom.FormatDecoder {
    22  	return decoder{}
    23  }
    24  
    25  func (d decoder) Decode(reader io.ReadSeeker) (*sbom.SBOM, sbom.FormatID, string, error) {
    26  	if reader == nil {
    27  		return nil, "", "", fmt.Errorf("no SBOM bytes provided")
    28  	}
    29  
    30  	id, version := d.Identify(reader)
    31  	if version == "" || id != ID {
    32  		return nil, "", "", fmt.Errorf("not a syft-json document")
    33  	}
    34  	var doc model.Document
    35  
    36  	if _, err := reader.Seek(0, io.SeekStart); err != nil {
    37  		return nil, "", "", fmt.Errorf("unable to seek to start of Syft JSON SBOM: %+v", err)
    38  	}
    39  
    40  	dec := json.NewDecoder(reader)
    41  
    42  	err := dec.Decode(&doc)
    43  	if err != nil {
    44  		return nil, "", "", fmt.Errorf("unable to decode syft-json document: %w", err)
    45  	}
    46  
    47  	if err := checkSupportedSchema(doc.Schema.Version, internal.JSONSchemaVersion); err != nil {
    48  		log.Warn(err)
    49  	}
    50  
    51  	return toSyftModel(doc), ID, doc.Schema.Version, nil
    52  }
    53  
    54  func (d decoder) Identify(reader io.ReadSeeker) (sbom.FormatID, string) {
    55  	if reader == nil {
    56  		return "", ""
    57  	}
    58  
    59  	if _, err := reader.Seek(0, io.SeekStart); err != nil {
    60  		log.Debugf("unable to seek to start of Syft JSON SBOM: %+v", err)
    61  		return "", ""
    62  	}
    63  
    64  	type Document struct {
    65  		Schema model.Schema `json:"schema"`
    66  	}
    67  
    68  	dec := json.NewDecoder(reader)
    69  
    70  	var doc Document
    71  	err := dec.Decode(&doc)
    72  	if err != nil {
    73  		// maybe not json? maybe not valid? doesn't matter, we won't process it.
    74  		return "", ""
    75  	}
    76  
    77  	if !strings.Contains(doc.Schema.URL, "anchore/syft") {
    78  		// not a syft-json document
    79  		return "", ""
    80  	}
    81  
    82  	// note: we support all previous schema versions
    83  	return ID, doc.Schema.Version
    84  }
    85  
    86  func checkSupportedSchema(documentVersion string, parserVersion string) error {
    87  	documentV, err := semver.NewVersion(documentVersion)
    88  	if err != nil {
    89  		return fmt.Errorf("error comparing document schema version with parser schema version: %w", err)
    90  	}
    91  
    92  	parserV, err := semver.NewVersion(parserVersion)
    93  	if err != nil {
    94  		return fmt.Errorf("error comparing document schema version with parser schema version: %w", err)
    95  	}
    96  
    97  	if documentV.GreaterThan(parserV) {
    98  		return fmt.Errorf("document has schema version %s, but parser has older schema version (%s)", documentVersion, parserVersion)
    99  	}
   100  
   101  	return nil
   102  }