Passed
Push — master ( 678e72...9865ea )
by kota
05:18
created

config.Distro.MajorVersion   A

Complexity

Conditions 4

Size

Total Lines 15
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 12
dl 0
loc 15
rs 9.8
c 0
b 0
f 0
nop 0
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 config
19
20
import (
21
	"errors"
22
	"fmt"
23
	"os"
24
	"path/filepath"
25
	"runtime"
26
	"strconv"
27
	"strings"
28
29
	syslog "github.com/RackSec/srslog"
30
31
	valid "github.com/asaskevich/govalidator"
32
	log "github.com/sirupsen/logrus"
33
)
34
35
// Version of Vuls
36
var Version = "0.5.0"
37
38
// Revision of Git
39
var Revision string
40
41
// Conf has Configuration
42
var Conf Config
43
44
const (
45
	// RedHat is
46
	RedHat = "redhat"
47
48
	// Debian is
49
	Debian = "debian"
50
51
	// Ubuntu is
52
	Ubuntu = "ubuntu"
53
54
	// CentOS is
55
	CentOS = "centos"
56
57
	// Fedora is
58
	Fedora = "fedora"
59
60
	// Amazon is
61
	Amazon = "amazon"
62
63
	// Oracle is
64
	Oracle = "oracle"
65
66
	// FreeBSD is
67
	FreeBSD = "freebsd"
68
69
	// Raspbian is
70
	Raspbian = "raspbian"
71
72
	// Windows is
73
	Windows = "windows"
74
75
	// OpenSUSE is
76
	OpenSUSE = "opensuse"
77
78
	// OpenSUSELeap is
79
	OpenSUSELeap = "opensuse.leap"
80
81
	// SUSEEnterpriseServer is
82
	SUSEEnterpriseServer = "suse.linux.enterprise.server"
83
84
	// SUSEEnterpriseDesktop is
85
	SUSEEnterpriseDesktop = "suse.linux.enterprise.desktop"
86
87
	// SUSEOpenstackCloud is
88
	SUSEOpenstackCloud = "suse.openstack.cloud"
89
90
	// Alpine is
91
	Alpine = "alpine"
92
)
93
94
const (
95
	// ServerTypePseudo is used for ServerInfo.Type
96
	ServerTypePseudo = "pseudo"
97
)
98
99
//Config is struct of Configuration
100
type Config struct {
101
	Debug      bool   `json:"debug"`
102
	DebugSQL   bool   `json:"debugSQL"`
103
	Lang       string `json:"lang"`
104
	HTTPProxy  string `valid:"url" json:"httpProxy"`
105
	LogDir     string `json:"logDir"`
106
	ResultsDir string `json:"resultsDir"`
107
	Pipe       bool   `json:"pipe"`
108
109
	Default            ServerInfo            `json:"default"`
110
	Servers            map[string]ServerInfo `json:"servers"`
111
	CvssScoreOver      float64               `json:"cvssScoreOver"`
112
	IgnoreUnscoredCves bool                  `json:"ignoreUnscoredCves"`
113
	IgnoreUnfixed      bool                  `json:"ignoreUnfixed"`
114
	SSHNative          bool                  `json:"sshNative"`
115
	SSHConfig          bool                  `json:"sshConfig"`
116
	ContainersOnly     bool                  `json:"containersOnly"`
117
	SkipBroken         bool                  `json:"skipBroken"`
118
	CacheDBPath        string                `json:"cacheDBPath"`
119
	Vvv                bool                  `json:"vvv"`
120
	UUID               bool                  `json:"uuid"`
121
122
	CveDict  GoCveDictConf `json:"cveDict"`
123
	OvalDict GovalDictConf `json:"ovalDict"`
124
	Gost     GostConf      `json:"gost"`
125
	Exploit  ExploitConf   `json:"exploit"`
126
127
	Slack    SlackConf    `json:"-"`
128
	EMail    SMTPConf     `json:"-"`
129
	HTTP     HTTPConf     `json:"-"`
130
	Syslog   SyslogConf   `json:"-"`
131
	AWS      AWS          `json:"-"`
132
	Azure    Azure        `json:"-"`
133
	Stride   StrideConf   `json:"-"`
134
	HipChat  HipChatConf  `json:"-"`
135
	ChatWork ChatWorkConf `json:"-"`
136
	Saas     SaasConf     `json:"-"`
137
138
	RefreshCve        bool `json:"refreshCve"`
139
	ToSlack           bool `json:"toSlack"`
140
	ToStride          bool `json:"toStride"`
141
	ToHipChat         bool `json:"toHipChat"`
142
	ToChatWork        bool `json:"toChatWork"`
143
	ToEmail           bool `json:"toEmail"`
144
	ToSyslog          bool `json:"toSyslog"`
145
	ToLocalFile       bool `json:"toLocalFile"`
146
	ToS3              bool `json:"toS3"`
147
	ToAzureBlob       bool `json:"toAzureBlob"`
148
	ToSaas            bool `json:"toSaas"`
149
	ToHTTP            bool `json:"toHTTP"`
150
	FormatXML         bool `json:"formatXML"`
151
	FormatJSON        bool `json:"formatJSON"`
152
	FormatOneEMail    bool `json:"formatOneEMail"`
153
	FormatOneLineText bool `json:"formatOneLineText"`
154
	FormatList        bool `json:"formatList"`
155
	FormatFullText    bool `json:"formatFullText"`
156
	GZIP              bool `json:"gzip"`
157
	Diff              bool `json:"diff"`
158
}
159
160
// ValidateOnConfigtest validates
161
func (c Config) ValidateOnConfigtest() bool {
162
	errs := []error{}
163
164
	if runtime.GOOS == "windows" && !c.SSHNative {
165
		errs = append(errs, fmt.Errorf("-ssh-native-insecure is needed on windows"))
166
	}
167
168
	_, err := valid.ValidateStruct(c)
169
	if err != nil {
170
		errs = append(errs, err)
171
	}
172
173
	for _, err := range errs {
174
		log.Error(err)
175
	}
176
177
	return len(errs) == 0
178
}
179
180
// ValidateOnScan validates configuration
181
func (c Config) ValidateOnScan() bool {
182
	errs := []error{}
183
184
	if len(c.ResultsDir) != 0 {
185
		if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
186
			errs = append(errs, fmt.Errorf(
187
				"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
188
		}
189
	}
190
191
	if runtime.GOOS == "windows" && !c.SSHNative {
192
		errs = append(errs, fmt.Errorf("-ssh-native-insecure is needed on windows"))
193
	}
194
195
	if len(c.ResultsDir) != 0 {
196
		if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
197
			errs = append(errs, fmt.Errorf(
198
				"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
199
		}
200
	}
201
202
	if len(c.CacheDBPath) != 0 {
203
		if ok, _ := valid.IsFilePath(c.CacheDBPath); !ok {
204
			errs = append(errs, fmt.Errorf(
205
				"Cache DB path must be a *Absolute* file path. -cache-dbpath: %s",
206
				c.CacheDBPath))
207
		}
208
	}
209
210
	_, err := valid.ValidateStruct(c)
211
	if err != nil {
212
		errs = append(errs, err)
213
	}
214
215
	for _, err := range errs {
216
		log.Error(err)
217
	}
218
219
	return len(errs) == 0
220
}
221
222
// ValidateOnReportDB validates configuration
223
func (c Config) ValidateOnReportDB() bool {
224
	errs := []error{}
225
226
	if err := validateDB("cvedb", c.CveDict.Type, c.CveDict.SQLite3Path, c.CveDict.URL); err != nil {
227
		errs = append(errs, err)
228
	}
229
	if c.CveDict.Type == "sqlite3" {
230
		if _, err := os.Stat(c.CveDict.SQLite3Path); os.IsNotExist(err) {
231
			errs = append(errs, fmt.Errorf("SQLite3 DB path (%s) is not exist: %s", "cvedb", c.CveDict.SQLite3Path))
232
		}
233
	}
234
235
	if err := validateDB("ovaldb", c.OvalDict.Type, c.OvalDict.SQLite3Path, c.OvalDict.URL); err != nil {
236
		errs = append(errs, err)
237
	}
238
239
	for _, err := range errs {
240
		log.Error(err)
241
	}
242
243
	return len(errs) == 0
244
}
245
246
// ValidateOnReport validates configuration
247
func (c Config) ValidateOnReport() bool {
248
	errs := []error{}
249
250
	if len(c.ResultsDir) != 0 {
251
		if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
252
			errs = append(errs, fmt.Errorf(
253
				"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
254
		}
255
	}
256
257
	_, err := valid.ValidateStruct(c)
258
	if err != nil {
259
		errs = append(errs, err)
260
	}
261
262
	if mailerrs := c.EMail.Validate(); 0 < len(mailerrs) {
263
		errs = append(errs, mailerrs...)
264
	}
265
266
	if slackerrs := c.Slack.Validate(); 0 < len(slackerrs) {
267
		errs = append(errs, slackerrs...)
268
	}
269
270
	if hipchaterrs := c.HipChat.Validate(); 0 < len(hipchaterrs) {
271
		errs = append(errs, hipchaterrs...)
272
	}
273
274
	if chatworkerrs := c.ChatWork.Validate(); 0 < len(chatworkerrs) {
275
		errs = append(errs, chatworkerrs...)
276
	}
277
278
	if strideerrs := c.Stride.Validate(); 0 < len(strideerrs) {
279
		errs = append(errs, strideerrs...)
280
	}
281
282
	if saaserrs := c.Saas.Validate(); 0 < len(saaserrs) {
283
		errs = append(errs, saaserrs...)
284
	}
285
286
	if syslogerrs := c.Syslog.Validate(); 0 < len(syslogerrs) {
287
		errs = append(errs, syslogerrs...)
288
	}
289
290
	if httperrs := c.HTTP.Validate(); 0 < len(httperrs) {
291
		errs = append(errs, httperrs...)
292
	}
293
294
	for _, err := range errs {
295
		log.Error(err)
296
	}
297
298
	return len(errs) == 0
299
}
300
301
// ValidateOnTui validates configuration
302
func (c Config) ValidateOnTui() bool {
303
	errs := []error{}
304
305
	if len(c.ResultsDir) != 0 {
306
		if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
307
			errs = append(errs, fmt.Errorf(
308
				"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
309
		}
310
	}
311
312
	if err := validateDB("cvedb", c.CveDict.Type, c.CveDict.SQLite3Path, c.CveDict.URL); err != nil {
313
		errs = append(errs, err)
314
	}
315
	if c.CveDict.Type == "sqlite3" {
316
		if _, err := os.Stat(c.CveDict.SQLite3Path); os.IsNotExist(err) {
317
			errs = append(errs, fmt.Errorf("SQLite3 DB path (%s) is not exist: %s", "cvedb", c.CveDict.SQLite3Path))
318
		}
319
	}
320
321
	for _, err := range errs {
322
		log.Error(err)
323
	}
324
325
	return len(errs) == 0
326
}
327
328
// validateDB validates configuration
329
//  dictionaryDB name is 'cvedb' or 'ovaldb'
330
func validateDB(dictionaryDBName, dbType, dbPath, dbURL string) error {
331
	switch dbType {
332
	case "sqlite3":
333
		if ok, _ := valid.IsFilePath(dbPath); !ok {
334
			return fmt.Errorf(
335
				"SQLite3 DB path (%s) must be a *Absolute* file path. -%s-path: %s",
336
				dictionaryDBName,
337
				dictionaryDBName,
338
				dbPath)
339
		}
340
	case "mysql":
341
		if dbURL == "" {
342
			return fmt.Errorf(
343
				`MySQL connection string is needed. -%s-url="user:pass@tcp(localhost:3306)/dbname"`,
344
				dictionaryDBName)
345
		}
346
	case "postgres":
347
		if dbURL == "" {
348
			return fmt.Errorf(
349
				`PostgreSQL connection string is needed. -%s-url="host=myhost user=user dbname=dbname sslmode=disable password=password"`,
350
				dictionaryDBName)
351
		}
352
	case "redis":
353
		if dbURL == "" {
354
			return fmt.Errorf(
355
				`Redis connection string is needed. -%s-url="redis://localhost/0"`,
356
				dictionaryDBName)
357
		}
358
	default:
359
		return fmt.Errorf(
360
			"%s type must be either 'sqlite3', 'mysql', 'postgres' or 'redis'.  -%s-type: %s",
361
			dictionaryDBName,
362
			dictionaryDBName,
363
			dbType)
364
	}
365
	return nil
366
}
367
368
// SMTPConf is smtp config
369
type SMTPConf struct {
370
	SMTPAddr      string   `toml:"smtpAddr,omitempty" json:"-"`
371
	SMTPPort      string   `toml:"smtpPort,omitempty" valid:"port" json:"-"`
372
	User          string   `toml:"user,omitempty" json:"-"`
373
	Password      string   `toml:"password,omitempty" json:"-"`
374
	From          string   `toml:"from,omitempty" json:"-"`
375
	To            []string `toml:"to,omitempty" json:"-"`
376
	Cc            []string `toml:"cc,omitempty" json:"-"`
377
	SubjectPrefix string   `toml:"subjectPrefix,omitempty" json:"-"`
378
}
379
380
func checkEmails(emails []string) (errs []error) {
381
	for _, addr := range emails {
382
		if len(addr) == 0 {
383
			return
384
		}
385
		if ok := valid.IsEmail(addr); !ok {
386
			errs = append(errs, fmt.Errorf("Invalid email address. email: %s", addr))
387
		}
388
	}
389
	return
390
}
391
392
// Validate SMTP configuration
393
func (c *SMTPConf) Validate() (errs []error) {
394
	if !Conf.ToEmail {
395
		return
396
	}
397
	// Check Emails fromat
398
	emails := []string{}
399
	emails = append(emails, c.From)
400
	emails = append(emails, c.To...)
401
	emails = append(emails, c.Cc...)
402
403
	if emailErrs := checkEmails(emails); 0 < len(emailErrs) {
404
		errs = append(errs, emailErrs...)
405
	}
406
407
	if len(c.SMTPAddr) == 0 {
408
		errs = append(errs, fmt.Errorf("email.smtpAddr must not be empty"))
409
	}
410
	if len(c.SMTPPort) == 0 {
411
		errs = append(errs, fmt.Errorf("email.smtpPort must not be empty"))
412
	}
413
	if len(c.To) == 0 {
414
		errs = append(errs, fmt.Errorf("email.To required at least one address"))
415
	}
416
	if len(c.From) == 0 {
417
		errs = append(errs, fmt.Errorf("email.From required at least one address"))
418
	}
419
420
	_, err := valid.ValidateStruct(c)
421
	if err != nil {
422
		errs = append(errs, err)
423
	}
424
	return
425
}
426
427
// StrideConf is stride config
428
type StrideConf struct {
429
	HookURL   string `json:"-"`
430
	AuthToken string `json:"-"`
431
}
432
433
// Validate validates configuration
434
func (c *StrideConf) Validate() (errs []error) {
435
	if !Conf.ToStride {
436
		return
437
	}
438
439
	if len(c.HookURL) == 0 {
440
		errs = append(errs, fmt.Errorf("stride.HookURL must not be empty"))
441
	}
442
443
	if len(c.AuthToken) == 0 {
444
		errs = append(errs, fmt.Errorf("stride.AuthToken must not be empty"))
445
	}
446
447
	_, err := valid.ValidateStruct(c)
448
	if err != nil {
449
		errs = append(errs, err)
450
	}
451
	return
452
}
453
454
// SlackConf is slack config
455
type SlackConf struct {
456
	HookURL     string   `valid:"url" json:"-" toml:"hookURL,omitempty"`
457
	LegacyToken string   `json:"-" toml:"legacyToken,omitempty"`
458
	Channel     string   `json:"-" toml:"channel,omitempty"`
459
	IconEmoji   string   `json:"-" toml:"iconEmoji,omitempty"`
460
	AuthUser    string   `json:"-" toml:"authUser,omitempty"`
461
	NotifyUsers []string `toml:"notifyUsers,omitempty" json:"-"`
462
	Text        string   `json:"-"`
463
}
464
465
// Validate validates configuration
466
func (c *SlackConf) Validate() (errs []error) {
467
	if !Conf.ToSlack {
468
		return
469
	}
470
471
	if len(c.HookURL) == 0 && len(c.LegacyToken) == 0 {
472
		errs = append(errs, fmt.Errorf("slack.hookURL or slack.LegacyToken must not be empty"))
473
	}
474
475
	if len(c.Channel) == 0 {
476
		errs = append(errs, fmt.Errorf("slack.channel must not be empty"))
477
	} else {
478
		if !(strings.HasPrefix(c.Channel, "#") ||
479
			c.Channel == "${servername}") {
480
			errs = append(errs, fmt.Errorf(
481
				"channel's prefix must be '#', channel: %s", c.Channel))
482
		}
483
	}
484
485
	if len(c.AuthUser) == 0 {
486
		errs = append(errs, fmt.Errorf("slack.authUser must not be empty"))
487
	}
488
489
	_, err := valid.ValidateStruct(c)
490
	if err != nil {
491
		errs = append(errs, err)
492
	}
493
494
	return
495
}
496
497
// HipChatConf is HipChat config
498
type HipChatConf struct {
499
	AuthToken string `json:"-"`
500
	Room      string `json:"-"`
501
}
502
503
// Validate validates configuration
504
func (c *HipChatConf) Validate() (errs []error) {
505
	if !Conf.ToHipChat {
506
		return
507
	}
508
	if len(c.Room) == 0 {
509
		errs = append(errs, fmt.Errorf("hipcaht.room must not be empty"))
510
	}
511
512
	if len(c.AuthToken) == 0 {
513
		errs = append(errs, fmt.Errorf("hipcaht.AuthToken must not be empty"))
514
	}
515
516
	_, err := valid.ValidateStruct(c)
517
	if err != nil {
518
		errs = append(errs, err)
519
	}
520
	return
521
}
522
523
// ChatWorkConf is ChatWork config
524
type ChatWorkConf struct {
525
	APIToken string `json:"-"`
526
	Room     string `json:"-"`
527
}
528
529
// Validate validates configuration
530
func (c *ChatWorkConf) Validate() (errs []error) {
531
	if !Conf.ToChatWork {
532
		return
533
	}
534
	if len(c.Room) == 0 {
535
		errs = append(errs, fmt.Errorf("chatworkcaht.room must not be empty"))
536
	}
537
538
	if len(c.APIToken) == 0 {
539
		errs = append(errs, fmt.Errorf("chatworkcaht.ApiToken must not be empty"))
540
	}
541
542
	_, err := valid.ValidateStruct(c)
543
	if err != nil {
544
		errs = append(errs, err)
545
	}
546
	return
547
}
548
549
// SaasConf is stride config
550
type SaasConf struct {
551
	GroupID int    `json:"-"`
552
	Token   string `json:"-"`
553
	URL     string `json:"-"`
554
}
555
556
// Validate validates configuration
557
func (c *SaasConf) Validate() (errs []error) {
558
	if !Conf.ToSaas {
559
		return
560
	}
561
562
	if c.GroupID == 0 {
563
		errs = append(errs, fmt.Errorf("saas.GroupID must not be empty"))
564
	}
565
566
	if len(c.Token) == 0 {
567
		errs = append(errs, fmt.Errorf("saas.Token must not be empty"))
568
	}
569
570
	if len(c.URL) == 0 {
571
		errs = append(errs, fmt.Errorf("saas.URL must not be empty"))
572
	}
573
574
	_, err := valid.ValidateStruct(c)
575
	if err != nil {
576
		errs = append(errs, err)
577
	}
578
	return
579
}
580
581
// SyslogConf is syslog config
582
type SyslogConf struct {
583
	Protocol string `json:"-"`
584
	Host     string `valid:"host" json:"-"`
585
	Port     string `valid:"port" json:"-"`
586
	Severity string `json:"-"`
587
	Facility string `json:"-"`
588
	Tag      string `json:"-"`
589
	Verbose  bool   `json:"-"`
590
}
591
592
// Validate validates configuration
593
func (c *SyslogConf) Validate() (errs []error) {
594
	if !Conf.ToSyslog {
595
		return nil
596
	}
597
	//  If protocol is empty, it will connect to the local syslog server.
598
	if len(c.Protocol) > 0 && c.Protocol != "tcp" && c.Protocol != "udp" {
599
		errs = append(errs, errors.New(`syslog.protocol must be "tcp" or "udp"`))
600
	}
601
602
	// Default port: 514
603
	if c.Port == "" {
604
		c.Port = "514"
605
	}
606
607
	if _, err := c.GetSeverity(); err != nil {
608
		errs = append(errs, err)
609
	}
610
611
	if _, err := c.GetFacility(); err != nil {
612
		errs = append(errs, err)
613
	}
614
615
	if _, err := valid.ValidateStruct(c); err != nil {
616
		errs = append(errs, err)
617
	}
618
	return errs
619
}
620
621
// GetSeverity gets severity
622
func (c *SyslogConf) GetSeverity() (syslog.Priority, error) {
623
	if c.Severity == "" {
624
		return syslog.LOG_INFO, nil
625
	}
626
627
	switch c.Severity {
628
	case "emerg":
629
		return syslog.LOG_EMERG, nil
630
	case "alert":
631
		return syslog.LOG_ALERT, nil
632
	case "crit":
633
		return syslog.LOG_CRIT, nil
634
	case "err":
635
		return syslog.LOG_ERR, nil
636
	case "warning":
637
		return syslog.LOG_WARNING, nil
638
	case "notice":
639
		return syslog.LOG_NOTICE, nil
640
	case "info":
641
		return syslog.LOG_INFO, nil
642
	case "debug":
643
		return syslog.LOG_DEBUG, nil
644
	default:
645
		return -1, fmt.Errorf("Invalid severity: %s", c.Severity)
646
	}
647
}
648
649
// GetFacility gets facility
650
func (c *SyslogConf) GetFacility() (syslog.Priority, error) {
651
	if c.Facility == "" {
652
		return syslog.LOG_AUTH, nil
653
	}
654
655
	switch c.Facility {
656
	case "kern":
657
		return syslog.LOG_KERN, nil
658
	case "user":
659
		return syslog.LOG_USER, nil
660
	case "mail":
661
		return syslog.LOG_MAIL, nil
662
	case "daemon":
663
		return syslog.LOG_DAEMON, nil
664
	case "auth":
665
		return syslog.LOG_AUTH, nil
666
	case "syslog":
667
		return syslog.LOG_SYSLOG, nil
668
	case "lpr":
669
		return syslog.LOG_LPR, nil
670
	case "news":
671
		return syslog.LOG_NEWS, nil
672
	case "uucp":
673
		return syslog.LOG_UUCP, nil
674
	case "cron":
675
		return syslog.LOG_CRON, nil
676
	case "authpriv":
677
		return syslog.LOG_AUTHPRIV, nil
678
	case "ftp":
679
		return syslog.LOG_FTP, nil
680
	case "local0":
681
		return syslog.LOG_LOCAL0, nil
682
	case "local1":
683
		return syslog.LOG_LOCAL1, nil
684
	case "local2":
685
		return syslog.LOG_LOCAL2, nil
686
	case "local3":
687
		return syslog.LOG_LOCAL3, nil
688
	case "local4":
689
		return syslog.LOG_LOCAL4, nil
690
	case "local5":
691
		return syslog.LOG_LOCAL5, nil
692
	case "local6":
693
		return syslog.LOG_LOCAL6, nil
694
	case "local7":
695
		return syslog.LOG_LOCAL7, nil
696
	default:
697
		return -1, fmt.Errorf("Invalid facility: %s", c.Facility)
698
	}
699
}
700
701
// HTTPConf is HTTP config
702
type HTTPConf struct {
703
	URL string `valid:"url" json:"-"`
704
}
705
706
// Validate validates configuration
707
func (c *HTTPConf) Validate() (errs []error) {
708
	if !Conf.ToHTTP {
709
		return nil
710
	}
711
712
	if _, err := valid.ValidateStruct(c); err != nil {
713
		errs = append(errs, err)
714
	}
715
	return errs
716
}
717
718
const httpKey = "VULS_HTTP_URL"
719
720
// Overwrite set options with the following priority.
721
// 1. Command line option
722
// 2. Environment variable
723
// 3. config.toml
724
func (c *HTTPConf) Overwrite(cmdOpt HTTPConf) {
725
	if os.Getenv(httpKey) != "" {
726
		c.URL = os.Getenv(httpKey)
727
	}
728
	if cmdOpt.URL != "" {
729
		c.URL = cmdOpt.URL
730
	}
731
}
732
733
// GoCveDictConf is go-cve-dictionary config
734
type GoCveDictConf struct {
735
	// DB type of CVE dictionary (sqlite3, mysql, postgres or redis)
736
	Type string
737
738
	// http://cve-dictionary.com:1323 or DB connection string
739
	URL string `json:"-"`
740
741
	// /path/to/cve.sqlite3
742
	SQLite3Path string `json:"-"`
743
}
744
745
func (cnf *GoCveDictConf) setDefault() {
746
	if cnf.Type == "" {
747
		cnf.Type = "sqlite3"
748
	}
749
	if cnf.URL == "" && cnf.SQLite3Path == "" {
750
		wd, _ := os.Getwd()
751
		cnf.SQLite3Path = filepath.Join(wd, "cve.sqlite3")
752
	}
753
}
754
755
const cveDBType = "CVEDB_TYPE"
756
const cveDBURL = "CVEDB_URL"
757
const cveDBPATH = "CVEDB_SQLITE3_PATH"
758
759
// Overwrite set options with the following priority.
760
// 1. Command line option
761
// 2. Environment variable
762
// 3. config.toml
763
func (cnf *GoCveDictConf) Overwrite(cmdOpt GoCveDictConf) {
764
	if os.Getenv(cveDBType) != "" {
765
		cnf.Type = os.Getenv(cveDBType)
766
	}
767
	if os.Getenv(cveDBURL) != "" {
768
		cnf.URL = os.Getenv(cveDBURL)
769
	}
770
	if os.Getenv(cveDBPATH) != "" {
771
		cnf.SQLite3Path = os.Getenv(cveDBPATH)
772
	}
773
774
	if cmdOpt.Type != "" {
775
		cnf.Type = cmdOpt.Type
776
	}
777
	if cmdOpt.URL != "" {
778
		cnf.URL = cmdOpt.URL
779
	}
780
	if cmdOpt.SQLite3Path != "" {
781
		cnf.SQLite3Path = cmdOpt.SQLite3Path
782
	}
783
	cnf.setDefault()
784
}
785
786
// GovalDictConf is goval-dictionary config
787
type GovalDictConf struct {
788
789
	// DB type of OVAL dictionary (sqlite3, mysql, postgres or redis)
790
	Type string
791
792
	// http://goval-dictionary.com:1324 or DB connection string
793
	URL string `json:"-"`
794
795
	// /path/to/oval.sqlite3
796
	SQLite3Path string `json:"-"`
797
}
798
799
func (cnf *GovalDictConf) setDefault() {
800
	if cnf.Type == "" {
801
		cnf.Type = "sqlite3"
802
	}
803
	if cnf.URL == "" && cnf.SQLite3Path == "" {
804
		wd, _ := os.Getwd()
805
		cnf.SQLite3Path = filepath.Join(wd, "oval.sqlite3")
806
	}
807
}
808
809
const govalType = "OVALDB_TYPE"
810
const govalURL = "OVALDB_URL"
811
const govalPATH = "OVALDB_SQLITE3_PATH"
812
813
// Overwrite set options with the following priority.
814
// 1. Command line option
815
// 2. Environment variable
816
// 3. config.toml
817
func (cnf *GovalDictConf) Overwrite(cmdOpt GovalDictConf) {
818
	if os.Getenv(govalType) != "" {
819
		cnf.Type = os.Getenv(govalType)
820
	}
821
	if os.Getenv(govalURL) != "" {
822
		cnf.URL = os.Getenv(govalURL)
823
	}
824
	if os.Getenv(govalPATH) != "" {
825
		cnf.SQLite3Path = os.Getenv(govalPATH)
826
	}
827
828
	if cmdOpt.Type != "" {
829
		cnf.Type = cmdOpt.Type
830
	}
831
	if cmdOpt.URL != "" {
832
		cnf.URL = cmdOpt.URL
833
	}
834
	if cmdOpt.SQLite3Path != "" {
835
		cnf.SQLite3Path = cmdOpt.SQLite3Path
836
	}
837
	cnf.setDefault()
838
}
839
840
// GostConf is gost config
841
type GostConf struct {
842
	// DB type for gost dictionary (sqlite3, mysql, postgres or redis)
843
	Type string
844
845
	// http://gost-dictionary.com:1324 or DB connection string
846
	URL string `json:"-"`
847
848
	// /path/to/gost.sqlite3
849
	SQLite3Path string `json:"-"`
850
}
851
852
func (cnf *GostConf) setDefault() {
853
	if cnf.Type == "" {
854
		cnf.Type = "sqlite3"
855
	}
856
	if cnf.URL == "" && cnf.SQLite3Path == "" {
857
		wd, _ := os.Getwd()
858
		cnf.SQLite3Path = filepath.Join(wd, "gost.sqlite3")
859
	}
860
}
861
862
const gostDBType = "GOSTDB_TYPE"
863
const gostDBURL = "GOSTDB_URL"
864
const gostDBPATH = "GOSTDB_SQLITE3_PATH"
865
866
// Overwrite set options with the following priority.
867
// 1. Command line option
868
// 2. Environment variable
869
// 3. config.toml
870
func (cnf *GostConf) Overwrite(cmdOpt GostConf) {
871
	if os.Getenv(gostDBType) != "" {
872
		cnf.Type = os.Getenv(gostDBType)
873
	}
874
	if os.Getenv(gostDBURL) != "" {
875
		cnf.URL = os.Getenv(gostDBURL)
876
	}
877
	if os.Getenv(gostDBPATH) != "" {
878
		cnf.SQLite3Path = os.Getenv(gostDBPATH)
879
	}
880
881
	if cmdOpt.Type != "" {
882
		cnf.Type = cmdOpt.Type
883
	}
884
	if cmdOpt.URL != "" {
885
		cnf.URL = cmdOpt.URL
886
	}
887
	if cmdOpt.SQLite3Path != "" {
888
		cnf.SQLite3Path = cmdOpt.SQLite3Path
889
	}
890
	cnf.setDefault()
891
}
892
893
// ExploitConf is exploit config
894
type ExploitConf struct {
895
	// DB type for exploit dictionary (sqlite3, mysql, postgres or redis)
896
	Type string
897
898
	// http://exploit-dictionary.com:1324 or DB connection string
899
	URL string `json:"-"`
900
901
	// /path/to/exploit.sqlite3
902
	SQLite3Path string `json:"-"`
903
}
904
905
func (cnf *ExploitConf) setDefault() {
906
	if cnf.Type == "" {
907
		cnf.Type = "sqlite3"
908
	}
909
	if cnf.URL == "" && cnf.SQLite3Path == "" {
910
		wd, _ := os.Getwd()
911
		cnf.SQLite3Path = filepath.Join(wd, "go-exploitdb.sqlite3")
912
	}
913
}
914
915
const exploitDBType = "EXPLOITDB_TYPE"
916
const exploitDBURL = "EXPLOITDB_URL"
917
const exploitDBPATH = "EXPLOITDB_SQLITE3_PATH"
918
919
// Overwrite set options with the following priority.
920
// 1. Command line option
921
// 2. Environment variable
922
// 3. config.toml
923
func (cnf *ExploitConf) Overwrite(cmdOpt ExploitConf) {
924
	if os.Getenv(exploitDBType) != "" {
925
		cnf.Type = os.Getenv(exploitDBType)
926
	}
927
	if os.Getenv(exploitDBURL) != "" {
928
		cnf.URL = os.Getenv(exploitDBURL)
929
	}
930
	if os.Getenv(exploitDBPATH) != "" {
931
		cnf.SQLite3Path = os.Getenv(exploitDBPATH)
932
	}
933
934
	if cmdOpt.Type != "" {
935
		cnf.Type = cmdOpt.Type
936
	}
937
	if cmdOpt.URL != "" {
938
		cnf.URL = cmdOpt.URL
939
	}
940
	if cmdOpt.SQLite3Path != "" {
941
		cnf.SQLite3Path = cmdOpt.SQLite3Path
942
	}
943
	cnf.setDefault()
944
}
945
946
// AWS is aws config
947
type AWS struct {
948
	// AWS profile to use
949
	Profile string `json:"profile"`
950
951
	// AWS region to use
952
	Region string `json:"region"`
953
954
	// S3 bucket name
955
	S3Bucket string `json:"s3Bucket"`
956
957
	// /bucket/path/to/results
958
	S3ResultsDir string `json:"s3ResultsDir"`
959
960
	// The Server-side encryption algorithm used when storing the reports in S3 (e.g., AES256, aws:kms).
961
	S3ServerSideEncryption string `json:"s3ServerSideEncryption"`
962
}
963
964
// Azure is azure config
965
type Azure struct {
966
	// Azure account name to use. AZURE_STORAGE_ACCOUNT environment variable is used if not specified
967
	AccountName string `json:"accountName"`
968
969
	// Azure account key to use. AZURE_STORAGE_ACCESS_KEY environment variable is used if not specified
970
	AccountKey string `json:"-"`
971
972
	// Azure storage container name
973
	ContainerName string `json:"containerName"`
974
}
975
976
// ServerInfo has SSH Info, additional CPE packages to scan.
977
type ServerInfo struct {
978
	ServerName             string                      `toml:"-" json:"serverName"`
979
	User                   string                      `toml:"user,omitempty" json:"user"`
980
	Host                   string                      `toml:"host,omitempty" json:"host"`
981
	Port                   string                      `toml:"port,omitempty" json:"port"`
982
	KeyPath                string                      `toml:"keyPath,omitempty" json:"keyPath"`
983
	KeyPassword            string                      `json:"-" toml:"-"`
984
	CpeNames               []string                    `toml:"cpeNames,omitempty" json:"cpeNames,omitempty"`
985
	ScanMode               []string                    `toml:"scanMode,omitempty" json:"scanMode,omitempty"`
986
	DependencyCheckXMLPath string                      `toml:"dependencyCheckXMLPath,omitempty" json:"-"` // TODO Deprecated remove in near future
987
	OwaspDCXMLPath         string                      `toml:"owaspDCXMLPath,omitempty" json:"owaspDCXMLPath"`
988
	ContainersIncluded     []string                    `toml:"containersIncluded,omitempty" json:"containersIncluded,omitempty"`
989
	ContainersExcluded     []string                    `toml:"containersExcluded,omitempty" json:"containersExcluded,omitempty"`
990
	ContainerType          string                      `toml:"containerType,omitempty" json:"containerType,omitempty"`
991
	Containers             map[string]ContainerSetting `toml:"containers" json:"containers,omitempty"`
992
	IgnoreCves             []string                    `toml:"ignoreCves,omitempty" json:"ignoreCves,omitempty"`
993
	IgnorePkgsRegexp       []string                    `toml:"ignorePkgsRegexp,omitempty" json:"ignorePkgsRegexp,omitempty"`
994
	UUIDs                  map[string]string           `toml:"uuids,omitempty" json:"uuids,omitempty"`
995
	Memo                   string                      `toml:"memo,omitempty" json:"memo"`
996
	Enablerepo             []string                    `toml:"enablerepo,omitempty" json:"enablerepo,omitempty"` // For CentOS, RHEL, Amazon
997
	Optional               map[string]interface{}      `toml:"optional,omitempty" json:"optional,omitempty"`     // Optional key-value set that will be outputted to JSON
998
	Type                   string                      `toml:"type,omitempty" json:"type"`                       // "pseudo" or ""
999
	IPv4Addrs              []string                    `toml:"-" json:"ipv4Addrs,omitempty"`
1000
	IPv6Addrs              []string                    `toml:"-" json:"ipv6Addrs,omitempty"`
1001
1002
	// used internal
1003
	LogMsgAnsiColor string    `toml:"-" json:"-"` // DebugLog Color
1004
	Container       Container `toml:"-" json:"-"`
1005
	Distro          Distro    `toml:"-" json:"-"`
1006
	Mode            ScanMode  `toml:"-" json:"-"`
1007
}
1008
1009
// ContainerSetting is used for loading container setting in config.toml
1010
type ContainerSetting struct {
1011
	Cpes             []string `json:"cpes,omitempty"`
1012
	OwaspDCXMLPath   string   `json:"owaspDCXMLPath"`
1013
	IgnorePkgsRegexp []string `json:"ignorePkgsRegexp,omitempty"`
1014
	IgnoreCves       []string `json:"ignoreCves,omitempty"`
1015
}
1016
1017
// ScanMode has a type of scan mode. fast, fast-root, deep and offline
1018
type ScanMode struct {
1019
	flag byte
1020
}
1021
1022
// Set mode
1023
func (s *ScanMode) Set(f byte) {
1024
	s.flag |= f
1025
}
1026
1027
// IsFast return whether scan mode is fast
1028
func (s ScanMode) IsFast() bool {
1029
	return s.flag&Fast == Fast
1030
}
1031
1032
// IsFastRoot return whether scan mode is fastroot
1033
func (s ScanMode) IsFastRoot() bool {
1034
	return s.flag&FastRoot == FastRoot
1035
}
1036
1037
// IsDeep return whether scan mode is deep
1038
func (s ScanMode) IsDeep() bool {
1039
	return s.flag&Deep == Deep
1040
}
1041
1042
// IsOffline return whether scan mode is offline
1043
func (s ScanMode) IsOffline() bool {
1044
	return s.flag&Offline == Offline
1045
}
1046
1047
func (s ScanMode) validate() error {
1048
	numTrue := 0
1049
	for _, b := range []bool{s.IsFast(), s.IsFastRoot(), s.IsDeep()} {
1050
		if b {
1051
			numTrue++
1052
		}
1053
	}
1054
	if numTrue == 0 {
1055
		s.Set(Fast)
1056
	} else if s.IsDeep() && s.IsOffline() {
1057
		return fmt.Errorf("Don't specify both of -deep and offline")
1058
	} else if numTrue != 1 {
1059
		return fmt.Errorf("Specify only one of -fast, -fast-root or -deep")
1060
	}
1061
	return nil
1062
}
1063
1064
func (s ScanMode) String() string {
1065
	ss := ""
1066
	if s.IsFast() {
1067
		ss = "fast"
1068
	} else if s.IsFastRoot() {
1069
		ss = "fast-root"
1070
	} else if s.IsDeep() {
1071
		ss = "deep"
1072
	}
1073
	if s.IsOffline() {
1074
		ss += " offline"
1075
	}
1076
	return ss + " mode"
1077
}
1078
1079
const (
1080
	// Fast is fast scan mode
1081
	Fast = byte(1 << iota)
1082
	// FastRoot is fast-root scan mode
1083
	FastRoot
1084
	// Deep is deep scan mode
1085
	Deep
1086
	// Offline is offline scan mode
1087
	Offline
1088
)
1089
1090
// GetServerName returns ServerName if this serverInfo is about host.
1091
// If this serverInfo is abount a container, returns containerID@ServerName
1092
func (s ServerInfo) GetServerName() string {
1093
	if len(s.Container.ContainerID) == 0 {
1094
		return s.ServerName
1095
	}
1096
	return fmt.Sprintf("%s@%s", s.Container.Name, s.ServerName)
1097
}
1098
1099
// Distro has distribution info
1100
type Distro struct {
1101
	Family  string
1102
	Release string
1103
}
1104
1105
func (l Distro) String() string {
1106
	return fmt.Sprintf("%s %s", l.Family, l.Release)
1107
}
1108
1109
// MajorVersion returns Major version
1110
func (l Distro) MajorVersion() (ver int, err error) {
1111
	if l.Family == Amazon {
1112
		ss := strings.Fields(l.Release)
1113
		if len(ss) == 1 {
1114
			return 1, nil
1115
		}
1116
		ver, err = strconv.Atoi(ss[0])
1117
		return
1118
	}
1119
	if 0 < len(l.Release) {
1120
		ver, err = strconv.Atoi(strings.Split(l.Release, ".")[0])
1121
	} else {
1122
		err = fmt.Errorf("Release is empty")
1123
	}
1124
	return
1125
}
1126
1127
// IsContainer returns whether this ServerInfo is about container
1128
func (s ServerInfo) IsContainer() bool {
1129
	return 0 < len(s.Container.ContainerID)
1130
}
1131
1132
// SetContainer set container
1133
func (s *ServerInfo) SetContainer(d Container) {
1134
	s.Container = d
1135
}
1136
1137
// Container has Container information.
1138
type Container struct {
1139
	ContainerID string
1140
	Name        string
1141
	Image       string
1142
}
1143