1package config 2 3import ( 4 "errors" 5 "fmt" 6 "os" 7 "path/filepath" 8 "runtime" 9 "strconv" 10 "strings" 11 12 syslog "github.com/RackSec/srslog" 13 valid "github.com/asaskevich/govalidator" 14 log "github.com/sirupsen/logrus" 15 "golang.org/x/xerrors" 16) 17 18// Version of Vuls 19var Version = "`make build` or `make install` will show the version" 20 21// Revision of Git 22var Revision string 23 24// Conf has Configuration 25var Conf Config 26 27const ( 28 // RedHat is 29 RedHat = "redhat" 30 31 // Debian is 32 Debian = "debian" 33 34 // Ubuntu is 35 Ubuntu = "ubuntu" 36 37 // CentOS is 38 CentOS = "centos" 39 40 // Fedora is 41 Fedora = "fedora" 42 43 // Amazon is 44 Amazon = "amazon" 45 46 // Oracle is 47 Oracle = "oracle" 48 49 // FreeBSD is 50 FreeBSD = "freebsd" 51 52 // Raspbian is 53 Raspbian = "raspbian" 54 55 // Windows is 56 Windows = "windows" 57 58 // OpenSUSE is 59 OpenSUSE = "opensuse" 60 61 // OpenSUSELeap is 62 OpenSUSELeap = "opensuse.leap" 63 64 // SUSEEnterpriseServer is 65 SUSEEnterpriseServer = "suse.linux.enterprise.server" 66 67 // SUSEEnterpriseDesktop is 68 SUSEEnterpriseDesktop = "suse.linux.enterprise.desktop" 69 70 // SUSEOpenstackCloud is 71 SUSEOpenstackCloud = "suse.openstack.cloud" 72 73 // Alpine is 74 Alpine = "alpine" 75) 76 77const ( 78 // ServerTypePseudo is used for ServerInfo.Type 79 ServerTypePseudo = "pseudo" 80) 81 82//Config is struct of Configuration 83type Config struct { 84 Debug bool `json:"debug,omitempty"` 85 DebugSQL bool `json:"debugSQL,omitempty"` 86 Lang string `json:"lang,omitempty"` 87 HTTPProxy string `valid:"url" json:"httpProxy,omitempty"` 88 LogDir string `json:"logDir,omitempty"` 89 ResultsDir string `json:"resultsDir,omitempty"` 90 Pipe bool `json:"pipe,omitempty"` 91 Quiet bool `json:"quiet,omitempty"` 92 NoProgress bool `json:"noProgress,omitempty"` 93 94 Default ServerInfo `json:"default,omitempty"` 95 Servers map[string]ServerInfo `json:"servers,omitempty"` 96 CvssScoreOver float64 `json:"cvssScoreOver,omitempty"` 97 98 IgnoreUnscoredCves bool `json:"ignoreUnscoredCves,omitempty"` 99 IgnoreUnfixed bool `json:"ignoreUnfixed,omitempty"` 100 IgnoreGitHubDismissed bool `json:"ignore_git_hub_dismissed,omitempty"` 101 102 SSHNative bool `json:"sshNative,omitempty"` 103 SSHConfig bool `json:"sshConfig,omitempty"` 104 105 ContainersOnly bool `json:"containersOnly,omitempty"` 106 LibsOnly bool `json:"libsOnly,omitempty"` 107 WordPressOnly bool `json:"wordpressOnly,omitempty"` 108 109 CacheDBPath string `json:"cacheDBPath,omitempty"` 110 TrivyCacheDBDir string `json:"trivyCacheDBDir,omitempty"` 111 112 SkipBroken bool `json:"skipBroken,omitempty"` 113 Vvv bool `json:"vvv,omitempty"` 114 UUID bool `json:"uuid,omitempty"` 115 DetectIPS bool `json:"detectIps,omitempty"` 116 117 CveDict GoCveDictConf `json:"cveDict,omitempty"` 118 OvalDict GovalDictConf `json:"ovalDict,omitempty"` 119 Gost GostConf `json:"gost,omitempty"` 120 Exploit ExploitConf `json:"exploit,omitempty"` 121 Metasploit MetasploitConf `json:"metasploit,omitempty"` 122 123 Slack SlackConf `json:"-"` 124 EMail SMTPConf `json:"-"` 125 HTTP HTTPConf `json:"-"` 126 Syslog SyslogConf `json:"-"` 127 AWS AWS `json:"-"` 128 Azure Azure `json:"-"` 129 Stride StrideConf `json:"-"` 130 HipChat HipChatConf `json:"-"` 131 ChatWork ChatWorkConf `json:"-"` 132 Telegram TelegramConf `json:"-"` 133 Saas SaasConf `json:"-"` 134 135 RefreshCve bool `json:"refreshCve,omitempty"` 136 ToSlack bool `json:"toSlack,omitempty"` 137 ToStride bool `json:"toStride,omitempty"` 138 ToHipChat bool `json:"toHipChat,omitempty"` 139 ToChatWork bool `json:"toChatWork,omitempty"` 140 ToTelegram bool `json:"ToTelegram,omitempty"` 141 ToEmail bool `json:"toEmail,omitempty"` 142 ToSyslog bool `json:"toSyslog,omitempty"` 143 ToLocalFile bool `json:"toLocalFile,omitempty"` 144 ToS3 bool `json:"toS3,omitempty"` 145 ToAzureBlob bool `json:"toAzureBlob,omitempty"` 146 ToSaas bool `json:"toSaas,omitempty"` 147 ToHTTP bool `json:"toHTTP,omitempty"` 148 FormatXML bool `json:"formatXML,omitempty"` 149 FormatJSON bool `json:"formatJSON,omitempty"` 150 FormatOneEMail bool `json:"formatOneEMail,omitempty"` 151 FormatOneLineText bool `json:"formatOneLineText,omitempty"` 152 FormatList bool `json:"formatList,omitempty"` 153 FormatFullText bool `json:"formatFullText,omitempty"` 154 FormatCsvList bool `json:"formatCsvList,omitempty"` 155 GZIP bool `json:"gzip,omitempty"` 156 Diff bool `json:"diff,omitempty"` 157 WpIgnoreInactive bool `json:"wpIgnoreInactive,omitempty"` 158} 159 160// ValidateOnConfigtest validates 161func (c Config) ValidateOnConfigtest() bool { 162 errs := c.checkSSHKeyExist() 163 164 if runtime.GOOS == "windows" && !c.SSHNative { 165 errs = append(errs, xerrors.New("-ssh-native-insecure is needed on windows")) 166 } 167 168 _, err := valid.ValidateStruct(c) 169 if err != nil { 170 errs = append(errs, err) 171 } 172 173 for _, err := range errs { 174 log.Error(err) 175 } 176 177 return len(errs) == 0 178} 179 180// ValidateOnScan validates configuration 181func (c Config) ValidateOnScan() bool { 182 errs := c.checkSSHKeyExist() 183 184 if runtime.GOOS == "windows" && !c.SSHNative { 185 errs = append(errs, xerrors.New("-ssh-native-insecure is needed on windows")) 186 } 187 188 if len(c.ResultsDir) != 0 { 189 if ok, _ := valid.IsFilePath(c.ResultsDir); !ok { 190 errs = append(errs, xerrors.Errorf( 191 "JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir)) 192 } 193 } 194 195 if len(c.CacheDBPath) != 0 { 196 if ok, _ := valid.IsFilePath(c.CacheDBPath); !ok { 197 errs = append(errs, xerrors.Errorf( 198 "Cache DB path must be a *Absolute* file path. -cache-dbpath: %s", 199 c.CacheDBPath)) 200 } 201 } 202 203 _, err := valid.ValidateStruct(c) 204 if err != nil { 205 errs = append(errs, err) 206 } 207 208 for _, err := range errs { 209 log.Error(err) 210 } 211 212 return len(errs) == 0 213} 214 215func (c Config) checkSSHKeyExist() (errs []error) { 216 for serverName, v := range c.Servers { 217 if v.Type == ServerTypePseudo { 218 continue 219 } 220 if v.KeyPath != "" { 221 if _, err := os.Stat(v.KeyPath); err != nil { 222 errs = append(errs, xerrors.Errorf( 223 "%s is invalid. keypath: %s not exists", serverName, v.KeyPath)) 224 } 225 } 226 } 227 return errs 228} 229 230// ValidateOnReportDB validates configuration 231func (c Config) ValidateOnReportDB() bool { 232 errs := []error{} 233 234 if err := validateDB("cvedb", c.CveDict.Type, c.CveDict.SQLite3Path, c.CveDict.URL); err != nil { 235 errs = append(errs, err) 236 } 237 238 if err := validateDB("ovaldb", c.OvalDict.Type, c.OvalDict.SQLite3Path, c.OvalDict.URL); err != nil { 239 errs = append(errs, err) 240 } 241 242 if err := validateDB("gostdb", c.Gost.Type, c.Gost.SQLite3Path, c.Gost.URL); err != nil { 243 errs = append(errs, err) 244 } 245 246 if err := validateDB("exploitdb", c.Exploit.Type, c.Exploit.SQLite3Path, c.Exploit.URL); err != nil { 247 errs = append(errs, err) 248 } 249 250 if err := validateDB("msfdb", c.Metasploit.Type, c.Metasploit.SQLite3Path, c.Metasploit.URL); err != nil { 251 errs = append(errs, err) 252 } 253 254 for _, err := range errs { 255 log.Error(err) 256 } 257 258 return len(errs) == 0 259} 260 261// ValidateOnReport validates configuration 262func (c Config) ValidateOnReport() bool { 263 errs := []error{} 264 265 if len(c.ResultsDir) != 0 { 266 if ok, _ := valid.IsFilePath(c.ResultsDir); !ok { 267 errs = append(errs, xerrors.Errorf( 268 "JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir)) 269 } 270 } 271 272 _, err := valid.ValidateStruct(c) 273 if err != nil { 274 errs = append(errs, err) 275 } 276 277 if mailerrs := c.EMail.Validate(); 0 < len(mailerrs) { 278 errs = append(errs, mailerrs...) 279 } 280 281 if slackerrs := c.Slack.Validate(); 0 < len(slackerrs) { 282 errs = append(errs, slackerrs...) 283 } 284 285 if hipchaterrs := c.HipChat.Validate(); 0 < len(hipchaterrs) { 286 errs = append(errs, hipchaterrs...) 287 } 288 289 if chatworkerrs := c.ChatWork.Validate(); 0 < len(chatworkerrs) { 290 errs = append(errs, chatworkerrs...) 291 } 292 293 if strideerrs := c.Stride.Validate(); 0 < len(strideerrs) { 294 errs = append(errs, strideerrs...) 295 } 296 297 if telegramerrs := c.Telegram.Validate(); 0 < len(telegramerrs) { 298 errs = append(errs, telegramerrs...) 299 } 300 301 if saaserrs := c.Saas.Validate(); 0 < len(saaserrs) { 302 errs = append(errs, saaserrs...) 303 } 304 305 if syslogerrs := c.Syslog.Validate(); 0 < len(syslogerrs) { 306 errs = append(errs, syslogerrs...) 307 } 308 309 if httperrs := c.HTTP.Validate(); 0 < len(httperrs) { 310 errs = append(errs, httperrs...) 311 } 312 313 for _, err := range errs { 314 log.Error(err) 315 } 316 317 return len(errs) == 0 318} 319 320// ValidateOnTui validates configuration 321func (c Config) ValidateOnTui() bool { 322 errs := []error{} 323 324 if len(c.ResultsDir) != 0 { 325 if ok, _ := valid.IsFilePath(c.ResultsDir); !ok { 326 errs = append(errs, xerrors.Errorf( 327 "JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir)) 328 } 329 } 330 331 if err := validateDB("cvedb", c.CveDict.Type, c.CveDict.SQLite3Path, c.CveDict.URL); err != nil { 332 errs = append(errs, err) 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' 344func 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 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", 352 dictionaryDBName, dictionaryDBName, dictionaryDBName, dictionaryDBName) 353 } 354 if ok, _ := valid.IsFilePath(dbPath); !ok { 355 return xerrors.Errorf("SQLite3 path must be a *Absolute* file path. -%s-path: %s", 356 dictionaryDBName, dbPath) 357 } 358 case "mysql": 359 if dbURL == "" { 360 return xerrors.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 xerrors.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 xerrors.Errorf(`Redis connection string is needed. -%s-url="redis://localhost/0"`, 371 dictionaryDBName) 372 } 373 case "http": 374 if dbURL == "" { 375 return xerrors.Errorf(`URL is needed. -%s-url="http://localhost:1323"`, 376 dictionaryDBName) 377 } 378 default: 379 return xerrors.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 386type 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 397func 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, xerrors.Errorf("Invalid email address. email: %s", addr)) 404 } 405 } 406 return 407} 408 409// Validate SMTP configuration 410func (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, xerrors.New("email.smtpAddr must not be empty")) 426 } 427 if len(c.SMTPPort) == 0 { 428 errs = append(errs, xerrors.New("email.smtpPort must not be empty")) 429 } 430 if len(c.To) == 0 { 431 errs = append(errs, xerrors.New("email.To required at least one address")) 432 } 433 if len(c.From) == 0 { 434 errs = append(errs, xerrors.New("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 445type StrideConf struct { 446 HookURL string `json:"-"` 447 AuthToken string `json:"-"` 448} 449 450// Validate validates configuration 451func (c *StrideConf) Validate() (errs []error) { 452 if !Conf.ToStride { 453 return 454 } 455 456 if len(c.HookURL) == 0 { 457 errs = append(errs, xerrors.New("stride.HookURL must not be empty")) 458 } 459 460 if len(c.AuthToken) == 0 { 461 errs = append(errs, xerrors.New("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 472type 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 483func (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, xerrors.New("slack.hookURL or slack.LegacyToken must not be empty")) 490 } 491 492 if len(c.Channel) == 0 { 493 errs = append(errs, xerrors.New("slack.channel must not be empty")) 494 } else { 495 if !(strings.HasPrefix(c.Channel, "#") || 496 c.Channel == "${servername}") { 497 errs = append(errs, xerrors.Errorf( 498 "channel's prefix must be '#', channel: %s", c.Channel)) 499 } 500 } 501 502 if len(c.AuthUser) == 0 { 503 errs = append(errs, xerrors.New("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 515type HipChatConf struct { 516 AuthToken string `json:"-"` 517 Room string `json:"-"` 518} 519 520// Validate validates configuration 521func (c *HipChatConf) Validate() (errs []error) { 522 if !Conf.ToHipChat { 523 return 524 } 525 if len(c.Room) == 0 { 526 errs = append(errs, xerrors.New("hipchat.room must not be empty")) 527 } 528 529 if len(c.AuthToken) == 0 { 530 errs = append(errs, xerrors.New("hipchat.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 541type ChatWorkConf struct { 542 APIToken string `json:"-"` 543 Room string `json:"-"` 544} 545 546// Validate validates configuration 547func (c *ChatWorkConf) Validate() (errs []error) { 548 if !Conf.ToChatWork { 549 return 550 } 551 if len(c.Room) == 0 { 552 errs = append(errs, xerrors.New("chatWorkConf.room must not be empty")) 553 } 554 555 if len(c.APIToken) == 0 { 556 errs = append(errs, xerrors.New("chatWorkConf.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 567type TelegramConf struct { 568 Token string `json:"-"` 569 ChatID string `json:"-"` 570} 571 572// Validate validates configuration 573func (c *TelegramConf) Validate() (errs []error) { 574 if !Conf.ToTelegram { 575 return 576 } 577 if len(c.ChatID) == 0 { 578 errs = append(errs, xerrors.New("TelegramConf.ChatID must not be empty")) 579 } 580 581 if len(c.Token) == 0 { 582 errs = append(errs, xerrors.New("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 593type SaasConf struct { 594 GroupID int64 `json:"-"` 595 Token string `json:"-"` 596 URL string `json:"-"` 597} 598 599// Validate validates configuration 600func (c *SaasConf) Validate() (errs []error) { 601 if !Conf.ToSaas { 602 return 603 } 604 605 if c.GroupID == 0 { 606 errs = append(errs, xerrors.New("saas.GroupID must not be empty")) 607 } 608 609 if len(c.Token) == 0 { 610 errs = append(errs, xerrors.New("saas.Token must not be empty")) 611 } 612 613 if len(c.URL) == 0 { 614 errs = append(errs, xerrors.New("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 625type 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 636func (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 665func (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, xerrors.Errorf("Invalid severity: %s", c.Severity) 689 } 690} 691 692// GetFacility gets facility 693func (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, xerrors.Errorf("Invalid facility: %s", c.Facility) 741 } 742} 743 744// HTTPConf is HTTP config 745type HTTPConf struct { 746 URL string `valid:"url" json:"-"` 747} 748 749// Validate validates configuration 750func (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 761const 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 767func (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 777type 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 788func (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 798const cveDBType = "CVEDB_TYPE" 799const cveDBURL = "CVEDB_URL" 800const 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 806func (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 830func (cnf *GoCveDictConf) IsFetchViaHTTP() bool { 831 return Conf.CveDict.Type == "http" 832} 833 834// GovalDictConf is goval-dictionary config 835type 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 847func (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 857const govalType = "OVALDB_TYPE" 858const govalURL = "OVALDB_URL" 859const 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 865func (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 889func (cnf *GovalDictConf) IsFetchViaHTTP() bool { 890 return Conf.OvalDict.Type == "http" 891} 892 893// GostConf is gost config 894type 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 905func (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 915const gostDBType = "GOSTDB_TYPE" 916const gostDBURL = "GOSTDB_URL" 917const 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 923func (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 947func (cnf *GostConf) IsFetchViaHTTP() bool { 948 return Conf.Gost.Type == "http" 949} 950 951// ExploitConf is exploit config 952type 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 963func (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 973const exploitDBType = "EXPLOITDB_TYPE" 974const exploitDBURL = "EXPLOITDB_URL" 975const 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 981func (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 1005func (cnf *ExploitConf) IsFetchViaHTTP() bool { 1006 return Conf.Exploit.Type == "http" 1007} 1008 1009// MetasploitConf is metasploit config 1010type MetasploitConf struct { 1011 // DB type for metasploit dictionary (sqlite3, mysql, postgres or redis) 1012 Type string 1013 1014 // http://metasploit-dictionary.com:1324 or DB connection string 1015 URL string `json:"-"` 1016 1017 // /path/to/metasploit.sqlite3 1018 SQLite3Path string `json:"-"` 1019} 1020 1021func (cnf *MetasploitConf) setDefault() { 1022 if cnf.Type == "" { 1023 cnf.Type = "sqlite3" 1024 } 1025 if cnf.URL == "" && cnf.SQLite3Path == "" { 1026 wd, _ := os.Getwd() 1027 cnf.SQLite3Path = filepath.Join(wd, "go-msfdb.sqlite3") 1028 } 1029} 1030 1031const metasploitDBType = "METASPLOITDB_TYPE" 1032const metasploitDBURL = "METASPLOITDB_URL" 1033const metasploitDBPATH = "METASPLOITDB_SQLITE3_PATH" 1034 1035// Overwrite set options with the following priority. 1036// 1. Command line option 1037// 2. Environment variable 1038// 3. config.toml 1039func (cnf *MetasploitConf) Overwrite(cmdOpt MetasploitConf) { 1040 if os.Getenv(metasploitDBType) != "" { 1041 cnf.Type = os.Getenv(metasploitDBType) 1042 } 1043 if os.Getenv(metasploitDBURL) != "" { 1044 cnf.URL = os.Getenv(metasploitDBURL) 1045 } 1046 if os.Getenv(metasploitDBPATH) != "" { 1047 cnf.SQLite3Path = os.Getenv(metasploitDBPATH) 1048 } 1049 1050 if cmdOpt.Type != "" { 1051 cnf.Type = cmdOpt.Type 1052 } 1053 if cmdOpt.URL != "" { 1054 cnf.URL = cmdOpt.URL 1055 } 1056 if cmdOpt.SQLite3Path != "" { 1057 cnf.SQLite3Path = cmdOpt.SQLite3Path 1058 } 1059 cnf.setDefault() 1060} 1061 1062// IsFetchViaHTTP returns wether fetch via http 1063func (cnf *MetasploitConf) IsFetchViaHTTP() bool { 1064 return Conf.Metasploit.Type == "http" 1065} 1066 1067// AWS is aws config 1068type AWS struct { 1069 // AWS profile to use 1070 Profile string `json:"profile"` 1071 1072 // AWS region to use 1073 Region string `json:"region"` 1074 1075 // S3 bucket name 1076 S3Bucket string `json:"s3Bucket"` 1077 1078 // /bucket/path/to/results 1079 S3ResultsDir string `json:"s3ResultsDir"` 1080 1081 // The Server-side encryption algorithm used when storing the reports in S3 (e.g., AES256, aws:kms). 1082 S3ServerSideEncryption string `json:"s3ServerSideEncryption"` 1083} 1084 1085// Azure is azure config 1086type Azure struct { 1087 // Azure account name to use. AZURE_STORAGE_ACCOUNT environment variable is used if not specified 1088 AccountName string `json:"accountName"` 1089 1090 // Azure account key to use. AZURE_STORAGE_ACCESS_KEY environment variable is used if not specified 1091 AccountKey string `json:"-"` 1092 1093 // Azure storage container name 1094 ContainerName string `json:"containerName"` 1095} 1096 1097// ServerInfo has SSH Info, additional CPE packages to scan. 1098type ServerInfo struct { 1099 ServerName string `toml:"-" json:"serverName,omitempty"` 1100 User string `toml:"user,omitempty" json:"user,omitempty"` 1101 Host string `toml:"host,omitempty" json:"host,omitempty"` 1102 JumpServer []string `toml:"jumpServer,omitempty" json:"jumpServer,omitempty"` 1103 Port string `toml:"port,omitempty" json:"port,omitempty"` 1104 SSHConfigPath string `toml:"sshConfigPath,omitempty" json:"sshConfigPath,omitempty"` 1105 KeyPath string `toml:"keyPath,omitempty" json:"keyPath,omitempty"` 1106 KeyPassword string `json:"-,omitempty" toml:"-"` 1107 CpeNames []string `toml:"cpeNames,omitempty" json:"cpeNames,omitempty"` 1108 ScanMode []string `toml:"scanMode,omitempty" json:"scanMode,omitempty"` 1109 DependencyCheckXMLPath string `toml:"dependencyCheckXMLPath,omitempty" json:"-"` // TODO Deprecated remove in near future 1110 OwaspDCXMLPath string `toml:"owaspDCXMLPath,omitempty" json:"owaspDCXMLPath,omitempty"` 1111 ContainersIncluded []string `toml:"containersIncluded,omitempty" json:"containersIncluded,omitempty"` 1112 ContainersExcluded []string `toml:"containersExcluded,omitempty" json:"containersExcluded,omitempty"` 1113 ContainerType string `toml:"containerType,omitempty" json:"containerType,omitempty"` 1114 Containers map[string]ContainerSetting `toml:"containers" json:"containers,omitempty"` 1115 IgnoreCves []string `toml:"ignoreCves,omitempty" json:"ignoreCves,omitempty"` 1116 IgnorePkgsRegexp []string `toml:"ignorePkgsRegexp,omitempty" json:"ignorePkgsRegexp,omitempty"` 1117 GitHubRepos map[string]GitHubConf `toml:"githubs" json:"githubs,omitempty"` // key: owner/repo 1118 UUIDs map[string]string `toml:"uuids,omitempty" json:"uuids,omitempty"` 1119 Memo string `toml:"memo,omitempty" json:"memo,omitempty"` 1120 Enablerepo []string `toml:"enablerepo,omitempty" json:"enablerepo,omitempty"` // For CentOS, RHEL, Amazon 1121 Optional map[string]interface{} `toml:"optional,omitempty" json:"optional,omitempty"` // Optional key-value set that will be outputted to JSON 1122 Lockfiles []string `toml:"lockfiles,omitempty" json:"lockfiles,omitempty"` // ie) path/to/package-lock.json 1123 FindLock bool `toml:"findLock,omitempty" json:"findLock,omitempty"` 1124 Type string `toml:"type,omitempty" json:"type,omitempty"` // "pseudo" or "" 1125 WordPress WordPressConf `toml:"wordpress,omitempty" json:"wordpress,omitempty"` 1126 IgnoredJSONKeys []string `toml:"ignoredJSONKeys,omitempty" json:"ignoredJSONKeys,omitempty"` 1127 1128 // internal use 1129 IPv4Addrs []string `toml:"-" json:"ipv4Addrs,omitempty"` 1130 IPv6Addrs []string `toml:"-" json:"ipv6Addrs,omitempty"` 1131 IPSIdentifiers map[IPS]string `toml:"-" json:"ipsIdentifiers,omitempty"` 1132 LogMsgAnsiColor string `toml:"-" json:"-"` // DebugLog Color 1133 Container Container `toml:"-" json:"-"` 1134 Distro Distro `toml:"-" json:"-"` 1135 Mode ScanMode `toml:"-" json:"-"` 1136} 1137 1138// ContainerSetting is used for loading container setting in config.toml 1139type ContainerSetting struct { 1140 Cpes []string `json:"cpes,omitempty"` 1141 OwaspDCXMLPath string `json:"owaspDCXMLPath"` 1142 IgnorePkgsRegexp []string `json:"ignorePkgsRegexp,omitempty"` 1143 IgnoreCves []string `json:"ignoreCves,omitempty"` 1144} 1145 1146// WordPressConf used for WordPress Scanning 1147type WordPressConf struct { 1148 OSUser string `toml:"osUser" json:"osUser,omitempty"` 1149 DocRoot string `toml:"docRoot" json:"docRoot,omitempty"` 1150 CmdPath string `toml:"cmdPath" json:"cmdPath,omitempty"` 1151 WPVulnDBToken string `toml:"wpVulnDBToken" json:"-,omitempty"` 1152 IgnoreInactive bool `json:"ignoreInactive,omitempty"` 1153} 1154 1155// GitHubConf is used for GitHub integration 1156type GitHubConf struct { 1157 Token string `json:"-"` 1158} 1159 1160// ScanMode has a type of scan mode. fast, fast-root, deep and offline 1161type ScanMode struct { 1162 flag byte 1163} 1164 1165// Set mode 1166func (s *ScanMode) Set(f byte) { 1167 s.flag |= f 1168} 1169 1170// IsFast return whether scan mode is fast 1171func (s ScanMode) IsFast() bool { 1172 return s.flag&Fast == Fast 1173} 1174 1175// IsFastRoot return whether scan mode is fastroot 1176func (s ScanMode) IsFastRoot() bool { 1177 return s.flag&FastRoot == FastRoot 1178} 1179 1180// IsDeep return whether scan mode is deep 1181func (s ScanMode) IsDeep() bool { 1182 return s.flag&Deep == Deep 1183} 1184 1185// IsOffline return whether scan mode is offline 1186func (s ScanMode) IsOffline() bool { 1187 return s.flag&Offline == Offline 1188} 1189 1190func (s ScanMode) validate() error { 1191 numTrue := 0 1192 for _, b := range []bool{s.IsFast(), s.IsFastRoot(), s.IsDeep()} { 1193 if b { 1194 numTrue++ 1195 } 1196 } 1197 if numTrue == 0 { 1198 s.Set(Fast) 1199 } else if s.IsDeep() && s.IsOffline() { 1200 return xerrors.New("Don't specify both of -deep and offline") 1201 } else if numTrue != 1 { 1202 return xerrors.New("Specify only one of -fast, -fast-root or -deep") 1203 } 1204 return nil 1205} 1206 1207func (s ScanMode) String() string { 1208 ss := "" 1209 if s.IsFast() { 1210 ss = "fast" 1211 } else if s.IsFastRoot() { 1212 ss = "fast-root" 1213 } else if s.IsDeep() { 1214 ss = "deep" 1215 } 1216 if s.IsOffline() { 1217 ss += " offline" 1218 } 1219 return ss + " mode" 1220} 1221 1222const ( 1223 // Fast is fast scan mode 1224 Fast = byte(1 << iota) 1225 // FastRoot is fast-root scan mode 1226 FastRoot 1227 // Deep is deep scan mode 1228 Deep 1229 // Offline is offline scan mode 1230 Offline 1231) 1232 1233// GetServerName returns ServerName if this serverInfo is about host. 1234// If this serverInfo is about a container, returns containerID@ServerName 1235func (s ServerInfo) GetServerName() string { 1236 if len(s.Container.ContainerID) == 0 { 1237 return s.ServerName 1238 } 1239 return fmt.Sprintf("%s@%s", s.Container.Name, s.ServerName) 1240} 1241 1242// Distro has distribution info 1243type Distro struct { 1244 Family string 1245 Release string 1246} 1247 1248func (l Distro) String() string { 1249 return fmt.Sprintf("%s %s", l.Family, l.Release) 1250} 1251 1252// MajorVersion returns Major version 1253func (l Distro) MajorVersion() (int, error) { 1254 if l.Family == Amazon { 1255 ss := strings.Fields(l.Release) 1256 if len(ss) == 1 { 1257 return 1, nil 1258 } 1259 return strconv.Atoi(ss[0]) 1260 } 1261 if 0 < len(l.Release) { 1262 return strconv.Atoi(strings.Split(l.Release, ".")[0]) 1263 } 1264 return 0, xerrors.New("Release is empty") 1265} 1266 1267// IsContainer returns whether this ServerInfo is about container 1268func (s ServerInfo) IsContainer() bool { 1269 return 0 < len(s.Container.ContainerID) 1270} 1271 1272// SetContainer set container 1273func (s *ServerInfo) SetContainer(d Container) { 1274 s.Container = d 1275} 1276 1277// Container has Container information. 1278type Container struct { 1279 ContainerID string 1280 Name string 1281 Image string 1282} 1283