Completed
Push — master ( 256c99...56d7d4 )
by kota
05:33
created

github.FillGitHubSecurityAlerts   C

Complexity

Conditions 11

Size

Total Lines 79
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 51
dl 0
loc 79
rs 5.3836
c 0
b 0
f 0
nop 4

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like github.FillGitHubSecurityAlerts often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/* Vuls - Vulnerability Scanner
2
Copyright (C) 2016  Future Corporation , Japan.
3
4
This program is free software: you can redistribute it and/or modify
5
it under the terms of the GNU General Public License as published by
6
the Free Software Foundation, either version 3 of the License, or
7
(at your option) any later version.
8
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
GNU General Public License for more details.
13
14
You should have received a copy of the GNU General Public License
15
along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
18
package github
19
20
import (
21
	"bytes"
22
	"context"
23
	"encoding/json"
24
	"fmt"
25
	"net/http"
26
	"time"
27
28
	"github.com/future-architect/vuls/config"
29
	"github.com/future-architect/vuls/models"
30
	"github.com/future-architect/vuls/util"
31
	"github.com/k0kubun/pp"
32
	"golang.org/x/oauth2"
33
)
34
35
// FillGitHubSecurityAlerts access to owner/repo on GitHub and fetch scurity alerts of the repository via GitHub API v4 GraphQL and then set to the given ScanResult.
36
// https://help.github.com/articles/about-security-alerts-for-vulnerable-dependencies/
37
func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (nCVEs int, err error) {
38
	src := oauth2.StaticTokenSource(
39
		&oauth2.Token{AccessToken: token},
40
	)
41
	httpClient := oauth2.NewClient(context.Background(), src)
42
43
	// TODO Use `https://github.com/shurcooL/githubv4` if the tool supports vulnerabilityAlerts Endpoint
44
	const jsonfmt = `{"query":
45
	"query { repository(owner:\"%s\", name:\"%s\") { url, vulnerabilityAlerts(first: %d, %s) { pageInfo{ endCursor, hasNextPage, startCursor}, edges { node { id, externalIdentifier, externalReference, fixedIn, packageName,  dismissReason, dismissedAt } } } } }"}`
46
	after := ""
47
48
	for {
49
		jsonStr := fmt.Sprintf(jsonfmt, owner, repo, 100, after)
50
		req, err := http.NewRequest("POST",
51
			"https://api.github.com/graphql",
52
			bytes.NewBuffer([]byte(jsonStr)),
53
		)
54
		if err != nil {
55
			return 0, err
56
		}
57
58
		// https://developer.github.com/v4/previews/#repository-vulnerability-alerts
59
		// To toggle this preview and access data, need to provide a custom media type in the Accept header:
60
		// MEMO: I tried to get the affected version via GitHub API. Bit it seems difficult to determin the affected version if there are multiple dependency files such as package.json.
61
		// TODO remove this header if it is no longer preview status in the future.
62
		req.Header.Set("Accept", "application/vnd.github.vixen-preview+json")
63
		req.Header.Set("Content-Type", "application/json")
64
65
		resp, err := httpClient.Do(req)
66
		if err != nil {
67
			return 0, err
68
		}
69
		defer resp.Body.Close()
70
		alerts := SecurityAlerts{}
71
		if json.NewDecoder(resp.Body).Decode(&alerts); err != nil {
72
			return 0, err
73
		}
74
75
		util.Log.Debugf("%s", pp.Sprint(alerts))
76
77
		for _, v := range alerts.Data.Repository.VulnerabilityAlerts.Edges {
78
			if config.Conf.IgnoreGitHubDismissed && v.Node.DismissReason != "" {
79
				continue
80
			}
81
82
			pkgName := fmt.Sprintf("%s %s",
83
				alerts.Data.Repository.URL, v.Node.PackageName)
84
85
			m := models.GitHubSecurityAlert{
86
				PackageName:   pkgName,
87
				FixedIn:       v.Node.FixedIn,
88
				AffectedRange: v.Node.AffectedRange,
89
				Dismissed:     len(v.Node.DismissReason) != 0,
90
				DismissedAt:   v.Node.DismissedAt,
91
				DismissReason: v.Node.DismissReason,
92
			}
93
94
			cveID := v.Node.ExternalIdentifier
95
96
			if val, ok := r.ScannedCves[cveID]; ok {
97
				val.GitHubSecurityAlerts = val.GitHubSecurityAlerts.Add(m)
98
				r.ScannedCves[cveID] = val
99
				nCVEs++
100
			} else {
101
				v := models.VulnInfo{
102
					CveID:                cveID,
103
					Confidences:          models.Confidences{models.GitHubMatch},
104
					GitHubSecurityAlerts: models.GitHubSecurityAlerts{m},
105
				}
106
				r.ScannedCves[cveID] = v
107
				nCVEs++
108
			}
109
		}
110
		if !alerts.Data.Repository.VulnerabilityAlerts.PageInfo.HasNextPage {
111
			break
112
		}
113
		after = fmt.Sprintf(`after: \"%s\"`, alerts.Data.Repository.VulnerabilityAlerts.PageInfo.EndCursor)
114
	}
115
	return nCVEs, err
116
}
117
118
//SecurityAlerts has detected CVE-IDs, PackageNames, Refs
119
type SecurityAlerts struct {
120
	Data struct {
121
		Repository struct {
122
			URL                 string `json:"url,omitempty"`
123
			VulnerabilityAlerts struct {
124
				PageInfo struct {
125
					EndCursor   string `json:"endCursor,omitempty"`
126
					HasNextPage bool   `json:"hasNextPage,omitempty"`
127
					StartCursor string `json:"startCursor,omitempty"`
128
				} `json:"pageInfo,omitempty"`
129
				Edges []struct {
130
					Node struct {
131
						ID                 string    `json:"id,omitempty"`
132
						ExternalIdentifier string    `json:"externalIdentifier,omitempty"`
133
						ExternalReference  string    `json:"externalReference,omitempty"`
134
						FixedIn            string    `json:"fixedIn,omitempty"`
135
						AffectedRange      string    `json:"affectedRange,omitempty"`
136
						PackageName        string    `json:"packageName,omitempty"`
137
						DismissReason      string    `json:"dismissReason,omitempty"`
138
						DismissedAt        time.Time `json:"dismissedAt,omitempty"`
139
					} `json:"node,omitempty"`
140
				} `json:"edges,omitempty"`
141
			} `json:"vulnerabilityAlerts,omitempty"`
142
		} `json:"repository,omitempty"`
143
	} `json:"data,omitempty"`
144
}
145