1package cmd
2
3import (
4	"fmt"
5
6	"github.com/BurntSushi/toml"
7	"github.com/inconshreveable/log15"
8
9	"github.com/knqyf263/gost/config"
10	"github.com/knqyf263/gost/db"
11	"github.com/knqyf263/gost/fetcher"
12	"github.com/knqyf263/gost/notifier"
13	"github.com/knqyf263/gost/util"
14	"github.com/spf13/cobra"
15	"github.com/spf13/viper"
16)
17
18// notifyCmd represents the notify command
19var notifyCmd = &cobra.Command{
20	Use:   "notify",
21	Short: "Notifiy update about the specified CVE",
22	Long:  `Notifiy update about the specified CVE`,
23	RunE:  executeNotify,
24}
25
26func init() {
27	RootCmd.AddCommand(notifyCmd)
28
29	RootCmd.PersistentFlags().Bool("to-email", false, "Send notification via Email")
30	viper.BindPFlag("to-email", RootCmd.PersistentFlags().Lookup("to-email"))
31	viper.SetDefault("to-email", false)
32
33	RootCmd.PersistentFlags().Bool("to-slack", false, "Send notification via Slack")
34	viper.BindPFlag("to-slack", RootCmd.PersistentFlags().Lookup("to-slack"))
35	viper.SetDefault("to-slack", false)
36}
37
38func executeNotify(cmd *cobra.Command, args []string) (err error) {
39	log15.Info("Load toml config")
40	var conf config.Config
41	if _, err = toml.DecodeFile("config.toml", &conf); err != nil {
42		return err
43	}
44	notifyRedhat(conf)
45	return err
46}
47
48func notifyRedhat(conf config.Config) error {
49	var watchCveURL []string
50	for cveID := range conf.Redhat {
51		watchCveURL = append(watchCveURL, fetcher.GetRedhatCveDetailURL(cveID))
52	}
53
54	log15.Info(fmt.Sprintf("Fetched %d CVEs", len(watchCveURL)))
55	cveJSONs, err := fetcher.RetrieveRedhatCveDetails(watchCveURL)
56	if err != nil {
57		return err
58	}
59
60	cves, err := db.ConvertRedhat(cveJSONs)
61	if err != nil {
62		return nil
63
64	}
65
66	log15.Info("Initialize Database")
67	driver, locked, err := db.NewDB(viper.GetString("dbtype"), viper.GetString("dbpath"), viper.GetBool("debug-sql"))
68	if err != nil {
69		if locked {
70			log15.Error("Failed to initialize DB. Close DB connection before fetching", "err", err)
71		}
72		return err
73	}
74
75	for _, cve := range cves {
76		// Select CVE information from DB
77		c := driver.GetRedhat(cve.Name)
78		db.ClearIDRedhat(c)
79
80		cve.Cvss3.Cvss3BaseScore = "10 (This is dummy)"
81		cve.ThreatSeverity = "High (This is dummy)"
82		body := util.DiffRedhat(c, &cve, conf.Redhat[cve.Name])
83		if body != "" {
84			subject := fmt.Sprintf("%s Update %s", conf.EMail.SubjectPrefix, cve.Name)
85			body = fmt.Sprintf("%s\nhttps://access.redhat.com/security/cve/%s\n========================================================\n",
86				cve.Name, cve.Name) + body
87			notify(subject, body, conf)
88		}
89	}
90	return nil
91}
92
93func notify(subject, body string, conf config.Config) (err error) {
94	if viper.GetBool("to-email") {
95		sender := notifier.NewEMailSender(conf.EMail)
96		log15.Info("Send e-mail")
97		if err = sender.Send(subject, body); err != nil {
98			return fmt.Errorf("Failed to send e-mail. err: %s", err)
99		}
100	}
101
102	if viper.GetBool("to-slack") {
103		log15.Info("Send slack")
104		if err = notifier.SendSlack(body, conf.Slack); err != nil {
105			return fmt.Errorf("Failed to send to Slack. err: %s", err)
106		}
107	}
108	return nil
109}
110