Skip to content

Commit b0d4547

Browse files
committed
add vuln extractors and it's tests
1 parent ade7ad3 commit b0d4547

File tree

6 files changed

+451
-0
lines changed

6 files changed

+451
-0
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/charmbracelet/fang v0.4.1
1111
github.com/github/go-spdx/v2 v2.3.3
1212
github.com/google/uuid v1.6.0
13+
github.com/knqyf263/go-cpe v0.0.0-20230627041855-cb0794d06872
1314
github.com/maxbrunsfeld/counterfeiter/v6 v6.12.0
1415
github.com/olekukonko/tablewriter v0.0.5
1516
github.com/package-url/packageurl-go v0.1.3

go.sum

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
7373
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
7474
github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
7575
github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
76+
github.com/knqyf263/go-cpe v0.0.0-20230627041855-cb0794d06872 h1:snH0nDYi3kizy9vxYBhZm5KXkGt9VXdGEtr6/1SGUqY=
77+
github.com/knqyf263/go-cpe v0.0.0-20230627041855-cb0794d06872/go.mod h1:4cVhzV/TndScEg4xMtSo3TTz3cMFhEAvhAA4igAyXZY=
7678
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
7779
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
7880
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -120,6 +122,7 @@ github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/
120122
github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
121123
github.com/package-url/packageurl-go v0.1.3 h1:4juMED3hHiz0set3Vq3KeQ75KD1avthoXLtmE3I0PLs=
122124
github.com/package-url/packageurl-go v0.1.3/go.mod h1:nKAWB8E6uk1MHqiS/lQb9pYBGH2+mdJ2PJc2s50dQY0=
125+
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
123126
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
124127
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
125128
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

pkg/scorer/v2/extractors/common.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import (
1919

2020
"github.com/interlynk-io/sbomqs/pkg/licenses"
2121
"github.com/interlynk-io/sbomqs/pkg/sbom"
22+
"github.com/knqyf263/go-cpe/naming"
23+
purl "github.com/package-url/packageurl-go"
2224
)
2325

2426
// license with "NOASSERTION" or "NONE" are considered as
@@ -107,3 +109,55 @@ func componentHasAnyRestrictive(c sbom.GetComponent) bool {
107109
}
108110
return false
109111
}
112+
113+
func compHasAnyPURLs(c sbom.GetComponent) bool {
114+
for _, p := range c.GetPurls() {
115+
if isValidPURL(string(p)) {
116+
return true
117+
}
118+
}
119+
return false
120+
}
121+
122+
func compHasAnyCPEs(c sbom.GetComponent) bool {
123+
for _, p := range c.GetCpes() {
124+
if isValidCPE(string(p)) {
125+
return true
126+
}
127+
}
128+
return false
129+
}
130+
131+
func isValidPURL(s string) bool {
132+
s = strings.TrimSpace(s)
133+
if s == "" {
134+
return false
135+
}
136+
137+
u, err := purl.FromString(s)
138+
if err != nil {
139+
return false
140+
}
141+
142+
// type and name must be present per spec
143+
if strings.TrimSpace(u.Type) == "" || strings.TrimSpace(u.Name) == "" {
144+
return false
145+
}
146+
return true
147+
}
148+
149+
func isValidCPE(s string) bool {
150+
ls := strings.TrimSpace(s)
151+
low := strings.ToLower(ls)
152+
153+
switch {
154+
case strings.HasPrefix(low, "cpe:2.3:"):
155+
_, err := naming.UnbindFS(ls)
156+
return err == nil
157+
case strings.HasPrefix(low, "cpe:/"):
158+
_, err := naming.UnbindURI(ls)
159+
return err == nil
160+
default:
161+
return false
162+
}
163+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2025 Interlynk.io
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package extractors
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright 2025 Interlynk.io
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package extractors
16+
17+
import (
18+
"github.com/interlynk-io/sbomqs/pkg/sbom"
19+
"github.com/interlynk-io/sbomqs/pkg/scorer/v2/config"
20+
"github.com/interlynk-io/sbomqs/pkg/scorer/v2/formulae"
21+
"github.com/samber/lo"
22+
)
23+
24+
// CompWithPURL check for PURLs
25+
func CompWithPURL(doc sbom.Document) config.FeatureScore {
26+
comps := doc.Components()
27+
if len(comps) == 0 {
28+
return formulae.ScoreNA()
29+
}
30+
31+
have := lo.CountBy(comps, func(c sbom.GetComponent) bool {
32+
return compHasAnyPURLs(c)
33+
})
34+
35+
return config.FeatureScore{
36+
Score: formulae.PerComponentScore(have, len(comps)),
37+
Desc: formulae.CompDescription(have, len(comps), "PURLs"),
38+
Ignore: false,
39+
}
40+
}
41+
42+
// CompWithCPE: percentage of components that have at least one syntactically valid CPE 2.3.
43+
func CompWithCPE(doc sbom.Document) config.FeatureScore {
44+
comps := doc.Components()
45+
if len(comps) == 0 {
46+
return formulae.ScoreNA()
47+
}
48+
49+
have := lo.CountBy(comps, func(c sbom.GetComponent) bool {
50+
return compHasAnyCPEs(c)
51+
})
52+
53+
return config.FeatureScore{
54+
Score: formulae.PerComponentScore(have, len(comps)),
55+
Desc: formulae.CompDescription(have, len(comps), "CPEs"),
56+
Ignore: false,
57+
}
58+
}

0 commit comments

Comments
 (0)