Completed
Push — master ( 967c56...3d8753 )
by kota
07:11
created

config.*TelegramConf.Validate   A

Complexity

Conditions 5

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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