github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/internal/binutils/classifier_test.go (about)

     1  package binutils
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/anchore/packageurl-go"
    12  	"github.com/anchore/syft/syft/cpe"
    13  	"github.com/anchore/syft/syft/file"
    14  	"github.com/anchore/syft/syft/internal/unionreader"
    15  )
    16  
    17  func Test_ClassifierCPEs(t *testing.T) {
    18  	tests := []struct {
    19  		name       string
    20  		fixture    string
    21  		classifier Classifier
    22  		cpes       []string
    23  	}{
    24  		{
    25  			name:    "no CPEs",
    26  			fixture: "test-fixtures/version.txt",
    27  			classifier: Classifier{
    28  				Package:         "some-app",
    29  				FileGlob:        "**/version.txt",
    30  				EvidenceMatcher: FileContentsVersionMatcher("cataloger-name", `(?m)my-verison:(?P<version>[0-9.]+)`),
    31  				CPEs:            []cpe.CPE{},
    32  			},
    33  			cpes: nil,
    34  		},
    35  		{
    36  			name:    "one Attributes",
    37  			fixture: "test-fixtures/version.txt",
    38  			classifier: Classifier{
    39  				Package:         "some-app",
    40  				FileGlob:        "**/version.txt",
    41  				EvidenceMatcher: FileContentsVersionMatcher("cataloger-name", `(?m)my-verison:(?P<version>[0-9.]+)`),
    42  				CPEs: []cpe.CPE{
    43  					cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
    44  				},
    45  			},
    46  			cpes: []string{
    47  				"cpe:2.3:a:some:app:1.8:*:*:*:*:*:*:*",
    48  			},
    49  		},
    50  		{
    51  			name:    "multiple CPEs",
    52  			fixture: "test-fixtures/version.txt",
    53  			classifier: Classifier{
    54  				Package:         "some-app",
    55  				FileGlob:        "**/version.txt",
    56  				EvidenceMatcher: FileContentsVersionMatcher("cataloger-name", `(?m)my-verison:(?P<version>[0-9.]+)`),
    57  				CPEs: []cpe.CPE{
    58  					cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
    59  					cpe.Must("cpe:2.3:a:some:apps:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
    60  				},
    61  			},
    62  			cpes: []string{
    63  				"cpe:2.3:a:some:app:1.8:*:*:*:*:*:*:*",
    64  				"cpe:2.3:a:some:apps:1.8:*:*:*:*:*:*:*",
    65  			},
    66  		},
    67  		{
    68  			name:    "version in parts",
    69  			fixture: "test-fixtures/version-parts.txt",
    70  			classifier: Classifier{
    71  				Package:         "some-app",
    72  				FileGlob:        "**/version-parts.txt",
    73  				EvidenceMatcher: FileContentsVersionMatcher("cataloger-name", `(?m)\x00(?P<major>[0-9.]+)\x00(?P<minor>[0-9.]+)\x00(?P<patch>[0-9.]+)\x00`),
    74  				CPEs:            []cpe.CPE{},
    75  			},
    76  			cpes: nil,
    77  		},
    78  	}
    79  
    80  	for _, test := range tests {
    81  		t.Run(test.name, func(t *testing.T) {
    82  			resolver := file.NewMockResolverForPaths(test.fixture)
    83  			ls, err := resolver.FilesByPath(test.fixture)
    84  			require.NoError(t, err)
    85  			require.Len(t, ls, 1)
    86  
    87  			pkgs, err := test.classifier.EvidenceMatcher(test.classifier, MatcherContext{Resolver: resolver, Location: ls[0]})
    88  			require.NoError(t, err)
    89  
    90  			require.Len(t, pkgs, 1)
    91  
    92  			p := pkgs[0]
    93  
    94  			var cpes []string
    95  			for _, c := range p.CPEs {
    96  				cpes = append(cpes, c.Attributes.String())
    97  			}
    98  			require.Equal(t, test.cpes, cpes)
    99  		})
   100  	}
   101  }
   102  
   103  func TestClassifier_MarshalJSON(t *testing.T) {
   104  
   105  	tests := []struct {
   106  		name       string
   107  		classifier Classifier
   108  		want       string
   109  		wantErr    assert.ErrorAssertionFunc
   110  	}{
   111  		{
   112  			name: "go case",
   113  			classifier: Classifier{
   114  				Class:           "class",
   115  				FileGlob:        "glob",
   116  				EvidenceMatcher: FileContentsVersionMatcher("cataloger-name", ".thing"),
   117  				Package:         "pkg",
   118  				PURL: packageurl.PackageURL{
   119  					Type:       "type",
   120  					Namespace:  "namespace",
   121  					Name:       "name",
   122  					Version:    "version",
   123  					Qualifiers: nil,
   124  					Subpath:    "subpath",
   125  				},
   126  				CPEs: []cpe.CPE{cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*", cpe.GeneratedSource)},
   127  			},
   128  			want: `{"class":"class","fileGlob":"glob","package":"pkg","purl":"pkg:type/namespace/name@version#subpath","cpes":["cpe:2.3:a:some:app:*:*:*:*:*:*:*:*"]}`,
   129  		},
   130  	}
   131  	for _, tt := range tests {
   132  		t.Run(tt.name, func(t *testing.T) {
   133  			if tt.wantErr == nil {
   134  				tt.wantErr = assert.NoError
   135  			}
   136  			cfg := tt.classifier
   137  			got, err := cfg.MarshalJSON()
   138  			if !tt.wantErr(t, err) {
   139  				return
   140  			}
   141  			assert.Equal(t, tt.want, string(got))
   142  		})
   143  	}
   144  }
   145  
   146  func TestFileContentsVersionMatcher(t *testing.T) {
   147  	tests := []struct {
   148  		name     string
   149  		pattern  string
   150  		data     string
   151  		expected string
   152  	}{
   153  		{
   154  			name:     "simple version string regexp",
   155  			pattern:  `some data (?P<version>[0-9]+\.[0-9]+\.[0-9]+) some data`,
   156  			data:     "some data 1.2.3 some data",
   157  			expected: "1.2.3",
   158  		},
   159  		{
   160  			name:     "version parts regexp",
   161  			pattern:  `\x00\x23(?P<major>[0-9]+)\x00\x23(?P<minor>[0-9]+)\x00\x23(?P<patch>[0-9]+)\x00\x23`,
   162  			data:     "\x00\x239\x00\x239\x00\x239\x00\x23",
   163  			expected: "9.9.9",
   164  		},
   165  	}
   166  	for _, tt := range tests {
   167  		t.Run(tt.name, func(t *testing.T) {
   168  			mockGetContent := func(context MatcherContext) (unionreader.UnionReader, error) {
   169  				return unionreader.GetUnionReader(io.NopCloser(bytes.NewBufferString(tt.data)))
   170  			}
   171  			fn := FileContentsVersionMatcher("cataloger-name", tt.pattern)
   172  			p, err := fn(Classifier{}, MatcherContext{
   173  				GetReader: mockGetContent,
   174  			})
   175  
   176  			if err != nil {
   177  				t.Errorf("Unexpected error %#v", err)
   178  			}
   179  
   180  			if p[0].Version != tt.expected {
   181  				t.Errorf("Versions don't match.\ngot\n%q\n\nexpected\n%q", p[0].Version, tt.expected)
   182  			}
   183  		})
   184  	}
   185  }