Completed
Push — master ( 89d58d...53f4a2 )
by
unknown
05:30
created

report.GithubSecurityAlertOption.apply   A

Complexity

Conditions 4

Size

Total Lines 13
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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