-
Notifications
You must be signed in to change notification settings - Fork 518
/
purl_provider.go
125 lines (101 loc) · 2.69 KB
/
purl_provider.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package pkg
import (
"bufio"
"fmt"
"io"
"os"
"strings"
"github.com/mitchellh/go-homedir"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/pkg"
)
const (
purlInputPrefix = "purl:"
cpesQualifierKey = "cpes"
)
type errEmptyPurlFile struct {
purlFilepath string
}
func (e errEmptyPurlFile) Error() string {
return fmt.Sprintf("purl file is empty: %s", e.purlFilepath)
}
func purlProvider(userInput string) ([]Package, error) {
p, err := getPurlPackages(userInput)
return p, err
}
func getPurlPackages(userInput string) ([]Package, error) {
reader, err := getPurlReader(userInput)
if err != nil {
return nil, err
}
return decodePurlFile(reader)
}
func decodePurlFile(reader io.Reader) ([]Package, error) {
scanner := bufio.NewScanner(reader)
packages := []Package{}
for scanner.Scan() {
rawLine := scanner.Text()
purl, err := packageurl.FromString(rawLine)
if err != nil {
return nil, fmt.Errorf("unable to decode purl %s: %w", rawLine, err)
}
cpes := []cpe.CPE{}
epoch := "0"
for _, qualifier := range purl.Qualifiers {
if qualifier.Key == cpesQualifierKey {
rawCpes := strings.Split(qualifier.Value, ",")
for _, rawCpe := range rawCpes {
c, err := cpe.New(rawCpe, "")
if err != nil {
return nil, fmt.Errorf("unable to decode cpe %s in purl %s: %w", rawCpe, rawLine, err)
}
cpes = append(cpes, c)
}
}
if qualifier.Key == "epoch" {
epoch = qualifier.Value
}
}
if purl.Type == packageurl.TypeRPM && !strings.HasPrefix(purl.Version, fmt.Sprintf("%s:", epoch)) {
purl.Version = fmt.Sprintf("%s:%s", epoch, purl.Version)
}
packages = append(packages, Package{
ID: ID(purl.String()),
CPEs: cpes,
Name: purl.Name,
Version: purl.Version,
Type: pkg.TypeByName(purl.Type),
Language: pkg.LanguageByName(purl.Type),
PURL: purl.String(),
})
}
if err := scanner.Err(); err != nil {
return nil, err
}
return packages, nil
}
func getPurlReader(userInput string) (r io.Reader, err error) {
if !explicitlySpecifyingPurl(userInput) {
return nil, errDoesNotProvide
}
path := strings.TrimPrefix(userInput, purlInputPrefix)
return openPurlFile(path)
}
func openPurlFile(path string) (*os.File, error) {
expandedPath, err := homedir.Expand(path)
if err != nil {
return nil, fmt.Errorf("unable to open purls: %w", err)
}
f, err := os.Open(expandedPath)
if err != nil {
return nil, fmt.Errorf("unable to open file %s: %w", expandedPath, err)
}
if !fileHasContent(f) {
return nil, errEmptyPurlFile{path}
}
return f, nil
}
func explicitlySpecifyingPurl(userInput string) bool {
return strings.HasPrefix(userInput, purlInputPrefix)
}