Completed
Push — master ( 76037c...7585f9 )
by kota
09:11 queued 01:58
created

config.*ExploitConf.IsFetchViaHTTP   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
dl 0
loc 2
rs 10
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.6.1"
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
	if err := validateDB("gostdb", c.Gost.Type, c.Gost.SQLite3Path, c.Gost.URL); err != nil {
240
		errs = append(errs, err)
241
	}
242
243
	if err := validateDB("exploitdb", c.Exploit.Type, c.Exploit.SQLite3Path, c.Exploit.URL); err != nil {
244
		errs = append(errs, err)
245
	}
246
247
	for _, err := range errs {
248
		log.Error(err)
249
	}
250
251
	return len(errs) == 0
252
}
253
254
// ValidateOnReport validates configuration
255
func (c Config) ValidateOnReport() bool {
256
	errs := []error{}
257
258
	if len(c.ResultsDir) != 0 {
259
		if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
260
			errs = append(errs, fmt.Errorf(
261
				"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
262
		}
263
	}
264
265
	_, err := valid.ValidateStruct(c)
266
	if err != nil {
267
		errs = append(errs, err)
268
	}
269
270
	if mailerrs := c.EMail.Validate(); 0 < len(mailerrs) {
271
		errs = append(errs, mailerrs...)
272
	}
273
274
	if slackerrs := c.Slack.Validate(); 0 < len(slackerrs) {
275
		errs = append(errs, slackerrs...)
276
	}
277
278
	if hipchaterrs := c.HipChat.Validate(); 0 < len(hipchaterrs) {
279
		errs = append(errs, hipchaterrs...)
280
	}
281
282
	if chatworkerrs := c.ChatWork.Validate(); 0 < len(chatworkerrs) {
283
		errs = append(errs, chatworkerrs...)
284
	}
285
286
	if strideerrs := c.Stride.Validate(); 0 < len(strideerrs) {
287
		errs = append(errs, strideerrs...)
288
	}
289
290
	if saaserrs := c.Saas.Validate(); 0 < len(saaserrs) {
291
		errs = append(errs, saaserrs...)
292
	}
293
294
	if syslogerrs := c.Syslog.Validate(); 0 < len(syslogerrs) {
295
		errs = append(errs, syslogerrs...)
296
	}
297
298
	if httperrs := c.HTTP.Validate(); 0 < len(httperrs) {
299
		errs = append(errs, httperrs...)
300
	}
301
302
	for _, err := range errs {
303
		log.Error(err)
304
	}
305
306
	return len(errs) == 0
307
}
308
309
// ValidateOnTui validates configuration
310
func (c Config) ValidateOnTui() bool {
311
	errs := []error{}
312
313
	if len(c.ResultsDir) != 0 {
314
		if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
315
			errs = append(errs, fmt.Errorf(
316
				"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
317
		}
318
	}
319
320
	if err := validateDB("cvedb", c.CveDict.Type, c.CveDict.SQLite3Path, c.CveDict.URL); err != nil {
321
		errs = append(errs, err)
322
	}
323
	if c.CveDict.Type == "sqlite3" {
324
		if _, err := os.Stat(c.CveDict.SQLite3Path); os.IsNotExist(err) {
325
			errs = append(errs, fmt.Errorf("SQLite3 DB path (%s) is not exist: %s", "cvedb", c.CveDict.SQLite3Path))
326
		}
327
	}
328
329
	for _, err := range errs {
330
		log.Error(err)
331
	}
332
333
	return len(errs) == 0
334
}
335
336
// validateDB validates configuration
337
//  dictionaryDB name is 'cvedb' or 'ovaldb'
338
func validateDB(dictionaryDBName, dbType, dbPath, dbURL string) error {
339
	log.Infof("-%s-type: %s, -%s-url: %s, -%s-path: %s",
340
		dictionaryDBName, dbType, dictionaryDBName, dbURL, dictionaryDBName, dbPath)
341
342
	switch dbType {
343
	case "sqlite3":
344
		if dbURL != "" {
345
			return fmt.Errorf("To use SQLite3, specify -%s-type=sqlite3 and -%s-path. To use as http server mode, specify -%s-type=http and -%s-url",
346
				dictionaryDBName, dictionaryDBName, dictionaryDBName, dictionaryDBName)
347
		}
348
		if ok, _ := valid.IsFilePath(dbPath); !ok {
349
			return fmt.Errorf("SQLite3 path must be a *Absolute* file path. -%s-path: %s",
350
				dictionaryDBName, dbPath)
351
		}
352
	case "mysql":
353
		if dbURL == "" {
354
			return fmt.Errorf(`MySQL connection string is needed. -%s-url="user:pass@tcp(localhost:3306)/dbname"`,
355
				dictionaryDBName)
356
		}
357
	case "postgres":
358
		if dbURL == "" {
359
			return fmt.Errorf(`PostgreSQL connection string is needed. -%s-url="host=myhost user=user dbname=dbname sslmode=disable password=password"`,
360
				dictionaryDBName)
361
		}
362
	case "redis":
363
		if dbURL == "" {
364
			return fmt.Errorf(`Redis connection string is needed. -%s-url="redis://localhost/0"`,
365
				dictionaryDBName)
366
		}
367
	case "http":
368
		if dbURL == "" {
369
			return fmt.Errorf(`URL is needed. -%s-url="http://localhost:1323"`,
370
				dictionaryDBName)
371
		}
372
	default:
373
		return fmt.Errorf("%s type must be either 'sqlite3', 'mysql', 'postgres', 'redis' or 'http'.  -%s-type: %s",
374
			dictionaryDBName, dictionaryDBName, dbType)
375
	}
376
	return nil
377
}
378
379
// SMTPConf is smtp config
380
type SMTPConf struct {
381
	SMTPAddr      string   `toml:"smtpAddr,omitempty" json:"-"`
382
	SMTPPort      string   `toml:"smtpPort,omitempty" valid:"port" json:"-"`
383
	User          string   `toml:"user,omitempty" json:"-"`
384
	Password      string   `toml:"password,omitempty" json:"-"`
385
	From          string   `toml:"from,omitempty" json:"-"`
386
	To            []string `toml:"to,omitempty" json:"-"`
387
	Cc            []string `toml:"cc,omitempty" json:"-"`
388
	SubjectPrefix string   `toml:"subjectPrefix,omitempty" json:"-"`
389
}
390
391
func checkEmails(emails []string) (errs []error) {
392
	for _, addr := range emails {
393
		if len(addr) == 0 {
394
			return
395
		}
396
		if ok := valid.IsEmail(addr); !ok {
397
			errs = append(errs, fmt.Errorf("Invalid email address. email: %s", addr))
398
		}
399
	}
400
	return
401
}
402
403
// Validate SMTP configuration
404
func (c *SMTPConf) Validate() (errs []error) {
405
	if !Conf.ToEmail {
406
		return
407
	}
408
	// Check Emails fromat
409
	emails := []string{}
410
	emails = append(emails, c.From)
411
	emails = append(emails, c.To...)
412
	emails = append(emails, c.Cc...)
413
414
	if emailErrs := checkEmails(emails); 0 < len(emailErrs) {
415
		errs = append(errs, emailErrs...)
416
	}
417
418
	if len(c.SMTPAddr) == 0 {
419
		errs = append(errs, fmt.Errorf("email.smtpAddr must not be empty"))
420
	}
421
	if len(c.SMTPPort) == 0 {
422
		errs = append(errs, fmt.Errorf("email.smtpPort must not be empty"))
423
	}
424
	if len(c.To) == 0 {
425
		errs = append(errs, fmt.Errorf("email.To required at least one address"))
426
	}
427
	if len(c.From) == 0 {
428
		errs = append(errs, fmt.Errorf("email.From required at least one address"))
429
	}
430
431
	_, err := valid.ValidateStruct(c)
432
	if err != nil {
433
		errs = append(errs, err)
434
	}
435
	return
436
}
437
438
// StrideConf is stride config
439
type StrideConf struct {
440
	HookURL   string `json:"-"`
441
	AuthToken string `json:"-"`
442
}
443
444
// Validate validates configuration
445
func (c *StrideConf) Validate() (errs []error) {
446
	if !Conf.ToStride {
447
		return
448
	}
449
450
	if len(c.HookURL) == 0 {
451
		errs = append(errs, fmt.Errorf("stride.HookURL must not be empty"))
452
	}
453
454
	if len(c.AuthToken) == 0 {
455
		errs = append(errs, fmt.Errorf("stride.AuthToken must not be empty"))
456
	}
457
458
	_, err := valid.ValidateStruct(c)
459
	if err != nil {
460
		errs = append(errs, err)
461
	}
462
	return
463
}
464
465
// SlackConf is slack config
466
type SlackConf struct {
467
	HookURL     string   `valid:"url" json:"-" toml:"hookURL,omitempty"`
468
	LegacyToken string   `json:"-" toml:"legacyToken,omitempty"`
469
	Channel     string   `json:"-" toml:"channel,omitempty"`
470
	IconEmoji   string   `json:"-" toml:"iconEmoji,omitempty"`
471
	AuthUser    string   `json:"-" toml:"authUser,omitempty"`
472
	NotifyUsers []string `toml:"notifyUsers,omitempty" json:"-"`
473
	Text        string   `json:"-"`
474
}
475
476
// Validate validates configuration
477
func (c *SlackConf) Validate() (errs []error) {
478
	if !Conf.ToSlack {
479
		return
480
	}
481
482
	if len(c.HookURL) == 0 && len(c.LegacyToken) == 0 {
483
		errs = append(errs, fmt.Errorf("slack.hookURL or slack.LegacyToken must not be empty"))
484
	}
485
486
	if len(c.Channel) == 0 {
487
		errs = append(errs, fmt.Errorf("slack.channel must not be empty"))
488
	} else {
489
		if !(strings.HasPrefix(c.Channel, "#") ||
490
			c.Channel == "${servername}") {
491
			errs = append(errs, fmt.Errorf(
492
				"channel's prefix must be '#', channel: %s", c.Channel))
493
		}
494
	}
495
496
	if len(c.AuthUser) == 0 {
497
		errs = append(errs, fmt.Errorf("slack.authUser must not be empty"))
498
	}
499
500
	_, err := valid.ValidateStruct(c)
501
	if err != nil {
502
		errs = append(errs, err)
503
	}
504
505
	return
506
}
507
508
// HipChatConf is HipChat config
509
type HipChatConf struct {
510
	AuthToken string `json:"-"`
511
	Room      string `json:"-"`
512
}
513
514
// Validate validates configuration
515
func (c *HipChatConf) Validate() (errs []error) {
516
	if !Conf.ToHipChat {
517
		return
518
	}
519
	if len(c.Room) == 0 {
520
		errs = append(errs, fmt.Errorf("hipcaht.room must not be empty"))
521
	}
522
523
	if len(c.AuthToken) == 0 {
524
		errs = append(errs, fmt.Errorf("hipcaht.AuthToken must not be empty"))
525
	}
526
527
	_, err := valid.ValidateStruct(c)
528
	if err != nil {
529
		errs = append(errs, err)
530
	}
531
	return
532
}
533
534
// ChatWorkConf is ChatWork config
535
type ChatWorkConf struct {
536
	APIToken string `json:"-"`
537
	Room     string `json:"-"`
538
}
539
540
// Validate validates configuration
541
func (c *ChatWorkConf) Validate() (errs []error) {
542
	if !Conf.ToChatWork {
543
		return
544
	}
545
	if len(c.Room) == 0 {
546
		errs = append(errs, fmt.Errorf("chatworkcaht.room must not be empty"))
547
	}
548
549
	if len(c.APIToken) == 0 {
550
		errs = append(errs, fmt.Errorf("chatworkcaht.ApiToken must not be empty"))
551
	}
552
553
	_, err := valid.ValidateStruct(c)
554
	if err != nil {
555
		errs = append(errs, err)
556
	}
557
	return
558
}
559
560
// SaasConf is stride config
561
type SaasConf struct {
562
	GroupID int    `json:"-"`
563
	Token   string `json:"-"`
564
	URL     string `json:"-"`
565
}
566
567
// Validate validates configuration
568
func (c *SaasConf) Validate() (errs []error) {
569
	if !Conf.ToSaas {
570
		return
571
	}
572
573
	if c.GroupID == 0 {
574
		errs = append(errs, fmt.Errorf("saas.GroupID must not be empty"))
575
	}
576
577
	if len(c.Token) == 0 {
578
		errs = append(errs, fmt.Errorf("saas.Token must not be empty"))
579
	}
580
581
	if len(c.URL) == 0 {
582
		errs = append(errs, fmt.Errorf("saas.URL must not be empty"))
583
	}
584
585
	_, err := valid.ValidateStruct(c)
586
	if err != nil {
587
		errs = append(errs, err)
588
	}
589
	return
590
}
591
592
// SyslogConf is syslog config
593
type SyslogConf struct {
594
	Protocol string `json:"-"`
595
	Host     string `valid:"host" json:"-"`
596
	Port     string `valid:"port" json:"-"`
597
	Severity string `json:"-"`
598
	Facility string `json:"-"`
599
	Tag      string `json:"-"`
600
	Verbose  bool   `json:"-"`
601
}
602
603
// Validate validates configuration
604
func (c *SyslogConf) Validate() (errs []error) {
605
	if !Conf.ToSyslog {
606
		return nil
607
	}
608
	//  If protocol is empty, it will connect to the local syslog server.
609
	if len(c.Protocol) > 0 && c.Protocol != "tcp" && c.Protocol != "udp" {
610
		errs = append(errs, errors.New(`syslog.protocol must be "tcp" or "udp"`))
611
	}
612
613
	// Default port: 514
614
	if c.Port == "" {
615
		c.Port = "514"
616
	}
617
618
	if _, err := c.GetSeverity(); err != nil {
619
		errs = append(errs, err)
620
	}
621
622
	if _, err := c.GetFacility(); err != nil {
623
		errs = append(errs, err)
624
	}
625
626
	if _, err := valid.ValidateStruct(c); err != nil {
627
		errs = append(errs, err)
628
	}
629
	return errs
630
}
631
632
// GetSeverity gets severity
633
func (c *SyslogConf) GetSeverity() (syslog.Priority, error) {
634
	if c.Severity == "" {
635
		return syslog.LOG_INFO, nil
636
	}
637
638
	switch c.Severity {
639
	case "emerg":
640
		return syslog.LOG_EMERG, nil
641
	case "alert":
642
		return syslog.LOG_ALERT, nil
643
	case "crit":
644
		return syslog.LOG_CRIT, nil
645
	case "err":
646
		return syslog.LOG_ERR, nil
647
	case "warning":
648
		return syslog.LOG_WARNING, nil
649
	case "notice":
650
		return syslog.LOG_NOTICE, nil
651
	case "info":
652
		return syslog.LOG_INFO, nil
653
	case "debug":
654
		return syslog.LOG_DEBUG, nil
655
	default:
656
		return -1, fmt.Errorf("Invalid severity: %s", c.Severity)
657
	}
658
}
659
660
// GetFacility gets facility
661
func (c *SyslogConf) GetFacility() (syslog.Priority, error) {
662
	if c.Facility == "" {
663
		return syslog.LOG_AUTH, nil
664
	}
665
666
	switch c.Facility {
667
	case "kern":
668
		return syslog.LOG_KERN, nil
669
	case "user":
670
		return syslog.LOG_USER, nil
671
	case "mail":
672
		return syslog.LOG_MAIL, nil
673
	case "daemon":
674
		return syslog.LOG_DAEMON, nil
675
	case "auth":
676
		return syslog.LOG_AUTH, nil
677
	case "syslog":
678
		return syslog.LOG_SYSLOG, nil
679
	case "lpr":
680
		return syslog.LOG_LPR, nil
681
	case "news":
682
		return syslog.LOG_NEWS, nil
683
	case "uucp":
684
		return syslog.LOG_UUCP, nil
685
	case "cron":
686
		return syslog.LOG_CRON, nil
687
	case "authpriv":
688
		return syslog.LOG_AUTHPRIV, nil
689
	case "ftp":
690
		return syslog.LOG_FTP, nil
691
	case "local0":
692
		return syslog.LOG_LOCAL0, nil
693
	case "local1":
694
		return syslog.LOG_LOCAL1, nil
695
	case "local2":
696
		return syslog.LOG_LOCAL2, nil
697
	case "local3":
698
		return syslog.LOG_LOCAL3, nil
699
	case "local4":
700
		return syslog.LOG_LOCAL4, nil
701
	case "local5":
702
		return syslog.LOG_LOCAL5, nil
703
	case "local6":
704
		return syslog.LOG_LOCAL6, nil
705
	case "local7":
706
		return syslog.LOG_LOCAL7, nil
707
	default:
708
		return -1, fmt.Errorf("Invalid facility: %s", c.Facility)
709
	}
710
}
711
712
// HTTPConf is HTTP config
713
type HTTPConf struct {
714
	URL string `valid:"url" json:"-"`
715
}
716
717
// Validate validates configuration
718
func (c *HTTPConf) Validate() (errs []error) {
719
	if !Conf.ToHTTP {
720
		return nil
721
	}
722
723
	if _, err := valid.ValidateStruct(c); err != nil {
724
		errs = append(errs, err)
725
	}
726
	return errs
727
}
728
729
const httpKey = "VULS_HTTP_URL"
730
731
// Overwrite set options with the following priority.
732
// 1. Command line option
733
// 2. Environment variable
734
// 3. config.toml
735
func (c *HTTPConf) Overwrite(cmdOpt HTTPConf) {
736
	if os.Getenv(httpKey) != "" {
737
		c.URL = os.Getenv(httpKey)
738
	}
739
	if cmdOpt.URL != "" {
740
		c.URL = cmdOpt.URL
741
	}
742
}
743
744
// GoCveDictConf is go-cve-dictionary config
745
type GoCveDictConf struct {
746
	// DB type of CVE dictionary (sqlite3, mysql, postgres or redis)
747
	Type string
748
749
	// http://cve-dictionary.com:1323 or DB connection string
750
	URL string `json:"-"`
751
752
	// /path/to/cve.sqlite3
753
	SQLite3Path string `json:"-"`
754
}
755
756
func (cnf *GoCveDictConf) setDefault() {
757
	if cnf.Type == "" {
758
		cnf.Type = "sqlite3"
759
	}
760
	if cnf.URL == "" && cnf.SQLite3Path == "" {
761
		wd, _ := os.Getwd()
762
		cnf.SQLite3Path = filepath.Join(wd, "cve.sqlite3")
763
	}
764
}
765
766
const cveDBType = "CVEDB_TYPE"
767
const cveDBURL = "CVEDB_URL"
768
const cveDBPATH = "CVEDB_SQLITE3_PATH"
769
770
// Overwrite set options with the following priority.
771
// 1. Command line option
772
// 2. Environment variable
773
// 3. config.toml
774
func (cnf *GoCveDictConf) Overwrite(cmdOpt GoCveDictConf) {
775
	if os.Getenv(cveDBType) != "" {
776
		cnf.Type = os.Getenv(cveDBType)
777
	}
778
	if os.Getenv(cveDBURL) != "" {
779
		cnf.URL = os.Getenv(cveDBURL)
780
	}
781
	if os.Getenv(cveDBPATH) != "" {
782
		cnf.SQLite3Path = os.Getenv(cveDBPATH)
783
	}
784
785
	if cmdOpt.Type != "" {
786
		cnf.Type = cmdOpt.Type
787
	}
788
	if cmdOpt.URL != "" {
789
		cnf.URL = cmdOpt.URL
790
	}
791
	if cmdOpt.SQLite3Path != "" {
792
		cnf.SQLite3Path = cmdOpt.SQLite3Path
793
	}
794
	cnf.setDefault()
795
}
796
797
// IsFetchViaHTTP returns wether fetch via http
798
func (cnf *GoCveDictConf) IsFetchViaHTTP() bool {
799
	return Conf.CveDict.Type == "http"
800
}
801
802
// GovalDictConf is goval-dictionary config
803
type GovalDictConf struct {
804
805
	// DB type of OVAL dictionary (sqlite3, mysql, postgres or redis)
806
	Type string
807
808
	// http://goval-dictionary.com:1324 or DB connection string
809
	URL string `json:"-"`
810
811
	// /path/to/oval.sqlite3
812
	SQLite3Path string `json:"-"`
813
}
814
815
func (cnf *GovalDictConf) setDefault() {
816
	if cnf.Type == "" {
817
		cnf.Type = "sqlite3"
818
	}
819
	if cnf.URL == "" && cnf.SQLite3Path == "" {
820
		wd, _ := os.Getwd()
821
		cnf.SQLite3Path = filepath.Join(wd, "oval.sqlite3")
822
	}
823
}
824
825
const govalType = "OVALDB_TYPE"
826
const govalURL = "OVALDB_URL"
827
const govalPATH = "OVALDB_SQLITE3_PATH"
828
829
// Overwrite set options with the following priority.
830
// 1. Command line option
831
// 2. Environment variable
832
// 3. config.toml
833
func (cnf *GovalDictConf) Overwrite(cmdOpt GovalDictConf) {
834
	if os.Getenv(govalType) != "" {
835
		cnf.Type = os.Getenv(govalType)
836
	}
837
	if os.Getenv(govalURL) != "" {
838
		cnf.URL = os.Getenv(govalURL)
839
	}
840
	if os.Getenv(govalPATH) != "" {
841
		cnf.SQLite3Path = os.Getenv(govalPATH)
842
	}
843
844
	if cmdOpt.Type != "" {
845
		cnf.Type = cmdOpt.Type
846
	}
847
	if cmdOpt.URL != "" {
848
		cnf.URL = cmdOpt.URL
849
	}
850
	if cmdOpt.SQLite3Path != "" {
851
		cnf.SQLite3Path = cmdOpt.SQLite3Path
852
	}
853
	cnf.setDefault()
854
}
855
856
// IsFetchViaHTTP returns wether fetch via http
857
func (cnf *GovalDictConf) IsFetchViaHTTP() bool {
858
	return Conf.OvalDict.Type == "http"
859
}
860
861
// GostConf is gost config
862
type GostConf struct {
863
	// DB type for gost dictionary (sqlite3, mysql, postgres or redis)
864
	Type string
865
866
	// http://gost-dictionary.com:1324 or DB connection string
867
	URL string `json:"-"`
868
869
	// /path/to/gost.sqlite3
870
	SQLite3Path string `json:"-"`
871
}
872
873
func (cnf *GostConf) setDefault() {
874
	if cnf.Type == "" {
875
		cnf.Type = "sqlite3"
876
	}
877
	if cnf.URL == "" && cnf.SQLite3Path == "" {
878
		wd, _ := os.Getwd()
879
		cnf.SQLite3Path = filepath.Join(wd, "gost.sqlite3")
880
	}
881
}
882
883
const gostDBType = "GOSTDB_TYPE"
884
const gostDBURL = "GOSTDB_URL"
885
const gostDBPATH = "GOSTDB_SQLITE3_PATH"
886
887
// Overwrite set options with the following priority.
888
// 1. Command line option
889
// 2. Environment variable
890
// 3. config.toml
891
func (cnf *GostConf) Overwrite(cmdOpt GostConf) {
892
	if os.Getenv(gostDBType) != "" {
893
		cnf.Type = os.Getenv(gostDBType)
894
	}
895
	if os.Getenv(gostDBURL) != "" {
896
		cnf.URL = os.Getenv(gostDBURL)
897
	}
898
	if os.Getenv(gostDBPATH) != "" {
899
		cnf.SQLite3Path = os.Getenv(gostDBPATH)
900
	}
901
902
	if cmdOpt.Type != "" {
903
		cnf.Type = cmdOpt.Type
904
	}
905
	if cmdOpt.URL != "" {
906
		cnf.URL = cmdOpt.URL
907
	}
908
	if cmdOpt.SQLite3Path != "" {
909
		cnf.SQLite3Path = cmdOpt.SQLite3Path
910
	}
911
	cnf.setDefault()
912
}
913
914
// IsFetchViaHTTP returns wether fetch via http
915
func (cnf *GostConf) IsFetchViaHTTP() bool {
916
	return Conf.Gost.Type == "http"
917
}
918
919
// ExploitConf is exploit config
920
type ExploitConf struct {
921
	// DB type for exploit dictionary (sqlite3, mysql, postgres or redis)
922
	Type string
923
924
	// http://exploit-dictionary.com:1324 or DB connection string
925
	URL string `json:"-"`
926
927
	// /path/to/exploit.sqlite3
928
	SQLite3Path string `json:"-"`
929
}
930
931
func (cnf *ExploitConf) setDefault() {
932
	if cnf.Type == "" {
933
		cnf.Type = "sqlite3"
934
	}
935
	if cnf.URL == "" && cnf.SQLite3Path == "" {
936
		wd, _ := os.Getwd()
937
		cnf.SQLite3Path = filepath.Join(wd, "go-exploitdb.sqlite3")
938
	}
939
}
940
941
const exploitDBType = "EXPLOITDB_TYPE"
942
const exploitDBURL = "EXPLOITDB_URL"
943
const exploitDBPATH = "EXPLOITDB_SQLITE3_PATH"
944
945
// Overwrite set options with the following priority.
946
// 1. Command line option
947
// 2. Environment variable
948
// 3. config.toml
949
func (cnf *ExploitConf) Overwrite(cmdOpt ExploitConf) {
950
	if os.Getenv(exploitDBType) != "" {
951
		cnf.Type = os.Getenv(exploitDBType)
952
	}
953
	if os.Getenv(exploitDBURL) != "" {
954
		cnf.URL = os.Getenv(exploitDBURL)
955
	}
956
	if os.Getenv(exploitDBPATH) != "" {
957
		cnf.SQLite3Path = os.Getenv(exploitDBPATH)
958
	}
959
960
	if cmdOpt.Type != "" {
961
		cnf.Type = cmdOpt.Type
962
	}
963
	if cmdOpt.URL != "" {
964
		cnf.URL = cmdOpt.URL
965
	}
966
	if cmdOpt.SQLite3Path != "" {
967
		cnf.SQLite3Path = cmdOpt.SQLite3Path
968
	}
969
	cnf.setDefault()
970
}
971
972
// IsFetchViaHTTP returns wether fetch via http
973
func (cnf *ExploitConf) IsFetchViaHTTP() bool {
974
	return Conf.Exploit.Type == "http"
975
}
976
977
// AWS is aws config
978
type AWS struct {
979
	// AWS profile to use
980
	Profile string `json:"profile"`
981
982
	// AWS region to use
983
	Region string `json:"region"`
984
985
	// S3 bucket name
986
	S3Bucket string `json:"s3Bucket"`
987
988
	// /bucket/path/to/results
989
	S3ResultsDir string `json:"s3ResultsDir"`
990
991
	// The Server-side encryption algorithm used when storing the reports in S3 (e.g., AES256, aws:kms).
992
	S3ServerSideEncryption string `json:"s3ServerSideEncryption"`
993
}
994
995
// Azure is azure config
996
type Azure struct {
997
	// Azure account name to use. AZURE_STORAGE_ACCOUNT environment variable is used if not specified
998
	AccountName string `json:"accountName"`
999
1000
	// Azure account key to use. AZURE_STORAGE_ACCESS_KEY environment variable is used if not specified
1001
	AccountKey string `json:"-"`
1002
1003
	// Azure storage container name
1004
	ContainerName string `json:"containerName"`
1005
}
1006
1007
// ServerInfo has SSH Info, additional CPE packages to scan.
1008
type ServerInfo struct {
1009
	ServerName             string                      `toml:"-" json:"serverName"`
1010
	User                   string                      `toml:"user,omitempty" json:"user"`
1011
	Host                   string                      `toml:"host,omitempty" json:"host"`
1012
	Port                   string                      `toml:"port,omitempty" json:"port"`
1013
	KeyPath                string                      `toml:"keyPath,omitempty" json:"keyPath"`
1014
	KeyPassword            string                      `json:"-" toml:"-"`
1015
	CpeNames               []string                    `toml:"cpeNames,omitempty" json:"cpeNames,omitempty"`
1016
	ScanMode               []string                    `toml:"scanMode,omitempty" json:"scanMode,omitempty"`
1017
	DependencyCheckXMLPath string                      `toml:"dependencyCheckXMLPath,omitempty" json:"-"` // TODO Deprecated remove in near future
1018
	OwaspDCXMLPath         string                      `toml:"owaspDCXMLPath,omitempty" json:"owaspDCXMLPath"`
1019
	ContainersIncluded     []string                    `toml:"containersIncluded,omitempty" json:"containersIncluded,omitempty"`
1020
	ContainersExcluded     []string                    `toml:"containersExcluded,omitempty" json:"containersExcluded,omitempty"`
1021
	ContainerType          string                      `toml:"containerType,omitempty" json:"containerType,omitempty"`
1022
	Containers             map[string]ContainerSetting `toml:"containers" json:"containers,omitempty"`
1023
	IgnoreCves             []string                    `toml:"ignoreCves,omitempty" json:"ignoreCves,omitempty"`
1024
	IgnorePkgsRegexp       []string                    `toml:"ignorePkgsRegexp,omitempty" json:"ignorePkgsRegexp,omitempty"`
1025
	UUIDs                  map[string]string           `toml:"uuids,omitempty" json:"uuids,omitempty"`
1026
	Memo                   string                      `toml:"memo,omitempty" json:"memo"`
1027
	Enablerepo             []string                    `toml:"enablerepo,omitempty" json:"enablerepo,omitempty"` // For CentOS, RHEL, Amazon
1028
	Optional               map[string]interface{}      `toml:"optional,omitempty" json:"optional,omitempty"`     // Optional key-value set that will be outputted to JSON
1029
	Type                   string                      `toml:"type,omitempty" json:"type"`                       // "pseudo" or ""
1030
	IPv4Addrs              []string                    `toml:"-" json:"ipv4Addrs,omitempty"`
1031
	IPv6Addrs              []string                    `toml:"-" json:"ipv6Addrs,omitempty"`
1032
1033
	// used internal
1034
	LogMsgAnsiColor string    `toml:"-" json:"-"` // DebugLog Color
1035
	Container       Container `toml:"-" json:"-"`
1036
	Distro          Distro    `toml:"-" json:"-"`
1037
	Mode            ScanMode  `toml:"-" json:"-"`
1038
}
1039
1040
// ContainerSetting is used for loading container setting in config.toml
1041
type ContainerSetting struct {
1042
	Cpes             []string `json:"cpes,omitempty"`
1043
	OwaspDCXMLPath   string   `json:"owaspDCXMLPath"`
1044
	IgnorePkgsRegexp []string `json:"ignorePkgsRegexp,omitempty"`
1045
	IgnoreCves       []string `json:"ignoreCves,omitempty"`
1046
}
1047
1048
// ScanMode has a type of scan mode. fast, fast-root, deep and offline
1049
type ScanMode struct {
1050
	flag byte
1051
}
1052
1053
// Set mode
1054
func (s *ScanMode) Set(f byte) {
1055
	s.flag |= f
1056
}
1057
1058
// IsFast return whether scan mode is fast
1059
func (s ScanMode) IsFast() bool {
1060
	return s.flag&Fast == Fast
1061
}
1062
1063
// IsFastRoot return whether scan mode is fastroot
1064
func (s ScanMode) IsFastRoot() bool {
1065
	return s.flag&FastRoot == FastRoot
1066
}
1067
1068
// IsDeep return whether scan mode is deep
1069
func (s ScanMode) IsDeep() bool {
1070
	return s.flag&Deep == Deep
1071
}
1072
1073
// IsOffline return whether scan mode is offline
1074
func (s ScanMode) IsOffline() bool {
1075
	return s.flag&Offline == Offline
1076
}
1077
1078
func (s ScanMode) validate() error {
1079
	numTrue := 0
1080
	for _, b := range []bool{s.IsFast(), s.IsFastRoot(), s.IsDeep()} {
1081
		if b {
1082
			numTrue++
1083
		}
1084
	}
1085
	if numTrue == 0 {
1086
		s.Set(Fast)
1087
	} else if s.IsDeep() && s.IsOffline() {
1088
		return fmt.Errorf("Don't specify both of -deep and offline")
1089
	} else if numTrue != 1 {
1090
		return fmt.Errorf("Specify only one of -fast, -fast-root or -deep")
1091
	}
1092
	return nil
1093
}
1094
1095
func (s ScanMode) String() string {
1096
	ss := ""
1097
	if s.IsFast() {
1098
		ss = "fast"
1099
	} else if s.IsFastRoot() {
1100
		ss = "fast-root"
1101
	} else if s.IsDeep() {
1102
		ss = "deep"
1103
	}
1104
	if s.IsOffline() {
1105
		ss += " offline"
1106
	}
1107
	return ss + " mode"
1108
}
1109
1110
const (
1111
	// Fast is fast scan mode
1112
	Fast = byte(1 << iota)
1113
	// FastRoot is fast-root scan mode
1114
	FastRoot
1115
	// Deep is deep scan mode
1116
	Deep
1117
	// Offline is offline scan mode
1118
	Offline
1119
)
1120
1121
// GetServerName returns ServerName if this serverInfo is about host.
1122
// If this serverInfo is abount a container, returns containerID@ServerName
1123
func (s ServerInfo) GetServerName() string {
1124
	if len(s.Container.ContainerID) == 0 {
1125
		return s.ServerName
1126
	}
1127
	return fmt.Sprintf("%s@%s", s.Container.Name, s.ServerName)
1128
}
1129
1130
// Distro has distribution info
1131
type Distro struct {
1132
	Family  string
1133
	Release string
1134
}
1135
1136
func (l Distro) String() string {
1137
	return fmt.Sprintf("%s %s", l.Family, l.Release)
1138
}
1139
1140
// MajorVersion returns Major version
1141
func (l Distro) MajorVersion() (ver int, err error) {
1142
	if l.Family == Amazon {
1143
		ss := strings.Fields(l.Release)
1144
		if len(ss) == 1 {
1145
			return 1, nil
1146
		}
1147
		ver, err = strconv.Atoi(ss[0])
1148
		return
1149
	}
1150
	if 0 < len(l.Release) {
1151
		ver, err = strconv.Atoi(strings.Split(l.Release, ".")[0])
1152
	} else {
1153
		err = fmt.Errorf("Release is empty")
1154
	}
1155
	return
1156
}
1157
1158
// IsContainer returns whether this ServerInfo is about container
1159
func (s ServerInfo) IsContainer() bool {
1160
	return 0 < len(s.Container.ContainerID)
1161
}
1162
1163
// SetContainer set container
1164
func (s *ServerInfo) SetContainer(d Container) {
1165
	s.Container = d
1166
}
1167
1168
// Container has Container information.
1169
type Container struct {
1170
	ContainerID string
1171
	Name        string
1172
	Image       string
1173
}
1174