Completed
Push — master ( 8eae50...9d7b11 )
by kota
05:53
created

report.fillAlerts   A

Complexity

Conditions 3

Size

Total Lines 15
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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