-
Notifications
You must be signed in to change notification settings - Fork 109
/
parse.go
133 lines (115 loc) · 2.99 KB
/
parse.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
126
127
128
129
130
131
132
133
package conan
import (
"fmt"
"github.com/liamg/jfather"
"io"
"strings"
"golang.org/x/exp/slices"
"golang.org/x/xerrors"
dio "github.com/aquasecurity/go-dep-parser/pkg/io"
"github.com/aquasecurity/go-dep-parser/pkg/log"
"github.com/aquasecurity/go-dep-parser/pkg/types"
)
type LockFile struct {
GraphLock GraphLock `json:"graph_lock"`
}
type GraphLock struct {
Nodes map[string]Node `json:"nodes"`
}
type Node struct {
Ref string `json:"ref"`
Requires []string `json:"requires"`
StartLine int
EndLine int
}
type Parser struct{}
func NewParser() types.Parser {
return &Parser{}
}
func (p *Parser) Parse(r dio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
var lock LockFile
input, err := io.ReadAll(r)
if err != nil {
return nil, nil, xerrors.Errorf("failed to read canon lock file: %w", err)
}
if err := jfather.Unmarshal(input, &lock); err != nil {
return nil, nil, xerrors.Errorf("failed to decode canon lock file: %w", err)
}
// Get a list of direct dependencies
var directDeps []string
if root, ok := lock.GraphLock.Nodes["0"]; ok {
directDeps = root.Requires
}
// Parse packages
parsed := map[string]types.Library{}
for i, node := range lock.GraphLock.Nodes {
if node.Ref == "" {
continue
}
lib, err := parseRef(node)
if err != nil {
log.Logger.Debug(err)
continue
}
// Determine if the package is a direct dependency or not
direct := slices.Contains(directDeps, i)
lib.Indirect = !direct
parsed[i] = lib
}
// Parse dependency graph
var libs []types.Library
var deps []types.Dependency
for i, node := range lock.GraphLock.Nodes {
lib, ok := parsed[i]
if !ok {
continue
}
var childDeps []string
for _, req := range node.Requires {
if child, ok := parsed[req]; ok {
childDeps = append(childDeps, child.ID)
}
}
if len(childDeps) != 0 {
deps = append(deps, types.Dependency{
ID: lib.ID,
DependsOn: childDeps,
})
}
libs = append(libs, lib)
}
return libs, deps, nil
}
func parseRef(node Node) (types.Library, error) {
// full ref format: package/version@user/channel#rrev:package_id#prev
// various examples:
// 'pkga/0.1@user/testing'
// 'pkgb/0.1.0'
// 'pkgc/system'
// 'pkgd/0.1.0#7dcb50c43a5a50d984c2e8fa5898bf18'
ss := strings.Split(strings.Split(strings.Split(node.Ref, "@")[0], "#")[0], "/")
if len(ss) != 2 {
return types.Library{}, xerrors.Errorf("Unable to determine conan dependency: %q", node.Ref)
}
return types.Library{
ID: fmt.Sprintf("%s/%s", ss[0], ss[1]),
Name: ss[0],
Version: ss[1],
Locations: []types.Location{
{
StartLine: node.StartLine,
EndLine: node.EndLine,
},
},
}, nil
}
// UnmarshalJSONWithMetadata needed to detect start and end lines of deps
func (n *Node) UnmarshalJSONWithMetadata(node jfather.Node) error {
if err := node.Decode(&n); err != nil {
return err
}
// Decode func will overwrite line numbers if we save them first
n.StartLine = node.Range().Start.Line
n.EndLine = node.Range().End.Line
return nil
}