config.*GoCveDictConf.IsFetchViaHTTP   A
last analyzed

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