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 }