Completed
Push — master ( 256c99...56d7d4 )
by kota
05:33
created

config.IntegrationConf.New   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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