Completed
Pull Request — master (#775)
by kota
06:21 queued 01:12
created

report.EnsureUUIDs   F

Complexity

Conditions 33

Size

Total Lines 189
Code Lines 130

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 33
eloc 130
dl 0
loc 189
rs 0
c 0
b 0
f 0
nop 2

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