Completed
Pull Request — master (#769)
by kota
10:39
created

report.fillGitHubSecurityAlerts   A

Complexity

Conditions 4

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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