1package commands 2 3import ( 4 "context" 5 "encoding/xml" 6 "flag" 7 "os" 8 "strings" 9 "time" 10 11 "github.com/google/subcommands" 12 "github.com/inconshreveable/log15" 13 c "github.com/kotakanbe/goval-dictionary/config" 14 "github.com/kotakanbe/goval-dictionary/db" 15 "github.com/kotakanbe/goval-dictionary/fetcher" 16 "github.com/kotakanbe/goval-dictionary/models" 17 "github.com/kotakanbe/goval-dictionary/util" 18 "github.com/ymomoi/goval-parser/oval" 19) 20 21// FetchUbuntuCmd is Subcommand for fetch RedHat OVAL 22type FetchUbuntuCmd struct { 23 Debug bool 24 DebugSQL bool 25 Quiet bool 26 NoDetails bool 27 LogDir string 28 LogJSON bool 29 DBPath string 30 DBType string 31 HTTPProxy string 32} 33 34// Name return subcommand name 35func (*FetchUbuntuCmd) Name() string { return "fetch-ubuntu" } 36 37// Synopsis return synopsis 38func (*FetchUbuntuCmd) Synopsis() string { return "Fetch Vulnerability dictionary from Ubuntu" } 39 40// Usage return usage 41func (*FetchUbuntuCmd) Usage() string { 42 return `fetch-ubuntu: 43 fetch-ubuntu 44 [-dbtype=sqlite3|mysql|postgres|redis] 45 [-dbpath=$PWD/oval.sqlite3 or connection string] 46 [-http-proxy=http://192.168.0.1:8080] 47 [-debug] 48 [-debug-sql] 49 [-quiet] 50 [-no-details] 51 [-log-dir=/path/to/log] 52 [-log-json] 53 54For the first time, run the below command to fetch data for all versions. 55 $ goval-dictionary fetch-ubuntu 14 16 18 19 20 56 57` 58} 59 60// SetFlags set flag 61func (p *FetchUbuntuCmd) SetFlags(f *flag.FlagSet) { 62 f.BoolVar(&p.Debug, "debug", false, "debug mode") 63 f.BoolVar(&p.DebugSQL, "debug-sql", false, "SQL debug mode") 64 f.BoolVar(&p.Quiet, "quiet", false, "quiet mode (no output)") 65 f.BoolVar(&p.NoDetails, "no-details", false, "without vulnerability details") 66 67 defaultLogDir := util.GetDefaultLogDir() 68 f.StringVar(&p.LogDir, "log-dir", defaultLogDir, "/path/to/log") 69 f.BoolVar(&p.LogJSON, "log-json", false, "output log as JSON") 70 71 pwd := os.Getenv("PWD") 72 f.StringVar(&p.DBPath, "dbpath", pwd+"/oval.sqlite3", 73 "/path/to/sqlite3 or SQL connection string") 74 75 f.StringVar(&p.DBType, "dbtype", "sqlite3", 76 "Database type to store data in (sqlite3, mysql, postgres or redis supported)") 77 78 f.StringVar( 79 &p.HTTPProxy, 80 "http-proxy", 81 "", 82 "http://proxy-url:port (default: empty)", 83 ) 84} 85 86// Execute execute 87func (p *FetchUbuntuCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { 88 c.Conf.Quiet = p.Quiet 89 c.Conf.DebugSQL = p.DebugSQL 90 c.Conf.Debug = p.Debug 91 c.Conf.DBPath = p.DBPath 92 c.Conf.DBType = p.DBType 93 c.Conf.HTTPProxy = p.HTTPProxy 94 c.Conf.NoDetails = p.NoDetails 95 96 util.SetLogger(p.LogDir, c.Conf.Quiet, c.Conf.Debug, p.LogJSON) 97 if !c.Conf.Validate() { 98 return subcommands.ExitUsageError 99 } 100 101 if len(f.Args()) == 0 { 102 log15.Error("Specify versions to fetch") 103 return subcommands.ExitUsageError 104 } 105 106 driver, locked, err := db.NewDB(c.Ubuntu, c.Conf.DBType, c.Conf.DBPath, c.Conf.DebugSQL) 107 if err != nil { 108 if locked { 109 log15.Error("Failed to open DB. Close DB connection before fetching", "err", err) 110 return subcommands.ExitFailure 111 } 112 log15.Error("Failed to open DB", "err", err) 113 return subcommands.ExitFailure 114 } 115 116 // Distinct 117 v := map[string]bool{} 118 vers := []string{} 119 for _, arg := range f.Args() { 120 v[arg] = true 121 } 122 for k := range v { 123 vers = append(vers, k) 124 } 125 126 results, err := fetcher.FetchUbuntuFiles(vers) 127 if err != nil { 128 log15.Error("Failed to fetch files", "err", err) 129 return subcommands.ExitFailure 130 } 131 132 for _, r := range results { 133 ovalroot := oval.Root{} 134 if err = xml.Unmarshal(r.Body, &ovalroot); err != nil { 135 log15.Error("Failed to unmarshal", "url", r.URL, "err", err) 136 return subcommands.ExitUsageError 137 } 138 log15.Info("Fetched", "URL", r.URL, "OVAL definitions", len(ovalroot.Definitions.Definitions)) 139 140 defs := models.ConvertUbuntuToModel(&ovalroot) 141 142 var timeformat = "2006-01-02T15:04:05" 143 t, err := time.Parse(timeformat, ovalroot.Generator.Timestamp) 144 if err != nil { 145 log15.Error("Failed to parse time", "err", err) 146 return subcommands.ExitFailure 147 } 148 149 root := models.Root{ 150 Family: c.Ubuntu, 151 OSVersion: r.Target, 152 Definitions: defs, 153 Timestamp: time.Now(), 154 } 155 156 ss := strings.Split(r.URL, "/") 157 fmeta := models.FetchMeta{ 158 Timestamp: t, 159 FileName: ss[len(ss)-1], 160 } 161 162 if err := driver.InsertOval(c.Ubuntu, &root, fmeta); err != nil { 163 log15.Error("Failed to insert OVAL", "err", err) 164 return subcommands.ExitFailure 165 } 166 if err := driver.InsertFetchMeta(fmeta); err != nil { 167 log15.Error("Failed to insert meta", "err", err) 168 return subcommands.ExitFailure 169 } 170 } 171 172 return subcommands.ExitSuccess 173} 174