From 337e63ac2ef2d11cd3274455752603faee5bee5a Mon Sep 17 00:00:00 2001 From: MaineK00n Date: Wed, 3 Jul 2024 14:41:51 +0900 Subject: [PATCH 1/2] feat: select advisories --- README.md | 37 +++++- commands/select.go | 248 ++++++++++++++++++++++------------------ db/db.go | 1 + db/rdb.go | 105 +++++++++++++++++ db/redis.go | 167 +++++++++++++++++++++++++-- models/debian/debian.go | 1 + models/models.go | 1 + server/server.go | 15 +++ 8 files changed, 448 insertions(+), 127 deletions(-) diff --git a/README.md b/README.md index b93acdc1..0e99983a 100644 --- a/README.md +++ b/README.md @@ -190,11 +190,11 @@ Select from DB where package name is golang.
-`$ goval-dictionary select --by-package redhat 7 golang x86_64` +`$ goval-dictionary select package redhat 7 golang` ```bash -$ goval-dictionary select --by-package redhat 7 golang x86_64 +$ goval-dictionary select package redhat 7 golang [Apr 10 10:22:43] INFO Opening DB (sqlite3). CVE-2015-5739 {3399 319 golang 0:1.6.3-1.el7_2.1} @@ -422,7 +422,7 @@ CVE-YYYY-NNNN ```bash -$ goval-dictionary select --by-cveid redhat 7 CVE-2017-6009 +$ goval-dictionary select cve-id redhat 7 CVE-2017-6009 [Apr 12 12:12:36] INFO Opening DB (sqlite3). RHSA-2017:0837: icoutils security update (Important) Important @@ -625,6 +625,37 @@ Upper part format: ```
+### Usage: select advisories + +
+ +`Select Advisories from DB` + + +```bash +$ goval-dictionary select advisories redhat 9 +map[string][]string{ + "RHSA-2023:6482": []string{ + "CVE-2023-35789", + }, + "RHSA-2022:8418": []string{ + "CVE-2021-28153", + }, + "RHSA-2024:0811": []string{ + "CVE-2023-28486", + "CVE-2023-28487", + "CVE-2023-42465", + "CVE-2023-28486", + "CVE-2023-28487", + "CVE-2023-42465", + "CVE-2023-28486", + "CVE-2023-28487", + "CVE-2023-42465", + }, + ... +} +``` + ### Usage: Start goval-dictionary as server mode ```bash diff --git a/commands/select.go b/commands/select.go index 337bd69f..9b2fb110 100644 --- a/commands/select.go +++ b/commands/select.go @@ -10,7 +10,6 @@ import ( "github.com/vulsio/goval-dictionary/config" "github.com/vulsio/goval-dictionary/db" - "github.com/vulsio/goval-dictionary/log" "github.com/vulsio/goval-dictionary/models" ) @@ -19,124 +18,145 @@ var selectCmd = &cobra.Command{ Use: "select", Short: "Select from DB", Long: `Select from DB`, - RunE: executeSelect, } func init() { RootCmd.AddCommand(selectCmd) - selectCmd.PersistentFlags().Bool("by-package", false, "select OVAL by package name") - _ = viper.BindPFlag("by-package", selectCmd.PersistentFlags().Lookup("by-package")) + selectCmd.AddCommand( + &cobra.Command{ + Use: "package ()", + Short: "Select OVAL by package name", + Args: cobra.RangeArgs(3, 4), + RunE: func(_ *cobra.Command, args []string) error { + arch := "" + if len(args) == 4 { + switch args[0] { + case config.Amazon, config.Oracle, config.Fedora: + arch = args[3] + default: + return xerrors.Errorf("Family: %s cannot use the Architecture argument.", args[0]) + } + } - selectCmd.PersistentFlags().Bool("by-cveid", false, "select OVAL by CVE-ID") - _ = viper.BindPFlag("by-cveid", selectCmd.PersistentFlags().Lookup("by-cveid")) -} + driver, err := db.NewDB(viper.GetString("dbtype"), viper.GetString("dbpath"), viper.GetBool("debug-sql"), db.Option{}) + if err != nil { + if xerrors.Is(err, db.ErrDBLocked) { + return xerrors.Errorf("Failed to open DB. Close DB connection before fetching. err: %w", err) + } + return xerrors.Errorf("Failed to open DB. err: %w", err) + } + + fetchMeta, err := driver.GetFetchMeta() + if err != nil { + return xerrors.Errorf("Failed to get FetchMeta from DB. err: %w", err) + } + if fetchMeta.OutDated() { + return xerrors.Errorf("Failed to select command. err: SchemaVersion is old. SchemaVersion: %+v", map[string]uint{"latest": models.LatestSchemaVersion, "DB": fetchMeta.SchemaVersion}) + } + + dfs, err := driver.GetByPackName(args[0], args[1], args[2], arch) + if err != nil { + return xerrors.Errorf("Failed to get cve by package. err: %w", err) + } + + for _, d := range dfs { + for _, cve := range d.Advisory.Cves { + fmt.Printf("%s\n", cve.CveID) + for _, pack := range d.AffectedPacks { + fmt.Printf(" %v\n", pack) + } + } + } + fmt.Println("------------------") + pp.ColoringEnabled = false + _, _ = pp.Println(dfs) + + return nil + }, + Example: `$ goval-dictionary select package ubuntu 24.04 bash +$ goval-dictionary select package oracle 9 bash x86_64`, + }, + &cobra.Command{ + Use: "cve-id ()", + Short: "Select OVAL by CVE-ID", + Args: cobra.RangeArgs(3, 4), + RunE: func(_ *cobra.Command, args []string) error { + arch := "" + if len(args) == 4 { + switch args[0] { + case config.Amazon, config.Oracle, config.Fedora: + arch = args[3] + default: + return xerrors.Errorf("Family: %s cannot use the Architecture argument.", args[0]) + } + } + + driver, err := db.NewDB(viper.GetString("dbtype"), viper.GetString("dbpath"), viper.GetBool("debug-sql"), db.Option{}) + if err != nil { + if xerrors.Is(err, db.ErrDBLocked) { + return xerrors.Errorf("Failed to open DB. Close DB connection before fetching. err: %w", err) + } + return xerrors.Errorf("Failed to open DB. err: %w", err) + } + + fetchMeta, err := driver.GetFetchMeta() + if err != nil { + return xerrors.Errorf("Failed to get FetchMeta from DB. err: %w", err) + } + if fetchMeta.OutDated() { + return xerrors.Errorf("Failed to select command. err: SchemaVersion is old. SchemaVersion: %+v", map[string]uint{"latest": models.LatestSchemaVersion, "DB": fetchMeta.SchemaVersion}) + } + + dfs, err := driver.GetByCveID(args[0], args[1], args[2], arch) + if err != nil { + return xerrors.Errorf("Failed to get cve by cveID. err: %w", err) + } + for _, d := range dfs { + fmt.Printf("%s\n", d.Title) + fmt.Printf("%v\n", d.Advisory.Cves) + } + fmt.Println("------------------") + pp.ColoringEnabled = false + _, _ = pp.Println(dfs) + + return nil + }, + Example: `$ goval-dictionary select cve-id ubuntu 24.04 CVE-2024-6387 +$ goval-dictionary select cve-id oracle 9 CVE-2024-6387 x86_64`, + }, + &cobra.Command{ + Use: "advisories ", + Short: "List Advisories and Releated CVE-IDs", + Args: cobra.ExactArgs(2), + RunE: func(_ *cobra.Command, args []string) error { + driver, err := db.NewDB(viper.GetString("dbtype"), viper.GetString("dbpath"), viper.GetBool("debug-sql"), db.Option{}) + if err != nil { + if xerrors.Is(err, db.ErrDBLocked) { + return xerrors.Errorf("Failed to open DB. Close DB connection before fetching. err: %w", err) + } + return xerrors.Errorf("Failed to open DB. err: %w", err) + } + + fetchMeta, err := driver.GetFetchMeta() + if err != nil { + return xerrors.Errorf("Failed to get FetchMeta from DB. err: %w", err) + } + if fetchMeta.OutDated() { + return xerrors.Errorf("Failed to select command. err: SchemaVersion is old. SchemaVersion: %+v", map[string]uint{"latest": models.LatestSchemaVersion, "DB": fetchMeta.SchemaVersion}) + } + + m, err := driver.GetAdvisories(args[0], args[1]) + if err != nil { + return xerrors.Errorf("Failed to get cve by cveID. err: %w", err) + } + pp.ColoringEnabled = false + _, _ = pp.Println(m) + + return nil + }, + Example: `$ goval-dictionary select advisories ubuntu 24.04`, + }, + ) -func executeSelect(_ *cobra.Command, args []string) error { - if err := log.SetLogger(viper.GetBool("log-to-file"), viper.GetString("log-dir"), viper.GetBool("debug"), viper.GetBool("log-json")); err != nil { - return xerrors.Errorf("Failed to SetLogger. err: %w", err) - } - - flagPkg := viper.GetBool("by-package") - flagCveID := viper.GetBool("by-cveid") - - if (!flagPkg && !flagCveID) || (flagPkg && flagCveID) { - return xerrors.New("Failed to select command. err: specify --by-package or --by-cveid") - } - - if len(args) < 3 { - if flagPkg { - return xerrors.Errorf(` - Usage: - select OVAL by package name - $ goval-dictionary select --by-package [osFamily] [osVersion] [Package Name] [Optional: Architecture (Oracle, Amazon Only)] - `) - } - if flagCveID { - return xerrors.Errorf(` - Usage: - select OVAL by CVE-ID - $ goval-dictionary select --by-cveid [osFamily] [osVersion] [CVE-ID] [Optional: Architecture (Oracle, Amazon Only)] - `) - } - } else if len(args) > 4 { - if flagPkg { - return xerrors.Errorf(` - Usage: - select OVAL by package name - $ goval-dictionary select --by-package [osFamily] [osVersion] [Package Name] [Optional: Architecture (Oracle, Amazon Only)] - `) - } - if flagCveID { - return xerrors.Errorf(` - Usage: - select OVAL by CVE-ID - $ goval-dictionary select --by-cveid [osFamily] [osVersion] [CVE-ID] [Optional: Architecture (Oracle, Amazon Only)] - `) - } - } - - family := args[0] - release := args[1] - arg := args[2] - arch := "" - if len(args) == 4 { - switch family { - case config.Amazon, config.Oracle, config.Fedora: - arch = args[3] - default: - return xerrors.Errorf("Family: %s cannot use the Architecture argument.", family) - } - } - - driver, err := db.NewDB(viper.GetString("dbtype"), viper.GetString("dbpath"), viper.GetBool("debug-sql"), db.Option{}) - if err != nil { - if xerrors.Is(err, db.ErrDBLocked) { - return xerrors.Errorf("Failed to open DB. Close DB connection before fetching. err: %w", err) - } - return xerrors.Errorf("Failed to open DB. err: %w", err) - } - - fetchMeta, err := driver.GetFetchMeta() - if err != nil { - return xerrors.Errorf("Failed to get FetchMeta from DB. err: %w", err) - } - if fetchMeta.OutDated() { - return xerrors.Errorf("Failed to select command. err: SchemaVersion is old. SchemaVersion: %+v", map[string]uint{"latest": models.LatestSchemaVersion, "DB": fetchMeta.SchemaVersion}) - } - - if flagPkg { - dfs, err := driver.GetByPackName(family, release, arg, arch) - if err != nil { - return xerrors.Errorf("Failed to get cve by package. err: %w", err) - } - - for _, d := range dfs { - for _, cve := range d.Advisory.Cves { - fmt.Printf("%s\n", cve.CveID) - for _, pack := range d.AffectedPacks { - fmt.Printf(" %v\n", pack) - } - } - } - fmt.Println("------------------") - pp.ColoringEnabled = false - _, _ = pp.Println(dfs) - } - - if flagCveID { - dfs, err := driver.GetByCveID(family, release, arg, arch) - if err != nil { - return xerrors.Errorf("Failed to get cve by cveID. err: %w", err) - } - for _, d := range dfs { - fmt.Printf("%s\n", d.Title) - fmt.Printf("%v\n", d.Advisory.Cves) - } - fmt.Println("------------------") - pp.ColoringEnabled = false - _, _ = pp.Println(dfs) - } - - return nil } diff --git a/db/db.go b/db/db.go index 898d6805..9e672558 100644 --- a/db/db.go +++ b/db/db.go @@ -23,6 +23,7 @@ type DB interface { GetByPackName(family string, osVer string, packName string, arch string) ([]models.Definition, error) GetByCveID(family string, osVer string, cveID string, arch string) ([]models.Definition, error) + GetAdvisories(family string, osVer string) (map[string][]string, error) InsertOval(*models.Root) error CountDefs(string, string) (int, error) GetLastModified(string, string) (time.Time, error) diff --git a/db/rdb.go b/db/rdb.go index 3d47f7ad..b3e776d3 100644 --- a/db/rdb.go +++ b/db/rdb.go @@ -8,6 +8,7 @@ import ( "io" "log" "os" + "strings" "time" "github.com/cheggaaa/pb/v3" @@ -24,6 +25,7 @@ import ( c "github.com/vulsio/goval-dictionary/config" "github.com/vulsio/goval-dictionary/models" + "github.com/vulsio/goval-dictionary/util" ) // Supported DB dialects. @@ -296,6 +298,109 @@ func (r *RDBDriver) GetByCveID(family, osVer, cveID, arch string) ([]models.Defi } } +// GetAdvisories select AdvisoryID: []CVE IDs +func (r *RDBDriver) GetAdvisories(family, osVer string) (map[string][]string, error) { + family, osVer, err := formatFamilyAndOSVer(family, osVer) + if err != nil { + return nil, xerrors.Errorf("Failed to formatFamilyAndOSVer. err: %w", err) + } + + q := r.conn. + Joins("JOIN roots ON roots.id = definitions.root_id AND roots.family= ? AND roots.os_version = ?", family, osVer). + Joins("JOIN advisories ON advisories.definition_id = definitions.id"). + Joins("JOIN cves ON cves.advisory_id = advisories.id"). + Preload("Advisory"). + Preload("Advisory.Cves") + + switch family { + case c.Debian: + q = q.Preload("Debian", "dsa != \"\"") + case c.Ubuntu: + q = q.Preload("References", "ref_url LIKE ?", "https://ubuntu.com/security/notices/USN-%") + case c.RedHat: + q = q.Not("definitions.definition_id LIKE ?", "oval:com.redhat.cve:def:%").Preload("References", "source = ?", "RHSA") + case c.Oracle: + q = q.Preload("References", "source = ?", "elsa") + case c.Amazon, c.Fedora: + case c.Alpine: + return nil, nil + case c.OpenSUSE, c.OpenSUSELeap, c.SUSEEnterpriseServer, c.SUSEEnterpriseDesktop: + q = q.Preload("References", "source = ?", "SUSE-SU") + default: + return nil, nil + } + + defs := []models.Definition{} + if err := q.Find(&defs).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + + m := map[string][]string{} + switch family { + case c.Debian: + for _, d := range defs { + if d.Debian == nil || d.Debian.DSA == "" { + continue + } + for _, cve := range d.Advisory.Cves { + m[d.Debian.DSA] = append(m[d.Debian.DSA], cve.CveID) + } + } + for k := range m { + m[k] = util.Unique(m[k]) + } + return m, nil + case c.Ubuntu: + for _, d := range defs { + cves := make([]string, 0, len(d.Advisory.Cves)) + for _, cve := range d.Advisory.Cves { + cves = append(cves, cve.CveID) + } + for _, r := range d.References { + m[strings.TrimPrefix(r.RefURL, "https://ubuntu.com/security/notices/")] = append(m[strings.TrimPrefix(r.RefURL, "https://ubuntu.com/security/notices/")], cves...) + } + } + for k := range m { + m[k] = util.Unique(m[k]) + } + return m, nil + case c.RedHat, c.Oracle: + for _, d := range defs { + cves := make([]string, 0, len(d.Advisory.Cves)) + for _, cve := range d.Advisory.Cves { + cves = append(cves, cve.CveID) + } + for _, r := range d.References { + m[r.RefID] = append(m[r.RefID], cves...) + } + } + return m, nil + case c.Amazon, c.Fedora: + for _, d := range defs { + for _, cve := range d.Advisory.Cves { + m[d.Title] = append(m[d.Title], cve.CveID) + } + } + return m, nil + case c.OpenSUSE, c.OpenSUSELeap, c.SUSEEnterpriseServer, c.SUSEEnterpriseDesktop: + for _, d := range defs { + cves := make([]string, 0, len(d.Advisory.Cves)) + for _, cve := range d.Advisory.Cves { + cves = append(cves, cve.CveID) + } + for _, r := range d.References { + m[r.RefID] = append(m[r.RefID], cves...) + } + } + for k := range m { + m[k] = util.Unique(m[k]) + } + return m, nil + default: + return nil, nil + } +} + // InsertOval inserts OVAL func (r *RDBDriver) InsertOval(root *models.Root) error { family, osVer, err := formatFamilyAndOSVer(root.Family, root.OSVersion) diff --git a/db/redis.go b/db/redis.go index e17b94f2..8596b7d7 100644 --- a/db/redis.go +++ b/db/redis.go @@ -15,10 +15,12 @@ import ( "github.com/go-redis/redis/v8" "github.com/inconshreveable/log15" "github.com/spf13/viper" + "golang.org/x/exp/maps" "golang.org/x/xerrors" c "github.com/vulsio/goval-dictionary/config" "github.com/vulsio/goval-dictionary/models" + "github.com/vulsio/goval-dictionary/util" ) /** @@ -56,6 +58,8 @@ import ( │ 3 │ OVAL#FETCHMETA │ SchemaVersion │ uint │ GET Go-Oval-Dictionary Schema Version │ ├───┼─────────────────────────────┼───────────────┼───────────┼───────────────────────────────────────────┤ │ 4 │ OVAL#FETCHMETA │ LastFetchedAt │ time.Time │ GET Go-Oval-Dictionary Last Fetched Time │ + ├───┼─────────────────────────────┼───────────────┼───────────┼───────────────────────────────────────────┤ + │ 5 │ OVAL#$OSFAMILY#$VERSION#ADV │ $ADVISORYID │ []$CVEID │ GET CVEIDs related to Advisory ID │ └───┴─────────────────────────────┴───────────────┴───────────┴───────────────────────────────────────────┘ **/ @@ -66,6 +70,7 @@ const ( defKeyFormat = "OVAL#%s#%s#DEF" cveKeyFormat = "OVAL#%s#%s#CVE#%s" pkgKeyFormat = "OVAL#%s#%s#PKG#%s" + advKeyFormat = "OVAL#%s#%s#ADV" depKeyFormat = "OVAL#%s#%s#DEP" lastModifiedKeyFormat = "OVAL#%s#%s#LASTMODIFIED" fetchMetaKey = "OVAL#FETCHMETA" @@ -311,6 +316,31 @@ func fileterPacksByArch(packs []models.Package, arch string) []models.Package { return filtered } +// GetAdvisories select AdvisoryID: []CVE IDs +func (r *RedisDriver) GetAdvisories(family, osVer string) (map[string][]string, error) { + family, osVer, err := formatFamilyAndOSVer(family, osVer) + if err != nil { + return nil, xerrors.Errorf("Failed to formatFamilyAndOSVer. err: %w", err) + } + + ctx := context.Background() + v, err := r.conn.HGetAll(ctx, fmt.Sprintf(advKeyFormat, family, osVer)).Result() + if err != nil { + return nil, xerrors.Errorf("Failed to HGetAll. err: %w", err) + } + + m := map[string][]string{} + for adv, s := range v { + var cves []string + if err := json.Unmarshal([]byte(s), &cves); err != nil { + return nil, xerrors.Errorf("Failed to Unmarshal JSON. err: %w", err) + } + m[adv] = cves + } + + return m, nil +} + // InsertOval inserts OVAL func (r *RedisDriver) InsertOval(root *models.Root) (err error) { ctx := context.Background() @@ -325,8 +355,89 @@ func (r *RedisDriver) InsertOval(root *models.Root) (err error) { } log15.Info("Refreshing...", "Family", family, "Version", osVer) - // newDeps, oldDeps: {"DEFID": {"cves": {"CVEID": {}}, "packages": {"PACKNAME": {}}}} - newDeps := map[string]map[string]map[string]struct{}{} + advs := map[string][]string{} + switch family { + case c.Debian: + for _, d := range root.Definitions { + if d.Debian == nil || d.Debian.DSA == "" { + continue + } + for _, cve := range d.Advisory.Cves { + advs[d.Debian.DSA] = append(advs[d.Debian.DSA], cve.CveID) + } + } + for k := range advs { + advs[k] = util.Unique(advs[k]) + } + case c.Ubuntu: + for _, d := range root.Definitions { + cves := make([]string, 0, len(d.Advisory.Cves)) + for _, cve := range d.Advisory.Cves { + cves = append(cves, cve.CveID) + } + for _, r := range d.References { + if strings.HasPrefix(r.RefURL, "https://ubuntu.com/security/notices/USN-") { + advs[strings.TrimPrefix(r.RefURL, "https://ubuntu.com/security/notices/")] = append(advs[strings.TrimPrefix(r.RefURL, "https://ubuntu.com/security/notices/")], cves...) + } + } + } + for k := range advs { + advs[k] = util.Unique(advs[k]) + } + case c.RedHat: + for _, d := range root.Definitions { + if strings.HasPrefix(d.DefinitionID, "oval:com.redhat.cve:def:") { + continue + } + cves := make([]string, 0, len(d.Advisory.Cves)) + for _, cve := range d.Advisory.Cves { + cves = append(cves, cve.CveID) + } + for _, r := range d.References { + if r.Source == "RHSA" { + advs[r.RefID] = append(advs[r.RefID], cves...) + } + } + } + case c.Oracle: + for _, d := range root.Definitions { + cves := make([]string, 0, len(d.Advisory.Cves)) + for _, cve := range d.Advisory.Cves { + cves = append(cves, cve.CveID) + } + for _, r := range d.References { + if r.Source == "elsa" { + advs[r.RefID] = append(advs[r.RefID], cves...) + } + } + } + case c.Amazon, c.Fedora: + for _, d := range root.Definitions { + for _, cve := range d.Advisory.Cves { + advs[d.Title] = append(advs[d.Title], cve.CveID) + } + } + case c.Alpine: + case c.OpenSUSE, c.OpenSUSELeap, c.SUSEEnterpriseServer, c.SUSEEnterpriseDesktop: + for _, d := range root.Definitions { + cves := make([]string, 0, len(d.Advisory.Cves)) + for _, cve := range d.Advisory.Cves { + cves = append(cves, cve.CveID) + } + for _, r := range d.References { + if r.Source == "SUSE-SU" { + advs[r.RefID] = append(advs[r.RefID], cves...) + } + } + } + for k := range advs { + advs[k] = util.Unique(advs[k]) + } + default: + } + + // newDeps, oldDeps: {"DEFID": {"cves": {"CVEID": {}}, "packages": {"PACKNAME": {}}}, "advisories": {"ADVISORYID": {}}} + newDeps := map[string]map[string]map[string]struct{}{"advisories": {}} depKey := fmt.Sprintf(depKeyFormat, family, osVer) oldDepsStr, err := r.conn.Get(ctx, depKey).Result() if err != nil { @@ -340,6 +451,7 @@ func (r *RedisDriver) InsertOval(root *models.Root) (err error) { return xerrors.Errorf("Failed to unmarshal JSON. err: %w", err) } + log15.Info("Insert Definitions...") bar := pb.StartNew(len(root.Definitions)).SetWriter(func() io.Writer { if viper.GetBool("log-json") { return io.Discard @@ -402,16 +514,51 @@ func (r *RedisDriver) InsertOval(root *models.Root) (err error) { } bar.Finish() - pipe := r.conn.Pipeline() - for defID, definitions := range oldDeps { - for cveID := range definitions["cves"] { - _ = pipe.SRem(ctx, fmt.Sprintf(cveKeyFormat, family, osVer, cveID), defID) + log15.Info("Insert Advisories...") + bar = pb.StartNew(len(advs)).SetWriter(func() io.Writer { + if viper.GetBool("log-json") { + return io.Discard } - for pack := range definitions["packages"] { - _ = pipe.SRem(ctx, fmt.Sprintf(pkgKeyFormat, family, osVer, pack), defID) + return os.Stderr + }()) + for idx := range chunkSlice(len(advs), batchSize) { + pipe := r.conn.Pipeline() + for _, adv := range maps.Keys(advs)[idx.From:idx.To] { + var aj []byte + if aj, err = json.Marshal(advs[adv]); err != nil { + return xerrors.Errorf("Failed to marshal json. err: %w", err) + } + + _ = pipe.HSet(ctx, fmt.Sprintf(advKeyFormat, family, osVer), adv, string(aj)) + newDeps["advisories"][adv] = map[string]struct{}{} + if _, ok := oldDeps["advisories"]; ok { + delete(oldDeps["advisories"], adv) + } + } + if _, err = pipe.Exec(ctx); err != nil { + return xerrors.Errorf("Failed to exec pipeline. err: %w", err) } - if _, ok := newDeps[defID]; !ok { - _ = pipe.HDel(ctx, fmt.Sprintf(defKeyFormat, family, osVer), defID) + bar.Add(idx.To - idx.From) + } + bar.Finish() + + pipe := r.conn.Pipeline() + for k, v := range oldDeps { + switch k { + case "advisories": + for advID := range v { + _ = pipe.HDel(ctx, fmt.Sprintf(advKeyFormat, family, osVer), advID) + } + default: + for cveID := range v["cves"] { + _ = pipe.SRem(ctx, fmt.Sprintf(cveKeyFormat, family, osVer, cveID), k) + } + for pack := range v["packages"] { + _ = pipe.SRem(ctx, fmt.Sprintf(pkgKeyFormat, family, osVer, pack), k) + } + if _, ok := newDeps[k]; !ok { + _ = pipe.HDel(ctx, fmt.Sprintf(defKeyFormat, family, osVer), k) + } } } newDepsJSON, err := json.Marshal(newDeps) diff --git a/models/debian/debian.go b/models/debian/debian.go index 34a88d4b..65cada79 100644 --- a/models/debian/debian.go +++ b/models/debian/debian.go @@ -52,6 +52,7 @@ func ConvertToModel(root *Root) (defs []models.Definition) { Updated: time.Date(1000, time.January, 1, 0, 0, 0, 0, time.UTC), }, Debian: &models.Debian{ + DSA: ovaldef.Debian.DSA, MoreInfo: ovaldef.Debian.MoreInfo, Date: util.ParsedOrDefaultTime([]string{"2006-01-02"}, ovaldef.Debian.Date), }, diff --git a/models/models.go b/models/models.go index 28243967..df6a2270 100644 --- a/models/models.go +++ b/models/models.go @@ -136,6 +136,7 @@ type Debian struct { ID uint `gorm:"primary_key" json:"-"` DefinitionID uint `gorm:"index:idx_debian_definition_id" json:"-" xml:"-"` + DSA string `gorm:"type:varchar(255)"` MoreInfo string `gorm:"type:text"` Date time.Time diff --git a/server/server.go b/server/server.go index 8b507bea..ddada6d5 100644 --- a/server/server.go +++ b/server/server.go @@ -43,6 +43,7 @@ func Start(logToFile bool, logDir string, driver db.DB) error { e.GET("/packs/:family/:release/:pack", getByPackName(driver)) e.GET("/cves/:family/:release/:id/:arch", getByCveID(driver)) e.GET("/cves/:family/:release/:id", getByCveID(driver)) + e.GET("/advisories/:family/:release", getAdvisories(driver)) e.GET("/count/:family/:release", countOvalDefs(driver)) e.GET("/lastmodified/:family/:release", getLastModified(driver)) // e.Post("/cpes", getByPackName(driver)) @@ -97,6 +98,20 @@ func getByCveID(driver db.DB) echo.HandlerFunc { } } +func getAdvisories(driver db.DB) echo.HandlerFunc { + return func(c echo.Context) (err error) { + family := strings.ToLower(c.Param("family")) + release := c.Param("release") + log15.Debug("Params", "Family", family, "Release", release) + + m, err := driver.GetAdvisories(family, release) + if err != nil { + log15.Error("Failed to get advisories.", "err", err) + } + return c.JSON(http.StatusOK, m) + } +} + func countOvalDefs(driver db.DB) echo.HandlerFunc { return func(c echo.Context) (err error) { family := strings.ToLower(c.Param("family")) From 9d5b62dc37db0a2c3f54a3a58346d9216233e980 Mon Sep 17 00:00:00 2001 From: MaineK00n Date: Fri, 5 Jul 2024 14:27:27 +0900 Subject: [PATCH 2/2] fix(db/redis): fix unstable insertion --- db/redis.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/db/redis.go b/db/redis.go index 8596b7d7..e1f2eeb9 100644 --- a/db/redis.go +++ b/db/redis.go @@ -521,9 +521,10 @@ func (r *RedisDriver) InsertOval(root *models.Root) (err error) { } return os.Stderr }()) - for idx := range chunkSlice(len(advs), batchSize) { + keys := maps.Keys(advs) + for idx := range chunkSlice(len(keys), batchSize) { pipe := r.conn.Pipeline() - for _, adv := range maps.Keys(advs)[idx.From:idx.To] { + for _, adv := range keys[idx.From:idx.To] { var aj []byte if aj, err = json.Marshal(advs[adv]); err != nil { return xerrors.Errorf("Failed to marshal json. err: %w", err)