-
Notifications
You must be signed in to change notification settings - Fork 0
/
client.go
135 lines (116 loc) · 3.38 KB
/
client.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
134
135
package jfrog
import (
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"net/url"
"path/filepath"
"github.com/google/go-containerregistry/pkg/name"
"github.com/jfrog/jfrog-client-go/config"
"github.com/jfrog/jfrog-client-go/http/jfroghttpclient"
"github.com/jfrog/jfrog-client-go/utils/io/httputils"
"github.com/jfrog/jfrog-client-go/xray"
"github.com/jfrog/jfrog-client-go/xray/auth"
"golang.org/x/xerrors"
)
type Client interface {
ScanResults(img Image) (ScanResult, error)
ResultsURL(img Image, packageID string) string
}
type client struct {
client *jfroghttpclient.JfrogHttpClient
baseURL string
token string
user string
}
func XRayClient(url, user, token string) (Client, error) {
details := auth.NewXrayDetails()
details.SetAccessToken(token)
details.SetUser(user)
details.SetUrl(url)
conf, err := config.NewConfigBuilder().SetServiceDetails(details).Build()
if err != nil {
return nil, xerrors.Errorf("new config builder: %w", err)
}
mgr, err := xray.New(conf)
if err != nil {
return nil, xerrors.Errorf("new xray manager: %w", err)
}
return &client{
client: mgr.Client(),
baseURL: url,
user: user,
token: token,
}, nil
}
type securityResultsPayload struct {
Data []ScanResult `json:"data"`
Offset int `json:"offset"`
}
type ScanResult struct {
Version string `json:"version"`
SecurityIssues SecurityIssues `json:"sec_issues"`
PackageID string `json:"package_id"`
}
type SecurityIssues struct {
Critical int `json:"critical"`
High int `json:"high"`
Medium int `json:"medium"`
Total int `json:"total"`
}
func (c *client) ScanResults(img Image) (ScanResult, error) {
b64str := base64.URLEncoding.EncodeToString([]byte(img.Package))
path := fmt.Sprintf("%s/xray/api/v1/packages/%s/versions?search=%s&base64id=true", c.baseURL, b64str, img.Version)
resp, body, _, err := c.client.SendGet(path, true, &httputils.HttpClientDetails{
User: c.user,
AccessToken: c.token,
})
if err != nil {
return ScanResult{}, xerrors.Errorf("send get: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return ScanResult{}, xerrors.Errorf("unexpected status code %d body (%s)", resp.StatusCode, body)
}
var payload securityResultsPayload
err = json.Unmarshal(body, &payload)
if err != nil {
return ScanResult{}, xerrors.Errorf("unmarshal (%s): %w", body, err)
}
if len(payload.Data) == 0 {
return ScanResult{}, xerrors.Errorf("no results")
}
return payload.Data[0], nil
}
type Image struct {
Repo string
Package string
Version string
}
func (c *client) ResultsURL(img Image, packageID string) string {
return fmt.Sprintf("%s/ui/scans-list/packages-scans/%s/scan-descendants/%s?package_id=%s&version=%s", c.baseURL, url.PathEscape(img.Package), url.PathEscape(img.Version), url.QueryEscape(packageID), url.QueryEscape(img.Version))
}
func ParseImage(image string) (Image, error) {
tag, err := name.NewTag(image)
if err != nil {
return Image{}, xerrors.Errorf("new tag: %w", err)
}
repo := root(tag.RepositoryStr())
pkg, err := filepath.Rel(repo, tag.RepositoryStr())
if err != nil {
return Image{}, xerrors.Errorf("rel path between %q and %q", repo, tag.RegistryStr())
}
return Image{
Repo: repo,
Package: pkg,
Version: tag.TagStr(),
}, nil
}
func root(path string) string {
dir := filepath.Dir(path)
if dir == "." {
return path
}
return root(dir)
}