report/report.go   F
last analyzed

Size/Duplication

Total Lines 710
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
cc 135
eloc 469
dl 0
loc 710
rs 2
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
F report.FillWithOval 0 64 18
A report.WordPressOption.apply 0 10 3
A report.FillWithGost 0 5 1
A report.FillWithExploit 0 4 1
A report.GithubSecurityAlerts 0 3 1
A report.GithubSecurityAlertOption.apply 0 13 3
A report.fillAlerts 0 12 2
D report.FillCveInfo 0 59 12
F report.FillCveInfos 0 101 21
B report.fillVulnByCpeURIs 0 25 5
C report.fillCweDict 0 41 11
F report.EnsureUUIDs 0 189 31
F report.cleanForTOMLEncoding 0 52 15
C report.fillCveDetail 0 33 11
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
	"bytes"
22
	"fmt"
23
	"io/ioutil"
24
	"os"
25
	"reflect"
26
	"regexp"
27
	"sort"
28
	"strings"
29
	"time"
30
31
	"github.com/BurntSushi/toml"
32
	"github.com/future-architect/vuls/config"
33
	c "github.com/future-architect/vuls/config"
34
	"github.com/future-architect/vuls/contrib/owasp-dependency-check/parser"
35
	"github.com/future-architect/vuls/cwe"
36
	"github.com/future-architect/vuls/exploit"
37
	"github.com/future-architect/vuls/github"
38
	"github.com/future-architect/vuls/gost"
39
	"github.com/future-architect/vuls/models"
40
	"github.com/future-architect/vuls/oval"
41
	"github.com/future-architect/vuls/util"
42
	"github.com/future-architect/vuls/wordpress"
43
	"github.com/hashicorp/uuid"
44
	gostdb "github.com/knqyf263/gost/db"
45
	cvedb "github.com/kotakanbe/go-cve-dictionary/db"
46
	ovaldb "github.com/kotakanbe/goval-dictionary/db"
47
	exploitdb "github.com/mozqnet/go-exploitdb/db"
48
	"golang.org/x/xerrors"
49
)
50
51
const (
52
	vulsOpenTag  = "<vulsreport>"
53
	vulsCloseTag = "</vulsreport>"
54
)
55
56
// FillCveInfos fills CVE Detailed Information
57
func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
58
	var filledResults []models.ScanResult
59
	reportedAt := time.Now()
60
	hostname, _ := os.Hostname()
61
	for _, r := range rs {
62
		if c.Conf.RefreshCve || needToRefreshCve(r) {
63
			if ovalSupported(&r) {
64
				r.ScannedCves = models.VulnInfos{}
65
			}
66
			cpeURIs := []string{}
67
			if len(r.Container.ContainerID) == 0 {
68
				cpeURIs = c.Conf.Servers[r.ServerName].CpeNames
69
				owaspDCXMLPath := c.Conf.Servers[r.ServerName].OwaspDCXMLPath
70
				if owaspDCXMLPath != "" {
71
					cpes, err := parser.Parse(owaspDCXMLPath)
72
					if err != nil {
73
						return nil, xerrors.Errorf("Failed to read OWASP Dependency Check XML on %s, `%s`, err: %w",
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
74
							r.ServerName, owaspDCXMLPath, err)
75
					}
76
					cpeURIs = append(cpeURIs, cpes...)
77
				}
78
			} else {
79
				if s, ok := c.Conf.Servers[r.ServerName]; ok {
80
					if con, ok := s.Containers[r.Container.Name]; ok {
81
						cpeURIs = con.Cpes
82
						owaspDCXMLPath := con.OwaspDCXMLPath
83
						if owaspDCXMLPath != "" {
84
							cpes, err := parser.Parse(owaspDCXMLPath)
85
							if err != nil {
86
								return nil, xerrors.Errorf("Failed to read OWASP Dependency Check XML on %s, `%s`, err: %w",
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
87
									r.ServerInfo(), owaspDCXMLPath, err)
88
							}
89
							cpeURIs = append(cpeURIs, cpes...)
90
						}
91
					}
92
				}
93
			}
94
95
			// Integrations
96
			githubInts := GithubSecurityAlerts(c.Conf.Servers[r.ServerName].GitHubRepos)
97
98
			wpOpt := WordPressOption{c.Conf.Servers[r.ServerName].WordPress.WPVulnDBToken}
99
100
			if err := FillCveInfo(dbclient,
101
				&r,
102
				cpeURIs,
103
				githubInts,
104
				wpOpt); err != nil {
105
				return nil, err
106
			}
107
			r.Lang = c.Conf.Lang
108
			r.ReportedAt = reportedAt
109
			r.ReportedVersion = c.Version
110
			r.ReportedRevision = c.Revision
111
			r.ReportedBy = hostname
112
			r.Config.Report = c.Conf
113
			r.Config.Report.Servers = map[string]c.ServerInfo{
114
				r.ServerName: c.Conf.Servers[r.ServerName],
115
			}
116
			if err := overwriteJSONFile(dir, r); err != nil {
117
				return nil, xerrors.Errorf("Failed to write JSON: %w", err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
118
			}
119
			filledResults = append(filledResults, r)
120
		} else {
121
			util.Log.Debugf("No need to refresh")
122
			filledResults = append(filledResults, r)
123
		}
124
	}
125
126
	if c.Conf.Diff {
127
		prevs, err := loadPrevious(filledResults)
128
		if err != nil {
129
			return nil, err
130
		}
131
132
		diff, err := diff(filledResults, prevs)
133
		if err != nil {
134
			return nil, err
135
		}
136
		filledResults = []models.ScanResult{}
137
		for _, r := range diff {
138
			if err := fillCveDetail(dbclient.CveDB, &r); err != nil {
139
				return nil, err
140
			}
141
			filledResults = append(filledResults, r)
142
		}
143
	}
144
145
	filtered := []models.ScanResult{}
146
	for _, r := range filledResults {
147
		r = r.FilterByCvssOver(c.Conf.CvssScoreOver)
148
		r = r.FilterIgnoreCves()
149
		r = r.FilterUnfixed()
150
		r = r.FilterIgnorePkgs()
151
		r = r.FilterInactiveWordPressLibs()
152
		if c.Conf.IgnoreUnscoredCves {
153
			r.ScannedCves = r.ScannedCves.FindScoredVulns()
154
		}
155
		filtered = append(filtered, r)
156
	}
157
	return filtered, nil
158
}
159
160
// FillCveInfo fill scanResult with cve info.
161
func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string, integrations ...Integration) error {
162
	util.Log.Debugf("need to refresh")
163
164
	nCVEs, err := FillWithOval(dbclient.OvalDB, r)
165
	if err != nil {
166
		return xerrors.Errorf("Failed to fill with OVAL: %w", err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
167
	}
168
	util.Log.Infof("%s: %d CVEs are detected with OVAL",
169
		r.FormatServerName(), nCVEs)
170
171
	for i, v := range r.ScannedCves {
172
		for j, p := range v.AffectedPackages {
173
			if p.NotFixedYet && p.FixState == "" {
174
				p.FixState = "Not fixed yet"
175
				r.ScannedCves[i].AffectedPackages[j] = p
176
			}
177
		}
178
	}
179
180
	nCVEs, err = fillVulnByCpeURIs(dbclient.CveDB, r, cpeURIs)
181
	if err != nil {
182
		return xerrors.Errorf("Failed to detect vulns of `%s`: %w", cpeURIs, err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
183
	}
184
	util.Log.Infof("%s: %d CVEs are detected with CPE", r.FormatServerName(), nCVEs)
185
186
	ints := &integrationResults{}
187
	for _, o := range integrations {
188
		if err = o.apply(r, ints); err != nil {
189
			return xerrors.Errorf("Failed to fill with integration: %w", err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
190
		}
191
	}
192
	util.Log.Infof("%s: %d CVEs are detected with GitHub Security Alerts", r.FormatServerName(), ints.GithubAlertsCveCounts)
193
194
	nCVEs, err = FillWithGost(dbclient.GostDB, r)
195
	if err != nil {
196
		return xerrors.Errorf("Failed to fill with gost: %w", err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
197
	}
198
	util.Log.Infof("%s: %d unfixed CVEs are detected with gost",
199
		r.FormatServerName(), nCVEs)
200
201
	util.Log.Infof("Fill CVE detailed information with CVE-DB")
202
	if err := fillCveDetail(dbclient.CveDB, r); err != nil {
203
		return xerrors.Errorf("Failed to fill with CVE: %w", err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
204
	}
205
206
	util.Log.Infof("Fill exploit information with Exploit-DB")
207
	nExploitCve, err := FillWithExploit(dbclient.ExploitDB, r)
208
	if err != nil {
209
		return xerrors.Errorf("Failed to fill with exploit: %w", err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
210
	}
211
	util.Log.Infof("%s: %d exploits are detected",
212
		r.FormatServerName(), nExploitCve)
213
214
	enAlertCnt, jaAlertCnt := fillAlerts(r)
215
	util.Log.Infof("%s: en: %d, ja: %d alerts are detected",
216
		r.FormatServerName(), enAlertCnt, jaAlertCnt)
217
218
	fillCweDict(r)
219
	return nil
220
}
221
222
// fillCveDetail fetches NVD, JVN from CVE Database
223
func fillCveDetail(driver cvedb.DB, r *models.ScanResult) error {
224
	var cveIDs []string
225
	for _, v := range r.ScannedCves {
226
		cveIDs = append(cveIDs, v.CveID)
227
	}
228
229
	ds, err := CveClient.FetchCveDetails(driver, cveIDs)
230
	if err != nil {
231
		return err
232
	}
233
	for _, d := range ds {
234
		nvd := models.ConvertNvdJSONToModel(d.CveID, d.NvdJSON)
235
		if nvd == nil {
236
			nvd = models.ConvertNvdXMLToModel(d.CveID, d.NvdXML)
237
		}
238
		jvn := models.ConvertJvnToModel(d.CveID, d.Jvn)
239
240
		for cveID, vinfo := range r.ScannedCves {
241
			if vinfo.CveID == d.CveID {
242
				if vinfo.CveContents == nil {
243
					vinfo.CveContents = models.CveContents{}
244
				}
245
				for _, con := range []*models.CveContent{nvd, jvn} {
246
					if con != nil && !con.Empty() {
247
						vinfo.CveContents[con.Type] = *con
248
					}
249
				}
250
				r.ScannedCves[cveID] = vinfo
251
				break
252
			}
253
		}
254
	}
255
	return nil
256
}
257
258
// FillWithOval fetches OVAL database
259
func FillWithOval(driver ovaldb.DB, r *models.ScanResult) (nCVEs int, err error) {
260
	var ovalClient oval.Client
261
	var ovalFamily string
262
263
	switch r.Family {
264
	case c.Debian:
265
		ovalClient = oval.NewDebian()
266
		ovalFamily = c.Debian
267
	case c.Ubuntu:
268
		ovalClient = oval.NewUbuntu()
269
		ovalFamily = c.Ubuntu
270
	case c.RedHat:
271
		ovalClient = oval.NewRedhat()
272
		ovalFamily = c.RedHat
273
	case c.CentOS:
274
		ovalClient = oval.NewCentOS()
275
		//use RedHat's OVAL
276
		ovalFamily = c.RedHat
277
	case c.Oracle:
278
		ovalClient = oval.NewOracle()
279
		ovalFamily = c.Oracle
280
	case c.SUSEEnterpriseServer:
281
		// TODO other suse family
282
		ovalClient = oval.NewSUSE()
283
		ovalFamily = c.SUSEEnterpriseServer
284
	case c.Alpine:
285
		ovalClient = oval.NewAlpine()
286
		ovalFamily = c.Alpine
287
	case c.Amazon, c.Raspbian, c.FreeBSD, c.Windows:
288
		return 0, nil
289
	case c.ServerTypePseudo:
290
		return 0, nil
291
	default:
292
		if r.Family == "" {
293
			return 0, xerrors.New("Probably an error occurred during scanning. Check the error message")
294
		}
295
		return 0, xerrors.Errorf("OVAL for %s is not implemented yet", r.Family)
296
	}
297
298
	if !c.Conf.OvalDict.IsFetchViaHTTP() {
299
		if driver == nil {
300
			return 0, nil
301
		}
302
		if err = driver.NewOvalDB(ovalFamily); err != nil {
303
			return 0, xerrors.Errorf("Failed to New Oval DB. err: %w", err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
304
		}
305
	}
306
307
	util.Log.Debugf("Check whether oval fetched: %s %s", ovalFamily, r.Release)
308
	ok, err := ovalClient.CheckIfOvalFetched(driver, ovalFamily, r.Release)
309
	if err != nil {
310
		return 0, err
311
	}
312
	if !ok {
313
		util.Log.Warnf("OVAL entries of %s %s are not found. It's recommended to use OVAL to improve scanning accuracy. For details, see https://github.com/kotakanbe/goval-dictionary#usage , Then report with --ovaldb-path or --ovaldb-url flag", ovalFamily, r.Release)
314
		return 0, nil
315
	}
316
317
	_, err = ovalClient.CheckIfOvalFresh(driver, ovalFamily, r.Release)
318
	if err != nil {
319
		return 0, err
320
	}
321
322
	return ovalClient.FillWithOval(driver, r)
323
}
324
325
// FillWithGost fills CVEs with gost dataabase
326
// https://github.com/knqyf263/gost
327
func FillWithGost(driver gostdb.DB, r *models.ScanResult) (nCVEs int, err error) {
328
	gostClient := gost.NewClient(r.Family)
329
	// TODO chekc if fetched
330
	// TODO chekc if fresh enough
331
	return gostClient.FillWithGost(driver, r)
332
}
333
334
// FillWithExploit fills Exploits with exploit dataabase
335
// https://github.com/mozqnet/go-exploitdb
336
func FillWithExploit(driver exploitdb.DB, r *models.ScanResult) (nExploitCve int, err error) {
337
	// TODO chekc if fetched
338
	// TODO chekc if fresh enough
339
	return exploit.FillWithExploit(driver, r)
340
}
341
342
func fillVulnByCpeURIs(driver cvedb.DB, r *models.ScanResult, cpeURIs []string) (nCVEs int, err error) {
343
	for _, name := range cpeURIs {
344
		details, err := CveClient.FetchCveDetailsByCpeName(driver, name)
345
		if err != nil {
346
			return 0, err
347
		}
348
		for _, detail := range details {
349
			if val, ok := r.ScannedCves[detail.CveID]; ok {
350
				names := val.CpeURIs
351
				names = util.AppendIfMissing(names, name)
352
				val.CpeURIs = names
353
				val.Confidences.AppendIfMissing(models.CpeNameMatch)
354
				r.ScannedCves[detail.CveID] = val
355
			} else {
356
				v := models.VulnInfo{
357
					CveID:       detail.CveID,
358
					CpeURIs:     []string{name},
359
					Confidences: models.Confidences{models.CpeNameMatch},
360
				}
361
				r.ScannedCves[detail.CveID] = v
362
				nCVEs++
363
			}
364
		}
365
	}
366
	return nCVEs, nil
367
}
368
369
type integrationResults struct {
370
	GithubAlertsCveCounts int
371
	WordPressCveCounts    int
372
}
373
374
// Integration is integration of vuls report
375
type Integration interface {
376
	apply(*models.ScanResult, *integrationResults) error
377
}
378
379
// GithubSecurityAlerts :
380
func GithubSecurityAlerts(githubConfs map[string]config.GitHubConf) Integration {
381
	return GithubSecurityAlertOption{
382
		GithubConfs: githubConfs,
383
	}
384
}
385
386
// GithubSecurityAlertOption :
387
type GithubSecurityAlertOption struct {
388
	GithubConfs map[string]config.GitHubConf
389
}
390
391
// https://help.github.com/articles/about-security-alerts-for-vulnerable-dependencies/
392
func (g GithubSecurityAlertOption) apply(r *models.ScanResult, ints *integrationResults) (err error) {
393
	var nCVEs int
394
	for ownerRepo, setting := range g.GithubConfs {
395
		ss := strings.Split(ownerRepo, "/")
396
		owner, repo := ss[0], ss[1]
397
		n, err := github.FillGitHubSecurityAlerts(r, owner, repo, setting.Token)
398
		if err != nil {
399
			return xerrors.Errorf("Failed to access GitHub Security Alerts: %w", err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
400
		}
401
		nCVEs += n
402
	}
403
	ints.GithubAlertsCveCounts = nCVEs
404
	return nil
405
}
406
407
// WordPressOption :
408
type WordPressOption struct {
409
	token string
410
}
411
412
func (g WordPressOption) apply(r *models.ScanResult, ints *integrationResults) (err error) {
413
	if g.token == "" {
414
		return nil
415
	}
416
	n, err := wordpress.FillWordPress(r, g.token)
417
	if err != nil {
418
		return xerrors.Errorf("Failed to fetch from WPVulnDB. Check the WPVulnDBToken in config.toml. err: %w", err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
419
	}
420
	ints.WordPressCveCounts = n
421
	return nil
422
}
423
424
func fillCweDict(r *models.ScanResult) {
425
	uniqCweIDMap := map[string]bool{}
426
	for _, vinfo := range r.ScannedCves {
427
		for _, cont := range vinfo.CveContents {
428
			for _, id := range cont.CweIDs {
429
				if strings.HasPrefix(id, "CWE-") {
430
					id = strings.TrimPrefix(id, "CWE-")
431
					uniqCweIDMap[id] = true
432
				}
433
			}
434
		}
435
	}
436
437
	dict := map[string]models.CweDictEntry{}
438
	for id := range uniqCweIDMap {
439
		entry := models.CweDictEntry{}
440
		if e, ok := cwe.CweDictEn[id]; ok {
441
			if rank, ok := cwe.OwaspTopTen2017[id]; ok {
442
				entry.OwaspTopTen2017 = rank
443
			}
444
			entry.En = &e
445
		} else {
446
			util.Log.Debugf("CWE-ID %s is not found in English CWE Dict", id)
447
			entry.En = &cwe.Cwe{CweID: id}
448
		}
449
450
		if c.Conf.Lang == "ja" {
451
			if e, ok := cwe.CweDictJa[id]; ok {
452
				if rank, ok := cwe.OwaspTopTen2017[id]; ok {
453
					entry.OwaspTopTen2017 = rank
454
				}
455
				entry.Ja = &e
456
			} else {
457
				util.Log.Debugf("CWE-ID %s is not found in Japanese CWE Dict", id)
458
				entry.Ja = &cwe.Cwe{CweID: id}
459
			}
460
		}
461
		dict[id] = entry
462
	}
463
	r.CweDict = dict
464
	return
465
}
466
467
func fillAlerts(r *models.ScanResult) (enCnt int, jaCnt int) {
468
	for cveID, vuln := range r.ScannedCves {
469
		enAs, jaAs := models.GetAlertsByCveID(cveID, "en"), models.GetAlertsByCveID(cveID, "ja")
470
		vuln.AlertDict = models.AlertDict{
471
			Ja: jaAs,
472
			En: enAs,
473
		}
474
		r.ScannedCves[cveID] = vuln
475
		enCnt += len(enAs)
476
		jaCnt += len(jaAs)
477
	}
478
	return enCnt, jaCnt
479
}
480
481
const reUUID = "[\\da-f]{8}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{12}"
482
483
// EnsureUUIDs generate a new UUID of the scan target server if UUID is not assigned yet.
484
// And then set the generated UUID to config.toml and scan results.
485
func EnsureUUIDs(configPath string, results models.ScanResults) error {
486
	// Sort Host->Container
487
	sort.Slice(results, func(i, j int) bool {
488
		if results[i].ServerName == results[j].ServerName {
489
			return results[i].Container.ContainerID < results[j].Container.ContainerID
490
		}
491
		return results[i].ServerName < results[j].ServerName
492
	})
493
494
	for i, r := range results {
495
		server := c.Conf.Servers[r.ServerName]
496
		if server.UUIDs == nil {
497
			server.UUIDs = map[string]string{}
498
		}
499
500
		name := ""
501
		if r.IsContainer() {
502
			name = fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
503
504
			// Scanning with the -containers-only flag at scan time, the UUID of Container Host may not be generated,
505
			// so check it. Otherwise create a UUID of the Container Host and set it.
506
			serverUUID := ""
507
			if id, ok := server.UUIDs[r.ServerName]; !ok {
508
				serverUUID = uuid.GenerateUUID()
509
			} else {
510
				matched, err := regexp.MatchString(reUUID, id)
511
				if !matched || err != nil {
512
					serverUUID = uuid.GenerateUUID()
513
				}
514
			}
515
			if serverUUID != "" {
516
				server.UUIDs[r.ServerName] = serverUUID
517
			}
518
		} else {
519
			name = r.ServerName
520
		}
521
522
		if id, ok := server.UUIDs[name]; ok {
523
			matched, err := regexp.MatchString(reUUID, id)
524
			if !matched || err != nil {
525
				util.Log.Warnf("UUID is invalid. Re-generate UUID %s: %s", id, err)
526
			} else {
527
				if r.IsContainer() {
528
					results[i].Container.UUID = id
529
					results[i].ServerUUID = server.UUIDs[r.ServerName]
530
				} else {
531
					results[i].ServerUUID = id
532
				}
533
				// continue if the UUID has already assigned and valid
534
				continue
535
			}
536
		}
537
538
		// Generate a new UUID and set to config and scan result
539
		id := uuid.GenerateUUID()
540
		server.UUIDs[name] = id
541
		server = cleanForTOMLEncoding(server, c.Conf.Default)
542
		c.Conf.Servers[r.ServerName] = server
543
544
		if r.IsContainer() {
545
			results[i].Container.UUID = id
546
			results[i].ServerUUID = server.UUIDs[r.ServerName]
547
		} else {
548
			results[i].ServerUUID = id
549
		}
550
	}
551
552
	for name, server := range c.Conf.Servers {
553
		server = cleanForTOMLEncoding(server, c.Conf.Default)
554
		c.Conf.Servers[name] = server
555
	}
556
557
	email := &c.Conf.EMail
558
	if email.SMTPAddr == "" {
559
		email = nil
560
	}
561
562
	slack := &c.Conf.Slack
563
	if slack.HookURL == "" {
564
		slack = nil
565
	}
566
567
	cveDict := &c.Conf.CveDict
568
	ovalDict := &c.Conf.OvalDict
569
	gost := &c.Conf.Gost
570
	exploit := &c.Conf.Exploit
571
	http := &c.Conf.HTTP
572
	if http.URL == "" {
573
		http = nil
574
	}
575
576
	syslog := &c.Conf.Syslog
577
	if syslog.Host == "" {
578
		syslog = nil
579
	}
580
581
	aws := &c.Conf.AWS
582
	if aws.S3Bucket == "" {
583
		aws = nil
584
	}
585
586
	azure := &c.Conf.Azure
587
	if azure.AccountName == "" {
588
		azure = nil
589
	}
590
591
	stride := &c.Conf.Stride
592
	if stride.HookURL == "" {
593
		stride = nil
594
	}
595
596
	hipChat := &c.Conf.HipChat
597
	if hipChat.AuthToken == "" {
598
		hipChat = nil
599
	}
600
601
	chatWork := &c.Conf.ChatWork
602
	if chatWork.APIToken == "" {
603
		chatWork = nil
604
	}
605
606
	saas := &c.Conf.Saas
607
	if saas.GroupID == 0 {
608
		saas = nil
609
	}
610
611
	c := struct {
612
		CveDict  *c.GoCveDictConf `toml:"cveDict"`
613
		OvalDict *c.GovalDictConf `toml:"ovalDict"`
614
		Gost     *c.GostConf      `toml:"gost"`
615
		Exploit  *c.ExploitConf   `toml:"exploit"`
616
		Slack    *c.SlackConf     `toml:"slack"`
617
		Email    *c.SMTPConf      `toml:"email"`
618
		HTTP     *c.HTTPConf      `toml:"http"`
619
		Syslog   *c.SyslogConf    `toml:"syslog"`
620
		AWS      *c.AWS           `toml:"aws"`
621
		Azure    *c.Azure         `toml:"azure"`
622
		Stride   *c.StrideConf    `toml:"stride"`
623
		HipChat  *c.HipChatConf   `toml:"hipChat"`
624
		ChatWork *c.ChatWorkConf  `toml:"chatWork"`
625
		Saas     *c.SaasConf      `toml:"saas"`
626
627
		Default c.ServerInfo            `toml:"default"`
628
		Servers map[string]c.ServerInfo `toml:"servers"`
629
	}{
630
		CveDict:  cveDict,
631
		OvalDict: ovalDict,
632
		Gost:     gost,
633
		Exploit:  exploit,
634
		Slack:    slack,
635
		Email:    email,
636
		HTTP:     http,
637
		Syslog:   syslog,
638
		AWS:      aws,
639
		Azure:    azure,
640
		Stride:   stride,
641
		HipChat:  hipChat,
642
		ChatWork: chatWork,
643
		Saas:     saas,
644
645
		Default: c.Conf.Default,
646
		Servers: c.Conf.Servers,
647
	}
648
649
	// rename the current config.toml to config.toml.bak
650
	info, err := os.Lstat(configPath)
651
	if err != nil {
652
		return xerrors.Errorf("Failed to lstat %s: %w", configPath, err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
653
	}
654
	realPath := configPath
655
	if info.Mode()&os.ModeSymlink == os.ModeSymlink {
656
		if realPath, err = os.Readlink(configPath); err != nil {
657
			return xerrors.Errorf("Failed to Read link %s: %w", configPath, err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
658
		}
659
	}
660
	if err := os.Rename(realPath, realPath+".bak"); err != nil {
661
		return xerrors.Errorf("Failed to rename %s: %w", configPath, err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
662
	}
663
664
	var buf bytes.Buffer
665
	if err := toml.NewEncoder(&buf).Encode(c); err != nil {
666
		return xerrors.Errorf("Failed to encode to toml: %w", err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
667
	}
668
	str := strings.Replace(buf.String(), "\n  [", "\n\n  [", -1)
669
	str = fmt.Sprintf("%s\n\n%s",
670
		"# See REAME for details: https://vuls.io/docs/en/usage-settings.html",
671
		str)
672
673
	return ioutil.WriteFile(realPath, []byte(str), 0600)
674
}
675
676
func cleanForTOMLEncoding(server c.ServerInfo, def c.ServerInfo) c.ServerInfo {
677
	if reflect.DeepEqual(server.Optional, def.Optional) {
678
		server.Optional = nil
679
	}
680
681
	if def.User == server.User {
682
		server.User = ""
683
	}
684
685
	if def.Host == server.Host {
686
		server.Host = ""
687
	}
688
689
	if def.Port == server.Port {
690
		server.Port = ""
691
	}
692
693
	if def.KeyPath == server.KeyPath {
694
		server.KeyPath = ""
695
	}
696
697
	if reflect.DeepEqual(server.ScanMode, def.ScanMode) {
698
		server.ScanMode = nil
699
	}
700
701
	if def.Type == server.Type {
702
		server.Type = ""
703
	}
704
705
	if reflect.DeepEqual(server.CpeNames, def.CpeNames) {
706
		server.CpeNames = nil
707
	}
708
709
	if def.OwaspDCXMLPath == server.OwaspDCXMLPath {
710
		server.OwaspDCXMLPath = ""
711
	}
712
713
	if reflect.DeepEqual(server.IgnoreCves, def.IgnoreCves) {
714
		server.IgnoreCves = nil
715
	}
716
717
	if reflect.DeepEqual(server.Enablerepo, def.Enablerepo) {
718
		server.Enablerepo = nil
719
	}
720
721
	for k, v := range def.Optional {
722
		if vv, ok := server.Optional[k]; ok && v == vv {
723
			delete(server.Optional, k)
724
		}
725
	}
726
727
	return server
728
}
729