github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/formats/syftjson/decoder_test.go (about)

     1  package syftjson
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/go-test/deep"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
    15  	"github.com/anchore/syft/internal"
    16  	"github.com/anchore/syft/syft/cpe"
    17  	"github.com/anchore/syft/syft/file"
    18  	"github.com/anchore/syft/syft/formats/internal/testutils"
    19  	"github.com/anchore/syft/syft/linux"
    20  	"github.com/anchore/syft/syft/pkg"
    21  	"github.com/anchore/syft/syft/sbom"
    22  	"github.com/anchore/syft/syft/source"
    23  )
    24  
    25  func TestEncodeDecodeCycle(t *testing.T) {
    26  	testImage := "image-simple"
    27  	originalSBOM := testutils.ImageInput(t, testImage)
    28  
    29  	var buf bytes.Buffer
    30  	assert.NoError(t, encoder(&buf, originalSBOM))
    31  
    32  	actualSBOM, err := decoder(bytes.NewReader(buf.Bytes()))
    33  	assert.NoError(t, err)
    34  
    35  	for _, d := range deep.Equal(originalSBOM.Source, actualSBOM.Source) {
    36  		if strings.HasSuffix(d, "<nil slice> != []") {
    37  			// semantically the same
    38  			continue
    39  		}
    40  		t.Errorf("metadata difference: %+v", d)
    41  	}
    42  
    43  	actualPackages := actualSBOM.Artifacts.Packages.Sorted()
    44  	for idx, p := range originalSBOM.Artifacts.Packages.Sorted() {
    45  		if !assert.Equal(t, p.Name, actualPackages[idx].Name) {
    46  			t.Errorf("different package at idx=%d: %s vs %s", idx, p.Name, actualPackages[idx].Name)
    47  			continue
    48  		}
    49  
    50  		for _, d := range deep.Equal(p, actualPackages[idx]) {
    51  			if strings.Contains(d, ".VirtualPath: ") {
    52  				// location.Virtual path is not exposed in the json output
    53  				continue
    54  			}
    55  			if strings.HasSuffix(d, "<nil slice> != []") {
    56  				// semantically the same
    57  				continue
    58  			}
    59  			t.Errorf("package difference (%s): %+v", p.Name, d)
    60  		}
    61  	}
    62  }
    63  
    64  func TestOutOfDateParser(t *testing.T) {
    65  	tests := []struct {
    66  		name            string
    67  		documentVersion string
    68  		parserVersion   string
    69  		want            error
    70  	}{{
    71  		name:            "no warning when doc version is older",
    72  		documentVersion: "1.0.9",
    73  		parserVersion:   "3.1.0",
    74  	}, {
    75  		name:            "warning when parser is older",
    76  		documentVersion: "4.3.2",
    77  		parserVersion:   "3.1.0",
    78  		want:            fmt.Errorf("document has schema version %s, but parser has older schema version (%s)", "4.3.2", "3.1.0"),
    79  	}, {
    80  		name:            "warning when document version is unparseable",
    81  		documentVersion: "some-nonsense",
    82  		parserVersion:   "3.1.0",
    83  		want:            fmt.Errorf("error comparing document schema version with parser schema version: %w", errors.New("Invalid Semantic Version")),
    84  	}, {
    85  		name:            "warning when parser version is unparseable",
    86  		documentVersion: "7.1.0",
    87  		parserVersion:   "some-nonsense",
    88  		want:            fmt.Errorf("error comparing document schema version with parser schema version: %w", errors.New("Invalid Semantic Version")),
    89  	}}
    90  
    91  	for _, tt := range tests {
    92  		t.Run(tt.name, func(t *testing.T) {
    93  			got := checkSupportedSchema(tt.documentVersion, tt.parserVersion)
    94  			assert.Equal(t, tt.want, got)
    95  		})
    96  	}
    97  }
    98  
    99  func Test_encodeDecodeFileMetadata(t *testing.T) {
   100  	p := pkg.Package{
   101  		Name:    "pkg",
   102  		Version: "version",
   103  		FoundBy: "something",
   104  		Locations: file.NewLocationSet(file.Location{
   105  			LocationData: file.LocationData{
   106  				Coordinates: file.Coordinates{
   107  					RealPath:     "/somewhere",
   108  					FileSystemID: "id",
   109  				},
   110  			},
   111  			LocationMetadata: file.LocationMetadata{
   112  				Annotations: map[string]string{
   113  					"key": "value",
   114  				},
   115  			},
   116  		}),
   117  		Licenses: pkg.NewLicenseSet(pkg.License{
   118  			Value:          "MIT",
   119  			SPDXExpression: "MIT",
   120  			Type:           "MIT",
   121  			URLs:           internal.NewStringSet("https://example.org/license"),
   122  			Locations:      file.LocationSet{},
   123  		}),
   124  		Language: "language",
   125  		Type:     "type",
   126  		CPEs: []cpe.CPE{
   127  			{
   128  				Part:    "a",
   129  				Vendor:  "vendor",
   130  				Product: "product",
   131  				Version: "version",
   132  				Update:  "update",
   133  			},
   134  		},
   135  		PURL:         "pkg:generic/pkg@version",
   136  		MetadataType: "",
   137  		Metadata:     nil,
   138  	}
   139  	p.SetID()
   140  
   141  	c := file.Coordinates{
   142  		RealPath:     "some-file",
   143  		FileSystemID: "some-fs-id",
   144  	}
   145  
   146  	s := sbom.SBOM{
   147  		Artifacts: sbom.Artifacts{
   148  			Packages: pkg.NewCollection(p),
   149  			FileMetadata: map[file.Coordinates]file.Metadata{
   150  				c: {
   151  					FileInfo: stereoscopeFile.ManualInfo{
   152  						NameValue: c.RealPath,
   153  						ModeValue: 0644,
   154  						SizeValue: 7,
   155  					},
   156  					Path:     c.RealPath,
   157  					Type:     stereoscopeFile.TypeRegular,
   158  					UserID:   1,
   159  					GroupID:  2,
   160  					MIMEType: "text/plain",
   161  				},
   162  			},
   163  			FileDigests: map[file.Coordinates][]file.Digest{
   164  				c: {
   165  					{
   166  						Algorithm: "sha1",
   167  						Value:     "d34db33f",
   168  					},
   169  				},
   170  			},
   171  			FileContents: map[file.Coordinates]string{
   172  				c: "some contents",
   173  			},
   174  			FileLicenses: map[file.Coordinates][]file.License{
   175  				c: {
   176  					{
   177  						Value:          "MIT",
   178  						SPDXExpression: "MIT",
   179  						Type:           "MIT",
   180  						LicenseEvidence: &file.LicenseEvidence{
   181  							Confidence: 1,
   182  							Offset:     2,
   183  							Extent:     3,
   184  						},
   185  					},
   186  				},
   187  			},
   188  			LinuxDistribution: &linux.Release{
   189  				PrettyName:       "some os",
   190  				Name:             "os",
   191  				ID:               "os-id",
   192  				IDLike:           []string{"os"},
   193  				Version:          "version",
   194  				VersionID:        "version",
   195  				VersionCodename:  "codename",
   196  				BuildID:          "build-id",
   197  				ImageID:          "image-id",
   198  				ImageVersion:     "image-version",
   199  				Variant:          "variant",
   200  				VariantID:        "variant-id",
   201  				HomeURL:          "https://example.org/os",
   202  				SupportURL:       "https://example.org/os/support",
   203  				BugReportURL:     "https://example.org/os/bugs",
   204  				PrivacyPolicyURL: "https://example.org/os/privacy",
   205  				CPEName:          "os-cpe",
   206  				SupportEnd:       "now",
   207  			},
   208  		},
   209  		Relationships: nil,
   210  		Source: source.Description{
   211  			ID:      "some-id",
   212  			Name:    "some-name",
   213  			Version: "some-version",
   214  			Metadata: source.FileSourceMetadata{
   215  				Path: "/some-file-source-path",
   216  				Digests: []file.Digest{
   217  					{
   218  						Algorithm: "sha1",
   219  						Value:     "d34db33f",
   220  					},
   221  				},
   222  				MIMEType: "file/zip",
   223  			},
   224  		},
   225  		Descriptor: sbom.Descriptor{
   226  			Name:    "syft",
   227  			Version: "this-version",
   228  		},
   229  	}
   230  
   231  	buf := &bytes.Buffer{}
   232  	err := encoder(buf, s)
   233  	require.NoError(t, err)
   234  
   235  	got, err := decoder(buf)
   236  	require.NoError(t, err)
   237  
   238  	require.Equal(t, s, *got)
   239  }