Issues (121)

report/cve_client.go (9 issues)

Severity
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 report
19
20
import (
21
	"encoding/json"
22
	"fmt"
23
	"net/http"
24
	"time"
25
26
	"github.com/cenkalti/backoff"
27
	"github.com/parnurzeal/gorequest"
28
	"golang.org/x/xerrors"
29
30
	"github.com/future-architect/vuls/config"
31
	"github.com/future-architect/vuls/util"
32
	cvedb "github.com/kotakanbe/go-cve-dictionary/db"
33
	cve "github.com/kotakanbe/go-cve-dictionary/models"
34
)
35
36
// CveClient is api client of CVE disctionary service.
37
var CveClient cvedictClient
38
39
type cvedictClient struct {
40
	//  httpProxy string
41
	baseURL string
42
}
43
44
func (api *cvedictClient) initialize() {
45
	api.baseURL = config.Conf.CveDict.URL
46
}
47
48
func (api cvedictClient) CheckHealth() error {
49
	if !config.Conf.CveDict.IsFetchViaHTTP() {
50
		util.Log.Debugf("get cve-dictionary from %s", config.Conf.CveDict.Type)
51
		return nil
52
	}
53
54
	api.initialize()
55
	url := fmt.Sprintf("%s/health", api.baseURL)
56
	var errs []error
57
	var resp *http.Response
58
	resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
59
	//  resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
60
	if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
61
		return xerrors.Errorf("Failed to request to CVE server. url: %s, errs: %w",
0 ignored issues
show
unrecognized printf verb 'w'
Loading history...
62
			url, errs)
63
	}
64
	return nil
65
}
66
67
type response struct {
68
	Key       string
69
	CveDetail cve.CveDetail
70
}
71
72
func (api cvedictClient) FetchCveDetails(driver cvedb.DB, cveIDs []string) (cveDetails []cve.CveDetail, err error) {
73
	if !config.Conf.CveDict.IsFetchViaHTTP() {
74
		for _, cveID := range cveIDs {
75
			cveDetail, err := driver.Get(cveID)
76
			if err != nil {
77
				return nil, xerrors.Errorf("Failed to fetch CVE. err: %w", err)
0 ignored issues
show
unrecognized printf verb 'w'
Loading history...
78
			}
79
			if len(cveDetail.CveID) == 0 {
80
				cveDetails = append(cveDetails, cve.CveDetail{
81
					CveID: cveID,
82
				})
83
			} else {
84
				cveDetails = append(cveDetails, *cveDetail)
85
			}
86
		}
87
		return
88
	}
89
90
	api.baseURL = config.Conf.CveDict.URL
91
	reqChan := make(chan string, len(cveIDs))
92
	resChan := make(chan response, len(cveIDs))
93
	errChan := make(chan error, len(cveIDs))
94
	defer close(reqChan)
95
	defer close(resChan)
96
	defer close(errChan)
97
98
	go func() {
99
		for _, cveID := range cveIDs {
100
			reqChan <- cveID
101
		}
102
	}()
103
104
	concurrency := 10
105
	tasks := util.GenWorkers(concurrency)
106
	for range cveIDs {
107
		tasks <- func() {
108
			select {
109
			case cveID := <-reqChan:
110
				url, err := util.URLPathJoin(api.baseURL, "cves", cveID)
111
				if err != nil {
112
					errChan <- err
113
				} else {
114
					util.Log.Debugf("HTTP Request to %s", url)
115
					api.httpGet(cveID, url, resChan, errChan)
116
				}
117
			}
118
		}
119
	}
120
121
	timeout := time.After(2 * 60 * time.Second)
122
	var errs []error
123
	for range cveIDs {
124
		select {
125
		case res := <-resChan:
126
			if len(res.CveDetail.CveID) == 0 {
127
				cveDetails = append(cveDetails, cve.CveDetail{
128
					CveID: res.Key,
129
				})
130
			} else {
131
				cveDetails = append(cveDetails, res.CveDetail)
132
			}
133
		case err := <-errChan:
134
			errs = append(errs, err)
135
		case <-timeout:
136
			return nil, xerrors.New("Timeout Fetching CVE")
137
		}
138
	}
139
	if len(errs) != 0 {
140
		return nil,
141
			xerrors.Errorf("Failed to fetch CVE. err: %w", errs)
0 ignored issues
show
unrecognized printf verb 'w'
Loading history...
142
	}
143
	return
144
}
145
146
func (api cvedictClient) httpGet(key, url string, resChan chan<- response, errChan chan<- error) {
147
	var body string
148
	var errs []error
149
	var resp *http.Response
150
	f := func() (err error) {
151
		//  resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
152
		resp, body, errs = gorequest.New().Get(url).End()
153
		if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
154
			return xerrors.Errorf("HTTP GET Error, url: %s, resp: %v, err: %w",
0 ignored issues
show
unrecognized printf verb 'w'
Loading history...
155
				url, resp, errs)
156
		}
157
		return nil
158
	}
159
	notify := func(err error, t time.Duration) {
160
		util.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %s",
161
			t, err)
162
	}
163
	err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
164
	if err != nil {
165
		errChan <- xerrors.Errorf("HTTP Error: %w", err)
0 ignored issues
show
unrecognized printf verb 'w'
Loading history...
166
		return
167
	}
168
	cveDetail := cve.CveDetail{}
169
	if err := json.Unmarshal([]byte(body), &cveDetail); err != nil {
170
		errChan <- xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
0 ignored issues
show
unrecognized printf verb 'w'
Loading history...
171
		return
172
	}
173
	resChan <- response{
174
		key,
175
		cveDetail,
176
	}
177
}
178
179
func (api cvedictClient) FetchCveDetailsByCpeName(driver cvedb.DB, cpeName string) ([]cve.CveDetail, error) {
180
	if config.Conf.CveDict.IsFetchViaHTTP() {
181
		api.baseURL = config.Conf.CveDict.URL
182
		url, err := util.URLPathJoin(api.baseURL, "cpes")
183
		if err != nil {
184
			return nil, err
185
		}
186
187
		query := map[string]string{"name": cpeName}
188
		util.Log.Debugf("HTTP Request to %s, query: %#v", url, query)
189
		return api.httpPost(cpeName, url, query)
190
	}
191
	return driver.GetByCpeURI(cpeName)
192
}
193
194
func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]cve.CveDetail, error) {
195
	var body string
196
	var errs []error
197
	var resp *http.Response
198
	f := func() (err error) {
199
		//  req := gorequest.New().SetDebug(config.Conf.Debug).Post(url)
200
		req := gorequest.New().Post(url)
201
		for key := range query {
202
			req = req.Send(fmt.Sprintf("%s=%s", key, query[key])).Type("json")
203
		}
204
		resp, body, errs = req.End()
205
		if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
206
			return xerrors.Errorf("HTTP POST error. url: %s, resp: %v, err: %w", url, resp, errs)
0 ignored issues
show
unrecognized printf verb 'w'
Loading history...
207
		}
208
		return nil
209
	}
210
	notify := func(err error, t time.Duration) {
211
		util.Log.Warnf("Failed to HTTP POST. retrying in %s seconds. err: %s", t, err)
212
	}
213
	err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
214
	if err != nil {
215
		return nil, xerrors.Errorf("HTTP Error: %w", err)
0 ignored issues
show
unrecognized printf verb 'w'
Loading history...
216
	}
217
218
	cveDetails := []cve.CveDetail{}
219
	if err := json.Unmarshal([]byte(body), &cveDetails); err != nil {
220
		return nil,
221
			xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
0 ignored issues
show
unrecognized printf verb 'w'
Loading history...
222
	}
223
	return cveDetails, nil
224
}
225