diff --git a/.binny.yaml b/.binny.yaml index 93c43b25..b9a6ed8a 100644 --- a/.binny.yaml +++ b/.binny.yaml @@ -117,3 +117,11 @@ tools: env: - CGO_ENABLED=0 - GO_DYN_FLAGS="" + + # used for db v6 schema management + - name: sqlc + version: + want: v1.26.0 + method: github-release + with: + repo: sqlc-dev/sqlc diff --git a/grype/db/v6/README.md b/grype/db/v6/README.md new file mode 100644 index 00000000..7961c717 --- /dev/null +++ b/grype/db/v6/README.md @@ -0,0 +1,7 @@ +```bash +make tools + +cd grype/db/v6 + + ../../../.tool/sqlc generate && go run ddl/main.go +``` \ No newline at end of file diff --git a/grype/db/v6/database_specific.go b/grype/db/v6/database_specific.go new file mode 100644 index 00000000..b3f0c791 --- /dev/null +++ b/grype/db/v6/database_specific.go @@ -0,0 +1,9 @@ +package v6 + +type DatabaseSpecificNvd struct { + VulnStatus string `json:"VulnStatus"` + CisaExploitAdd string `json:"CisaExploitAdd"` + CisaActionDue string `json:"CisaActionDue"` + CisaRequiredAction string `json:"CisaRequiredAction"` + CisaVulnerabilityName string `json:"CisaVulnerabilityName"` +} diff --git a/grype/db/v6/ddl/main.go b/grype/db/v6/ddl/main.go new file mode 100644 index 00000000..7579c7a9 --- /dev/null +++ b/grype/db/v6/ddl/main.go @@ -0,0 +1,193 @@ +package main + +import ( + "context" + "database/sql" + _ "embed" + "fmt" + v6 "github.com/anchore/grype/grype/db/v6" + "github.com/anchore/grype/grype/db/v6/store/adapter" + "github.com/anchore/grype/grype/db/v6/store/repository" + "log" + _ "modernc.org/sqlite" +) + +//go:embed schema/6_0_0.sql +var ddl string + +func main() { + if err := run(); err != nil { + log.Fatal(err) + } +} + +func run() error { + ctx := context.Background() + + db, err := sql.Open("sqlite", ":memory:") + if err != nil { + return err + } + + // create tables + if _, err := db.ExecContext(ctx, ddl); err != nil { + return err + } + + a := adapter.New(db) + + err = a.AddDatabaseSpecificNVD(ctx, v6.DatabaseSpecificNvd{ + VulnStatus: "", + CisaExploitAdd: "", + CisaActionDue: "", + CisaRequiredAction: "", + CisaVulnerabilityName: "", + }) + + if err != nil { + return err + } + + dbSpecNvd, err := a.API.ListDatabaseSpecificNvd(ctx) + if err != nil { + return err + } + + fmt.Println("All Database Specific NVD:") + for _, s := range dbSpecNvd { + log.Printf("%#v\n", s) + } + + dbSpec, err := a.API.ListDatabaseSpecific(ctx) + if err != nil { + return err + } + + fmt.Println("All Database Specific:") + for _, s := range dbSpec { + log.Printf("%#v\n", s) + } + + return nil +} + +//func stringPtr(s string) *string { +// return &s +//} + +func stringPtr(s string) sql.NullString { + if s == "" { + return sql.NullString{Valid: false} + } + return sql.NullString{String: s, Valid: true} +} + +//func run() error { +// ctx := context.Background() +// +// db, err := sql.Open("sqlite", ":memory:") +// if err != nil { +// return err +// } +// +// // create tables +// if _, err := db.ExecContext(ctx, ddl); err != nil { +// return err +// } +// +// queries := repository.New(db) +// +// // create OS +// _, err = queries.CreateOperatingSystem(ctx, repository.CreateOperatingSystemParams{ +// Name: "redhat", +// MajorVersion: "7", +// MinorVersion: stringPtr("2"), +// Codename: stringPtr("something-else"), +// }) +// if err != nil { +// return err +// } +// //log.Printf("%#v\n", os) +// +// _, err = queries.CreateOperatingSystem(ctx, repository.CreateOperatingSystemParams{ +// Name: "redhat", +// MajorVersion: "8", +// MinorVersion: stringPtr("2"), +// Codename: stringPtr("maipo"), +// }) +// if err != nil { +// return err +// } +// +// allOS, err := queries.ListOperatingSystems(ctx) //&repository.ListOperatingSystemsParams{ +// // Name: "redhat", +// // MajorVersion: "8", +// // //MinorVersion: stringPtr("2"), +// // //Codename: stringPtr("maipo"), +// //}, +// +// if err != nil { +// return err +// } +// +// fmt.Println("All OS:") +// for _, os := range allOS { +// log.Printf("%s\n", osStringer{os}) +// } +// +// //_, err = queries.CreateAffectedDistroPackage(ctx, repository.CreateAffectedDistroPackageParams{ +// // PackageName: "name!", +// // OsID: rhel8.OsID, +// //}) +// //if err != nil { +// // return err +// //} +// // +// ////log.Printf("%#v\n", os) +// // +// //allOS, err := queries.ListOperatingSystems(ctx) +// //if err != nil { +// // return err +// //} +// // +// //fmt.Println("All OS:") +// //for _, os := range allOS { +// // log.Printf("%#v\n", os) +// //} +// // +// //allAffected, err := queries.ListAffectedDistroPackagesByPackageNameAndOsMajorMinorVersion(ctx, +// // repository.ListAffectedDistroPackagesByPackageNameAndOsMajorMinorVersionParams{ +// // PackageName: "name!", +// // Name: "redhat", +// // MajorVersion: "8", +// // }) +// //if err != nil { +// // return err +// //} +// // +// //fmt.Println("All Affected:") +// //for _, affected := range allAffected { +// // log.Printf("%#v\n", affected) +// //} +// +// return nil +//} + +type osStringer struct { + repository.OperatingSystem +} + +func (o osStringer) String() string { + + //mi := "?" + //if o.MinorVersion != nil { + // mi = "." + *o.MinorVersion + //} + // + //var co string + //if o.Codename != nil { + // co = fmt.Sprintf(", codename=%s", *o.Codename) + //} + //return fmt.Sprintf("OS(%s@%s%s%s)", o.Name, o.MajorVersion, mi, co) + return fmt.Sprintf("OS(%s@%s.%s %s)", o.Name, o.MajorVersion, o.MinorVersion.String, o.Codename.String) +} diff --git a/grype/db/v6/ddl/queries/affected_cpes.sql b/grype/db/v6/ddl/queries/affected_cpes.sql new file mode 100644 index 00000000..5a9bba07 --- /dev/null +++ b/grype/db/v6/ddl/queries/affected_cpes.sql @@ -0,0 +1,22 @@ + +-- name: CreateAffectedCpe :one +INSERT INTO affected_cpes (vulnerability_id, type, vendor, product, version, "update", target_software) +VALUES (?, ?, ?, ?, ?, ?, ?) +RETURNING affected_id; + +-- name: ListAffectedCPEsByProduct :many +SELECT * FROM affected_cpes +WHERE product = ?; + +-- name: ListAffectedCPEsByProductAndVendor :many +SELECT * FROM affected_cpes +WHERE product = ? + AND vendor = ? +ORDER BY product, vendor; + +-- name: ListAffectedCPEsByProductAndVendorAndVersion :many +SELECT * FROM affected_cpes +WHERE product = ? + AND vendor = ? + AND version = ? +ORDER BY product, vendor, version; \ No newline at end of file diff --git a/grype/db/v6/ddl/queries/affected_distro_packages.sql b/grype/db/v6/ddl/queries/affected_distro_packages.sql new file mode 100644 index 00000000..20fdcea5 --- /dev/null +++ b/grype/db/v6/ddl/queries/affected_distro_packages.sql @@ -0,0 +1,13 @@ +-- name: CreateAffectedDistroPackage :one +INSERT INTO affected_distro_packages (affected_id, vulnerability_id, package_name, os_id) +VALUES (?, ?, ?, ?) +RETURNING affected_id; + +-- name: ListAffectedDistroPackagesByPackageNameAndOsMajorMinorVersion :many +SELECT adp.*, os.name, os.major_version, os.minor_version +FROM affected_distro_packages adp + JOIN operating_systems os ON adp.os_id = os.os_id +WHERE adp.package_name = ? + AND os.name = ? + AND os.major_version = ? + AND os.minor_version = ?; diff --git a/grype/db/v6/ddl/queries/affected_packages.sql b/grype/db/v6/ddl/queries/affected_packages.sql new file mode 100644 index 00000000..013e9e09 --- /dev/null +++ b/grype/db/v6/ddl/queries/affected_packages.sql @@ -0,0 +1,14 @@ +-- name: CreateAffectedPackage :one +INSERT INTO affected_packages (vulnerability_id, ecosystem, package_name, purl) +VALUES (?, ?, ?, ?) +RETURNING affected_id; + +-- name: ListAffectedPackagesByPackageName :many +SELECT * FROM affected_packages +WHERE package_name = ?; + +-- name: ListAffectedPackagesByPackageNameAndEcosystem :many +SELECT * FROM affected_packages +WHERE package_name = ? + AND ecosystem = ? +ORDER BY package_name, ecosystem; diff --git a/grype/db/v6/ddl/queries/create.sql b/grype/db/v6/ddl/queries/create.sql new file mode 100644 index 00000000..7abcf215 --- /dev/null +++ b/grype/db/v6/ddl/queries/create.sql @@ -0,0 +1,91 @@ +-- name: CreateDbMetadata :one +INSERT INTO db_metadata (build_timestamp, schema_version) +VALUES (?, ?) +RETURNING *; + +-- name: CreateProvider :one +INSERT INTO providers (name, version, date_captured, input_digest, data_oci_repository) +VALUES (?, ?, ?, ?, ?) +RETURNING provider_id; + +-- name: CreateVulnerability :one +INSERT INTO vulnerabilities (provider_id, id, modified, published, withdrawn, summary_digest, detail_digest, database_specific_id) +VALUES (?, ?, ?, ?, ?, ?, ?, ?) +RETURNING vulnerability_id; + +-- name: CreateAlias :one +INSERT INTO aliases (vulnerability_id, alias) +VALUES (?, ?) +RETURNING alias_id; + +-- name: CreateRelatedVulnerability :one +INSERT INTO related_vulnerabilities (vulnerability_id, related_vulnerability_id) +VALUES (?, ?) +RETURNING related_id; + +-- name: CreateSeverity :one +INSERT INTO severities (vulnerability_id, type, score, source, tag) +VALUES (?, ?, ?, ?, ?) +RETURNING severity_id; + +-- name: CreatePackageQualifier :one +INSERT INTO package_qualifiers (affected_id, entity_type) +VALUES (?, ?) +RETURNING qualifier_id; + +-- name: CreatePackageQualifierPlatformCpe :one +INSERT INTO package_qualifier_platform_cpes (qualifier_id, cpe) +VALUES (?, ?) +RETURNING qualifier_id; + +-- name: CreatePackageQualifierRpmModularity :one +INSERT INTO package_qualifier_rpm_modularities (qualifier_id, module) +VALUES (?, ?) +RETURNING qualifier_id; + +-- name: CreateAffected :one +INSERT INTO affected (entity_type) +VALUES (?) +RETURNING affected_id; + +-- name: CreateLogicalPackage :one +INSERT INTO logical_package (logical_package_id, affected_id) +VALUES (?, ?) +RETURNING logical_package_id; + +-- name: CreatePackageDigest :one +INSERT INTO package_digests (vulnerability_id, digest_algorithm, digest_value) +VALUES (?, ?, ?) +RETURNING *; + +-- name: CreateAffectedVersion :one +INSERT INTO affected_versions (affected_id, version) +VALUES (?, ?) +RETURNING affected_id; + +-- name: CreateNotAffectedVersion :one +INSERT INTO not_affected_versions (affected_id, version) +VALUES (?, ?) +RETURNING affected_id; + +-- name: CreateAffectedSeverity :one +INSERT INTO affected_severities (affected_id, type, score, source, tag) +VALUES (?, ?, ?, ?, ?) +RETURNING affected_id; + +-- name: CreateRangeEvent :one +INSERT INTO range_events (affected_id, type, repo, introduced, fixed, last_affected, limit, state) +VALUES (?, ?, ?, ?, ?, ?, ?, ?) +RETURNING event_id; + +-- name: CreateReference :one +INSERT INTO "references" (vulnerability_id, type, url) +VALUES (?, ?, ?) +RETURNING reference_id; + + +-- TODO: another DB? maybe via an attachment? +-- -- name: CreateDescription :one +-- INSERT INTO descriptions (digest, value) +-- VALUES (?, ?) +-- RETURNING *; diff --git a/grype/db/v6/ddl/queries/database_specific.sql b/grype/db/v6/ddl/queries/database_specific.sql new file mode 100644 index 00000000..df801d2d --- /dev/null +++ b/grype/db/v6/ddl/queries/database_specific.sql @@ -0,0 +1,7 @@ +-- name: CreateDatabaseSpecific :one +INSERT INTO database_specific (entity_type) +VALUES (?) +RETURNING db_specific_id; + +-- name: ListDatabaseSpecific :many +SELECT * FROM database_specific; diff --git a/grype/db/v6/ddl/queries/database_specific_nvd.sql b/grype/db/v6/ddl/queries/database_specific_nvd.sql new file mode 100644 index 00000000..3bc45edc --- /dev/null +++ b/grype/db/v6/ddl/queries/database_specific_nvd.sql @@ -0,0 +1,7 @@ +-- name: CreateDatabaseSpecificNvd :one +INSERT INTO database_specific_nvd (db_specific_id, vulnStatus, cisaExploitAdd, cisaActionDue, cisaRequiredAction, cisaVulnerabilityName) +VALUES (?, ?, ?, ?, ?, ?) +RETURNING db_specific_id; + +-- name: ListDatabaseSpecificNvd :many +SELECT * FROM database_specific_nvd; diff --git a/grype/db/v6/ddl/queries/operating_system.sql b/grype/db/v6/ddl/queries/operating_system.sql new file mode 100644 index 00000000..be61f602 --- /dev/null +++ b/grype/db/v6/ddl/queries/operating_system.sql @@ -0,0 +1,40 @@ +-- name: CreateOperatingSystem :one +INSERT INTO operating_systems (name, major_version, minor_version, codename) +VALUES (?, ?, ?, ?) +RETURNING os_id; + +-- TODO: this ends up creating interface{} values for input params, which is not great at runtime safety-wise... but this is a single flex statement, replacing all of the other ones below +-- -- name: ListOperatingSystems :many +-- SELECT * FROM operating_systems +-- WHERE (name = COALESCE(NULLIF(@name, ''), name)) +-- AND (major_version = COALESCE(NULLIF(@major_version, ''), major_version)) +-- AND (minor_version = COALESCE(?, minor_version)) +-- AND (codename = COALESCE(?, codename)) +-- ORDER BY name, major_version, minor_version; + +-- name: ListOperatingSystems :many +SELECT * FROM operating_systems +ORDER BY name, major_version, minor_version; + +-- name: ListOperatingSystemsByName :many +SELECT * FROM operating_systems +WHERE name = ? +ORDER BY name, major_version, minor_version; + +-- name: ListOperatingSystemsByNameAndMajorVersion :many +SELECT * FROM operating_systems +WHERE name = ? + AND major_version = ? +ORDER BY name, major_version, minor_version; + +-- name: ListOperatingSystemsByNameAndExactVersion :many +SELECT * FROM operating_systems +WHERE name = ? + AND major_version = ? + AND minor_version = ? +ORDER BY name, major_version, minor_version; + +-- name: ListOperatingSystemsByCodename :many +SELECT * FROM operating_systems +WHERE codename = ? +ORDER BY name, major_version, minor_version; diff --git a/grype/db/v6/ddl/schema/6_0_0.sql b/grype/db/v6/ddl/schema/6_0_0.sql new file mode 100644 index 00000000..0e3fb41b --- /dev/null +++ b/grype/db/v6/ddl/schema/6_0_0.sql @@ -0,0 +1,225 @@ +CREATE TABLE db_metadata +( + build_timestamp TEXT NOT NULL, + schema_version INTEGER NOT NULL +); + +CREATE TABLE providers +( + provider_id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + version TEXT, + date_captured TEXT, + input_digest TEXT, + data_oci_repository TEXT +); + +CREATE TABLE vulnerabilities +( + vulnerability_id INTEGER PRIMARY KEY, + provider_id INTEGER NOT NULL, + id TEXT NOT NULL, + modified TEXT, + published TEXT, + withdrawn TEXT, + summary_digest TEXT, + detail_digest TEXT, + database_specific_id INTEGER, + FOREIGN KEY (provider_id) REFERENCES providers (provider_id), + FOREIGN KEY (database_specific_id) REFERENCES database_specific (db_specific_id) +); + +CREATE TABLE aliases +( + alias_id INTEGER PRIMARY KEY, + vulnerability_id INTEGER NOT NULL, + alias TEXT NOT NULL, + FOREIGN KEY (vulnerability_id) REFERENCES vulnerabilities (vulnerability_id) +); + +CREATE TABLE related_vulnerabilities +( + related_id INTEGER PRIMARY KEY, + vulnerability_id INTEGER NOT NULL, + related_vulnerability_id TEXT NOT NULL, + FOREIGN KEY (vulnerability_id) REFERENCES vulnerabilities (vulnerability_id) +); + +CREATE TABLE severities +( + severity_id INTEGER PRIMARY KEY, + vulnerability_id INTEGER NOT NULL, + type TEXT NOT NULL, + score TEXT NOT NULL, + source TEXT, + tag TEXT, + FOREIGN KEY (vulnerability_id) REFERENCES vulnerabilities (vulnerability_id) +); + +CREATE TABLE package_qualifiers +( + qualifier_id INTEGER PRIMARY KEY, + affected_id INTEGER, + entity_type TEXT NOT NULL, + FOREIGN KEY (affected_id) REFERENCES affected (affected_id) +); + +CREATE TABLE package_qualifier_platform_cpes +( + qualifier_id INTEGER PRIMARY KEY, + cpe TEXT NOT NULL, -- TODO: should this be broken out into fields? + FOREIGN KEY (qualifier_id) REFERENCES package_qualifiers (qualifier_id) +); + +CREATE TABLE package_qualifier_rpm_modularities +( + qualifier_id INTEGER PRIMARY KEY, + module TEXT NOT NULL, + FOREIGN KEY (qualifier_id) REFERENCES package_qualifiers (qualifier_id) +); + +CREATE TABLE affected +( + affected_id INTEGER PRIMARY KEY, + entity_type TEXT NOT NULL +); + +CREATE TABLE affected_distro_packages +( + affected_id INTEGER PRIMARY KEY, + vulnerability_id INTEGER NOT NULL, + package_name TEXT NOT NULL, + os_id INTEGER NOT NULL, + FOREIGN KEY (affected_id) REFERENCES affected (affected_id), + FOREIGN KEY (vulnerability_id) REFERENCES vulnerabilities (vulnerability_id), + FOREIGN KEY (os_id) REFERENCES operating_systems (os_id) +); + +CREATE TABLE operating_systems +( + os_id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + major_version TEXT NOT NULL, + minor_version TEXT, + codename TEXT +); + +CREATE TABLE affected_packages +( + affected_id INTEGER PRIMARY KEY, + vulnerability_id INTEGER, + ecosystem TEXT, + package_name TEXT, + purl TEXT, + FOREIGN KEY (affected_id) REFERENCES affected (affected_id), + FOREIGN KEY (vulnerability_id) REFERENCES vulnerabilities (vulnerability_id) +); + +CREATE TABLE affected_cpes +( + affected_id INTEGER PRIMARY KEY, + vulnerability_id INTEGER NOT NULL, + -- TODO: should we add all CPE fields here? + type TEXT NOT NULL, + vendor TEXT, + product TEXT NOT NULL, + version TEXT, + "update" TEXT, + target_software TEXT, + FOREIGN KEY (affected_id) REFERENCES affected (affected_id), + FOREIGN KEY (vulnerability_id) REFERENCES vulnerabilities (vulnerability_id) +); + +CREATE TABLE logical_package +( + logical_package_id INTEGER NOT NULL, + affected_id INTEGER NOT NULL, + PRIMARY KEY (logical_package_id, affected_id), + FOREIGN KEY (affected_id) REFERENCES affected (affected_id) +); + +CREATE TABLE package_digests +( + vulnerability_id INTEGER NOT NULL, + digest_algorithm TEXT NOT NULL, + digest_value TEXT NOT NULL, + FOREIGN KEY (vulnerability_id) REFERENCES vulnerabilities (vulnerability_id) +); + +CREATE TABLE affected_versions +( + version_id INTEGER PRIMARY KEY, + affected_id INTEGER NOT NULL, + version TEXT NOT NULL, + FOREIGN KEY (affected_id) REFERENCES affected (affected_id) +); + +CREATE TABLE not_affected_versions +( + version_id INTEGER PRIMARY KEY, + affected_id INTEGER NOT NULL, + version TEXT NOT NULL, + FOREIGN KEY (affected_id) REFERENCES affected (affected_id) +); + +CREATE TABLE affected_severities +( + affected_severity_id INTEGER PRIMARY KEY, + affected_id INTEGER NOT NULL, + type TEXT NOT NULL, + score TEXT NOT NULL, + source TEXT, + tag TEXT, + FOREIGN KEY (affected_id) REFERENCES affected (affected_id) +); + +CREATE TABLE range_events +( + event_id INTEGER PRIMARY KEY, + affected_id INTEGER NOT NULL, + type TEXT NOT NULL, + repo TEXT, + introduced TEXT, + fixed TEXT, + last_affected TEXT, + "limit" TEXT, + state TEXT, + FOREIGN KEY (affected_id) REFERENCES affected (affected_id) +); + +CREATE TABLE "references" +( + reference_id INTEGER PRIMARY KEY, + vulnerability_id INTEGER NOT NULL, + type TEXT NOT NULL, + url TEXT NOT NULL, + FOREIGN KEY (vulnerability_id) REFERENCES vulnerabilities (vulnerability_id) +); + +CREATE TABLE database_specific +( + db_specific_id INTEGER PRIMARY KEY, + -- entity_type is a discriminator column (to support pseudo inheritance) + entity_type TEXT NOT NULL CHECK( entity_type IN ('nvd') ) +); + +CREATE TABLE database_specific_nvd +( + db_specific_id INTEGER PRIMARY KEY, + vulnStatus TEXT, + cisaExploitAdd TEXT, + cisaActionDue TEXT, + cisaRequiredAction TEXT, + cisaVulnerabilityName TEXT, + FOREIGN KEY (db_specific_id) REFERENCES database_specific (db_specific_id) +); + +CREATE TABLE descriptions +( + digest TEXT PRIMARY KEY, + value TEXT NOT NULL +); + +CREATE INDEX vulnerability_provider_idx ON vulnerabilities (vulnerability_id, provider_id); + +PRAGMA foreign_keys = ON; diff --git a/grype/db/v6/sqlc.yaml b/grype/db/v6/sqlc.yaml new file mode 100644 index 00000000..16bbd9dd --- /dev/null +++ b/grype/db/v6/sqlc.yaml @@ -0,0 +1,19 @@ +version: "2" +sql: + - engine: "sqlite" + queries: "ddl/queries" + schema: "ddl/schema" + gen: + # see https://docs.sqlc.dev/en/stable/reference/config.html#go for more options + go: + package: "repository" + out: "store/repository" +# emit_interface: true +# emit_pointers_for_null_types: true + emit_json_tags: true + emit_db_tags: true + emit_empty_slices: true +# emit_enum_valid_method: true +# emit_all_enum_values: true +# emit_sql_as_comment: true + json_tags_case_style: camel diff --git a/grype/db/v6/store/adapter/adapter.go b/grype/db/v6/store/adapter/adapter.go new file mode 100644 index 00000000..d6d60312 --- /dev/null +++ b/grype/db/v6/store/adapter/adapter.go @@ -0,0 +1,18 @@ +package adapter + +import ( + "database/sql" + "github.com/anchore/grype/grype/db/v6/store/repository" +) + +type Adapter struct { + db *sql.DB + API *repository.Queries +} + +func New(db *sql.DB) *Adapter { + return &Adapter{ + db: db, + API: repository.New(db), + } +} diff --git a/grype/db/v6/store/adapter/database_specific.go b/grype/db/v6/store/adapter/database_specific.go new file mode 100644 index 00000000..00ae49a5 --- /dev/null +++ b/grype/db/v6/store/adapter/database_specific.go @@ -0,0 +1,57 @@ +package adapter + +import ( + "context" + "database/sql" + "fmt" + v6 "github.com/anchore/grype/grype/db/v6" + "github.com/anchore/grype/grype/db/v6/store/repository" +) + +const nvdDatabaseSpecificEntity = "nvd" + +func stringRef(s string) sql.NullString { + if s == "" { + return sql.NullString{} + } + return sql.NullString{ + String: s, + Valid: true, + } +} + +func (a *Adapter) AddDatabaseSpecificNVD(ctx context.Context, v v6.DatabaseSpecificNvd) error { + fn := func(qtx *repository.Queries, id int64) error { + _, err := qtx.CreateDatabaseSpecificNvd(ctx, repository.CreateDatabaseSpecificNvdParams{ + DbSpecificID: id, + Vulnstatus: stringRef(v.VulnStatus), + Cisaexploitadd: stringRef(v.CisaExploitAdd), + Cisaactiondue: stringRef(v.CisaActionDue), + Cisarequiredaction: stringRef(v.CisaRequiredAction), + Cisavulnerabilityname: stringRef(v.CisaVulnerabilityName), + }) + return err + } + return a.addDatabaseSpecific(ctx, fn, nvdDatabaseSpecificEntity) +} + +func (a *Adapter) addDatabaseSpecific(ctx context.Context, fn func(qtx *repository.Queries, id int64) error, entity string) error { + if fn == nil || entity == "" { + return fmt.Errorf("invalid database specific input input") + } + + tx, err := a.db.Begin() + if err != nil { + return err + } + defer tx.Rollback() + qtx := a.API.WithTx(tx) + id, err := qtx.CreateDatabaseSpecific(ctx, entity) + if err != nil { + return err + } + if err := fn(qtx, id); err != nil { + return err + } + return tx.Commit() +} diff --git a/grype/db/v6/store/repository/affected_cpes.sql.go b/grype/db/v6/store/repository/affected_cpes.sql.go new file mode 100644 index 00000000..0d3700c5 --- /dev/null +++ b/grype/db/v6/store/repository/affected_cpes.sql.go @@ -0,0 +1,169 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.26.0 +// source: affected_cpes.sql + +package repository + +import ( + "context" + "database/sql" +) + +const createAffectedCpe = `-- name: CreateAffectedCpe :one +INSERT INTO affected_cpes (vulnerability_id, type, vendor, product, version, "update", target_software) +VALUES (?, ?, ?, ?, ?, ?, ?) +RETURNING affected_id +` + +type CreateAffectedCpeParams struct { + VulnerabilityID int64 `db:"vulnerability_id" json:"vulnerabilityId"` + Type string `db:"type" json:"type"` + Vendor sql.NullString `db:"vendor" json:"vendor"` + Product string `db:"product" json:"product"` + Version sql.NullString `db:"version" json:"version"` + Update sql.NullString `db:"update" json:"update"` + TargetSoftware sql.NullString `db:"target_software" json:"targetSoftware"` +} + +func (q *Queries) CreateAffectedCpe(ctx context.Context, arg CreateAffectedCpeParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createAffectedCpe, + arg.VulnerabilityID, + arg.Type, + arg.Vendor, + arg.Product, + arg.Version, + arg.Update, + arg.TargetSoftware, + ) + var affected_id int64 + err := row.Scan(&affected_id) + return affected_id, err +} + +const listAffectedCPEsByProduct = `-- name: ListAffectedCPEsByProduct :many +SELECT affected_id, vulnerability_id, type, vendor, product, version, "update", target_software FROM affected_cpes +WHERE product = ? +` + +func (q *Queries) ListAffectedCPEsByProduct(ctx context.Context, product string) ([]AffectedCpe, error) { + rows, err := q.db.QueryContext(ctx, listAffectedCPEsByProduct, product) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AffectedCpe{} + for rows.Next() { + var i AffectedCpe + if err := rows.Scan( + &i.AffectedID, + &i.VulnerabilityID, + &i.Type, + &i.Vendor, + &i.Product, + &i.Version, + &i.Update, + &i.TargetSoftware, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listAffectedCPEsByProductAndVendor = `-- name: ListAffectedCPEsByProductAndVendor :many +SELECT affected_id, vulnerability_id, type, vendor, product, version, "update", target_software FROM affected_cpes +WHERE product = ? + AND vendor = ? +ORDER BY product, vendor +` + +type ListAffectedCPEsByProductAndVendorParams struct { + Product string `db:"product" json:"product"` + Vendor sql.NullString `db:"vendor" json:"vendor"` +} + +func (q *Queries) ListAffectedCPEsByProductAndVendor(ctx context.Context, arg ListAffectedCPEsByProductAndVendorParams) ([]AffectedCpe, error) { + rows, err := q.db.QueryContext(ctx, listAffectedCPEsByProductAndVendor, arg.Product, arg.Vendor) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AffectedCpe{} + for rows.Next() { + var i AffectedCpe + if err := rows.Scan( + &i.AffectedID, + &i.VulnerabilityID, + &i.Type, + &i.Vendor, + &i.Product, + &i.Version, + &i.Update, + &i.TargetSoftware, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listAffectedCPEsByProductAndVendorAndVersion = `-- name: ListAffectedCPEsByProductAndVendorAndVersion :many +SELECT affected_id, vulnerability_id, type, vendor, product, version, "update", target_software FROM affected_cpes +WHERE product = ? + AND vendor = ? + AND version = ? +ORDER BY product, vendor, version +` + +type ListAffectedCPEsByProductAndVendorAndVersionParams struct { + Product string `db:"product" json:"product"` + Vendor sql.NullString `db:"vendor" json:"vendor"` + Version sql.NullString `db:"version" json:"version"` +} + +func (q *Queries) ListAffectedCPEsByProductAndVendorAndVersion(ctx context.Context, arg ListAffectedCPEsByProductAndVendorAndVersionParams) ([]AffectedCpe, error) { + rows, err := q.db.QueryContext(ctx, listAffectedCPEsByProductAndVendorAndVersion, arg.Product, arg.Vendor, arg.Version) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AffectedCpe{} + for rows.Next() { + var i AffectedCpe + if err := rows.Scan( + &i.AffectedID, + &i.VulnerabilityID, + &i.Type, + &i.Vendor, + &i.Product, + &i.Version, + &i.Update, + &i.TargetSoftware, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/grype/db/v6/store/repository/affected_distro_packages.sql.go b/grype/db/v6/store/repository/affected_distro_packages.sql.go new file mode 100644 index 00000000..c30dadf3 --- /dev/null +++ b/grype/db/v6/store/repository/affected_distro_packages.sql.go @@ -0,0 +1,99 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.26.0 +// source: affected_distro_packages.sql + +package repository + +import ( + "context" + "database/sql" +) + +const createAffectedDistroPackage = `-- name: CreateAffectedDistroPackage :one +INSERT INTO affected_distro_packages (affected_id, vulnerability_id, package_name, os_id) +VALUES (?, ?, ?, ?) +RETURNING affected_id +` + +type CreateAffectedDistroPackageParams struct { + AffectedID int64 `db:"affected_id" json:"affectedId"` + VulnerabilityID int64 `db:"vulnerability_id" json:"vulnerabilityId"` + PackageName string `db:"package_name" json:"packageName"` + OsID int64 `db:"os_id" json:"osId"` +} + +func (q *Queries) CreateAffectedDistroPackage(ctx context.Context, arg CreateAffectedDistroPackageParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createAffectedDistroPackage, + arg.AffectedID, + arg.VulnerabilityID, + arg.PackageName, + arg.OsID, + ) + var affected_id int64 + err := row.Scan(&affected_id) + return affected_id, err +} + +const listAffectedDistroPackagesByPackageNameAndOsMajorMinorVersion = `-- name: ListAffectedDistroPackagesByPackageNameAndOsMajorMinorVersion :many +SELECT adp.affected_id, adp.vulnerability_id, adp.package_name, adp.os_id, os.name, os.major_version, os.minor_version +FROM affected_distro_packages adp + JOIN operating_systems os ON adp.os_id = os.os_id +WHERE adp.package_name = ? + AND os.name = ? + AND os.major_version = ? + AND os.minor_version = ? +` + +type ListAffectedDistroPackagesByPackageNameAndOsMajorMinorVersionParams struct { + PackageName string `db:"package_name" json:"packageName"` + Name string `db:"name" json:"name"` + MajorVersion string `db:"major_version" json:"majorVersion"` + MinorVersion sql.NullString `db:"minor_version" json:"minorVersion"` +} + +type ListAffectedDistroPackagesByPackageNameAndOsMajorMinorVersionRow struct { + AffectedID int64 `db:"affected_id" json:"affectedId"` + VulnerabilityID int64 `db:"vulnerability_id" json:"vulnerabilityId"` + PackageName string `db:"package_name" json:"packageName"` + OsID int64 `db:"os_id" json:"osId"` + Name string `db:"name" json:"name"` + MajorVersion string `db:"major_version" json:"majorVersion"` + MinorVersion sql.NullString `db:"minor_version" json:"minorVersion"` +} + +func (q *Queries) ListAffectedDistroPackagesByPackageNameAndOsMajorMinorVersion(ctx context.Context, arg ListAffectedDistroPackagesByPackageNameAndOsMajorMinorVersionParams) ([]ListAffectedDistroPackagesByPackageNameAndOsMajorMinorVersionRow, error) { + rows, err := q.db.QueryContext(ctx, listAffectedDistroPackagesByPackageNameAndOsMajorMinorVersion, + arg.PackageName, + arg.Name, + arg.MajorVersion, + arg.MinorVersion, + ) + if err != nil { + return nil, err + } + defer rows.Close() + items := []ListAffectedDistroPackagesByPackageNameAndOsMajorMinorVersionRow{} + for rows.Next() { + var i ListAffectedDistroPackagesByPackageNameAndOsMajorMinorVersionRow + if err := rows.Scan( + &i.AffectedID, + &i.VulnerabilityID, + &i.PackageName, + &i.OsID, + &i.Name, + &i.MajorVersion, + &i.MinorVersion, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/grype/db/v6/store/repository/affected_packages.sql.go b/grype/db/v6/store/repository/affected_packages.sql.go new file mode 100644 index 00000000..cfffac53 --- /dev/null +++ b/grype/db/v6/store/repository/affected_packages.sql.go @@ -0,0 +1,111 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.26.0 +// source: affected_packages.sql + +package repository + +import ( + "context" + "database/sql" +) + +const createAffectedPackage = `-- name: CreateAffectedPackage :one +INSERT INTO affected_packages (vulnerability_id, ecosystem, package_name, purl) +VALUES (?, ?, ?, ?) +RETURNING affected_id +` + +type CreateAffectedPackageParams struct { + VulnerabilityID sql.NullInt64 `db:"vulnerability_id" json:"vulnerabilityId"` + Ecosystem sql.NullString `db:"ecosystem" json:"ecosystem"` + PackageName sql.NullString `db:"package_name" json:"packageName"` + Purl sql.NullString `db:"purl" json:"purl"` +} + +func (q *Queries) CreateAffectedPackage(ctx context.Context, arg CreateAffectedPackageParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createAffectedPackage, + arg.VulnerabilityID, + arg.Ecosystem, + arg.PackageName, + arg.Purl, + ) + var affected_id int64 + err := row.Scan(&affected_id) + return affected_id, err +} + +const listAffectedPackagesByPackageName = `-- name: ListAffectedPackagesByPackageName :many +SELECT affected_id, vulnerability_id, ecosystem, package_name, purl FROM affected_packages +WHERE package_name = ? +` + +func (q *Queries) ListAffectedPackagesByPackageName(ctx context.Context, packageName sql.NullString) ([]AffectedPackage, error) { + rows, err := q.db.QueryContext(ctx, listAffectedPackagesByPackageName, packageName) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AffectedPackage{} + for rows.Next() { + var i AffectedPackage + if err := rows.Scan( + &i.AffectedID, + &i.VulnerabilityID, + &i.Ecosystem, + &i.PackageName, + &i.Purl, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listAffectedPackagesByPackageNameAndEcosystem = `-- name: ListAffectedPackagesByPackageNameAndEcosystem :many +SELECT affected_id, vulnerability_id, ecosystem, package_name, purl FROM affected_packages +WHERE package_name = ? + AND ecosystem = ? +ORDER BY package_name, ecosystem +` + +type ListAffectedPackagesByPackageNameAndEcosystemParams struct { + PackageName sql.NullString `db:"package_name" json:"packageName"` + Ecosystem sql.NullString `db:"ecosystem" json:"ecosystem"` +} + +func (q *Queries) ListAffectedPackagesByPackageNameAndEcosystem(ctx context.Context, arg ListAffectedPackagesByPackageNameAndEcosystemParams) ([]AffectedPackage, error) { + rows, err := q.db.QueryContext(ctx, listAffectedPackagesByPackageNameAndEcosystem, arg.PackageName, arg.Ecosystem) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AffectedPackage{} + for rows.Next() { + var i AffectedPackage + if err := rows.Scan( + &i.AffectedID, + &i.VulnerabilityID, + &i.Ecosystem, + &i.PackageName, + &i.Purl, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/grype/db/v6/store/repository/create.sql.go b/grype/db/v6/store/repository/create.sql.go new file mode 100644 index 00000000..b8090158 --- /dev/null +++ b/grype/db/v6/store/repository/create.sql.go @@ -0,0 +1,371 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.26.0 +// source: create.sql + +package repository + +import ( + "context" + "database/sql" +) + +const createAffected = `-- name: CreateAffected :one +INSERT INTO affected (entity_type) +VALUES (?) +RETURNING affected_id +` + +func (q *Queries) CreateAffected(ctx context.Context, entityType string) (int64, error) { + row := q.db.QueryRowContext(ctx, createAffected, entityType) + var affected_id int64 + err := row.Scan(&affected_id) + return affected_id, err +} + +const createAffectedSeverity = `-- name: CreateAffectedSeverity :one +INSERT INTO affected_severities (affected_id, type, score, source, tag) +VALUES (?, ?, ?, ?, ?) +RETURNING affected_id +` + +type CreateAffectedSeverityParams struct { + AffectedID int64 `db:"affected_id" json:"affectedId"` + Type string `db:"type" json:"type"` + Score string `db:"score" json:"score"` + Source sql.NullString `db:"source" json:"source"` + Tag sql.NullString `db:"tag" json:"tag"` +} + +func (q *Queries) CreateAffectedSeverity(ctx context.Context, arg CreateAffectedSeverityParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createAffectedSeverity, + arg.AffectedID, + arg.Type, + arg.Score, + arg.Source, + arg.Tag, + ) + var affected_id int64 + err := row.Scan(&affected_id) + return affected_id, err +} + +const createAffectedVersion = `-- name: CreateAffectedVersion :one +INSERT INTO affected_versions (affected_id, version) +VALUES (?, ?) +RETURNING affected_id +` + +type CreateAffectedVersionParams struct { + AffectedID int64 `db:"affected_id" json:"affectedId"` + Version string `db:"version" json:"version"` +} + +func (q *Queries) CreateAffectedVersion(ctx context.Context, arg CreateAffectedVersionParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createAffectedVersion, arg.AffectedID, arg.Version) + var affected_id int64 + err := row.Scan(&affected_id) + return affected_id, err +} + +const createAlias = `-- name: CreateAlias :one +INSERT INTO aliases (vulnerability_id, alias) +VALUES (?, ?) +RETURNING alias_id +` + +type CreateAliasParams struct { + VulnerabilityID int64 `db:"vulnerability_id" json:"vulnerabilityId"` + Alias string `db:"alias" json:"alias"` +} + +func (q *Queries) CreateAlias(ctx context.Context, arg CreateAliasParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createAlias, arg.VulnerabilityID, arg.Alias) + var alias_id int64 + err := row.Scan(&alias_id) + return alias_id, err +} + +const createDbMetadata = `-- name: CreateDbMetadata :one +INSERT INTO db_metadata (build_timestamp, schema_version) +VALUES (?, ?) +RETURNING build_timestamp, schema_version +` + +type CreateDbMetadataParams struct { + BuildTimestamp string `db:"build_timestamp" json:"buildTimestamp"` + SchemaVersion int64 `db:"schema_version" json:"schemaVersion"` +} + +func (q *Queries) CreateDbMetadata(ctx context.Context, arg CreateDbMetadataParams) (DbMetadatum, error) { + row := q.db.QueryRowContext(ctx, createDbMetadata, arg.BuildTimestamp, arg.SchemaVersion) + var i DbMetadatum + err := row.Scan(&i.BuildTimestamp, &i.SchemaVersion) + return i, err +} + +const createLogicalPackage = `-- name: CreateLogicalPackage :one +INSERT INTO logical_package (logical_package_id, affected_id) +VALUES (?, ?) +RETURNING logical_package_id +` + +type CreateLogicalPackageParams struct { + LogicalPackageID int64 `db:"logical_package_id" json:"logicalPackageId"` + AffectedID int64 `db:"affected_id" json:"affectedId"` +} + +func (q *Queries) CreateLogicalPackage(ctx context.Context, arg CreateLogicalPackageParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createLogicalPackage, arg.LogicalPackageID, arg.AffectedID) + var logical_package_id int64 + err := row.Scan(&logical_package_id) + return logical_package_id, err +} + +const createNotAffectedVersion = `-- name: CreateNotAffectedVersion :one +INSERT INTO not_affected_versions (affected_id, version) +VALUES (?, ?) +RETURNING affected_id +` + +type CreateNotAffectedVersionParams struct { + AffectedID int64 `db:"affected_id" json:"affectedId"` + Version string `db:"version" json:"version"` +} + +func (q *Queries) CreateNotAffectedVersion(ctx context.Context, arg CreateNotAffectedVersionParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createNotAffectedVersion, arg.AffectedID, arg.Version) + var affected_id int64 + err := row.Scan(&affected_id) + return affected_id, err +} + +const createPackageDigest = `-- name: CreatePackageDigest :one +INSERT INTO package_digests (vulnerability_id, digest_algorithm, digest_value) +VALUES (?, ?, ?) +RETURNING vulnerability_id, digest_algorithm, digest_value +` + +type CreatePackageDigestParams struct { + VulnerabilityID int64 `db:"vulnerability_id" json:"vulnerabilityId"` + DigestAlgorithm string `db:"digest_algorithm" json:"digestAlgorithm"` + DigestValue string `db:"digest_value" json:"digestValue"` +} + +func (q *Queries) CreatePackageDigest(ctx context.Context, arg CreatePackageDigestParams) (PackageDigest, error) { + row := q.db.QueryRowContext(ctx, createPackageDigest, arg.VulnerabilityID, arg.DigestAlgorithm, arg.DigestValue) + var i PackageDigest + err := row.Scan(&i.VulnerabilityID, &i.DigestAlgorithm, &i.DigestValue) + return i, err +} + +const createPackageQualifier = `-- name: CreatePackageQualifier :one +INSERT INTO package_qualifiers (affected_id, entity_type) +VALUES (?, ?) +RETURNING qualifier_id +` + +type CreatePackageQualifierParams struct { + AffectedID sql.NullInt64 `db:"affected_id" json:"affectedId"` + EntityType string `db:"entity_type" json:"entityType"` +} + +func (q *Queries) CreatePackageQualifier(ctx context.Context, arg CreatePackageQualifierParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createPackageQualifier, arg.AffectedID, arg.EntityType) + var qualifier_id int64 + err := row.Scan(&qualifier_id) + return qualifier_id, err +} + +const createPackageQualifierPlatformCpe = `-- name: CreatePackageQualifierPlatformCpe :one +INSERT INTO package_qualifier_platform_cpes (qualifier_id, cpe) +VALUES (?, ?) +RETURNING qualifier_id +` + +type CreatePackageQualifierPlatformCpeParams struct { + QualifierID int64 `db:"qualifier_id" json:"qualifierId"` + Cpe string `db:"cpe" json:"cpe"` +} + +func (q *Queries) CreatePackageQualifierPlatformCpe(ctx context.Context, arg CreatePackageQualifierPlatformCpeParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createPackageQualifierPlatformCpe, arg.QualifierID, arg.Cpe) + var qualifier_id int64 + err := row.Scan(&qualifier_id) + return qualifier_id, err +} + +const createPackageQualifierRpmModularity = `-- name: CreatePackageQualifierRpmModularity :one +INSERT INTO package_qualifier_rpm_modularities (qualifier_id, module) +VALUES (?, ?) +RETURNING qualifier_id +` + +type CreatePackageQualifierRpmModularityParams struct { + QualifierID int64 `db:"qualifier_id" json:"qualifierId"` + Module string `db:"module" json:"module"` +} + +func (q *Queries) CreatePackageQualifierRpmModularity(ctx context.Context, arg CreatePackageQualifierRpmModularityParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createPackageQualifierRpmModularity, arg.QualifierID, arg.Module) + var qualifier_id int64 + err := row.Scan(&qualifier_id) + return qualifier_id, err +} + +const createProvider = `-- name: CreateProvider :one +INSERT INTO providers (name, version, date_captured, input_digest, data_oci_repository) +VALUES (?, ?, ?, ?, ?) +RETURNING provider_id +` + +type CreateProviderParams struct { + Name string `db:"name" json:"name"` + Version sql.NullString `db:"version" json:"version"` + DateCaptured sql.NullString `db:"date_captured" json:"dateCaptured"` + InputDigest sql.NullString `db:"input_digest" json:"inputDigest"` + DataOciRepository sql.NullString `db:"data_oci_repository" json:"dataOciRepository"` +} + +func (q *Queries) CreateProvider(ctx context.Context, arg CreateProviderParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createProvider, + arg.Name, + arg.Version, + arg.DateCaptured, + arg.InputDigest, + arg.DataOciRepository, + ) + var provider_id int64 + err := row.Scan(&provider_id) + return provider_id, err +} + +const createRangeEvent = `-- name: CreateRangeEvent :one +INSERT INTO range_events (affected_id, type, repo, introduced, fixed, last_affected, limit, state) +VALUES (?, ?, ?, ?, ?, ?, ?, ?) +RETURNING event_id +` + +type CreateRangeEventParams struct { + AffectedID int64 `db:"affected_id" json:"affectedId"` + Type string `db:"type" json:"type"` + Repo sql.NullString `db:"repo" json:"repo"` + Introduced sql.NullString `db:"introduced" json:"introduced"` + Fixed sql.NullString `db:"fixed" json:"fixed"` + LastAffected sql.NullString `db:"last_affected" json:"lastAffected"` + Limit sql.NullString `db:"limit" json:"limit"` + State sql.NullString `db:"state" json:"state"` +} + +func (q *Queries) CreateRangeEvent(ctx context.Context, arg CreateRangeEventParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createRangeEvent, + arg.AffectedID, + arg.Type, + arg.Repo, + arg.Introduced, + arg.Fixed, + arg.LastAffected, + arg.Limit, + arg.State, + ) + var event_id int64 + err := row.Scan(&event_id) + return event_id, err +} + +const createReference = `-- name: CreateReference :one +INSERT INTO "references" (vulnerability_id, type, url) +VALUES (?, ?, ?) +RETURNING reference_id +` + +type CreateReferenceParams struct { + VulnerabilityID int64 `db:"vulnerability_id" json:"vulnerabilityId"` + Type string `db:"type" json:"type"` + Url string `db:"url" json:"url"` +} + +func (q *Queries) CreateReference(ctx context.Context, arg CreateReferenceParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createReference, arg.VulnerabilityID, arg.Type, arg.Url) + var reference_id int64 + err := row.Scan(&reference_id) + return reference_id, err +} + +const createRelatedVulnerability = `-- name: CreateRelatedVulnerability :one +INSERT INTO related_vulnerabilities (vulnerability_id, related_vulnerability_id) +VALUES (?, ?) +RETURNING related_id +` + +type CreateRelatedVulnerabilityParams struct { + VulnerabilityID int64 `db:"vulnerability_id" json:"vulnerabilityId"` + RelatedVulnerabilityID string `db:"related_vulnerability_id" json:"relatedVulnerabilityId"` +} + +func (q *Queries) CreateRelatedVulnerability(ctx context.Context, arg CreateRelatedVulnerabilityParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createRelatedVulnerability, arg.VulnerabilityID, arg.RelatedVulnerabilityID) + var related_id int64 + err := row.Scan(&related_id) + return related_id, err +} + +const createSeverity = `-- name: CreateSeverity :one +INSERT INTO severities (vulnerability_id, type, score, source, tag) +VALUES (?, ?, ?, ?, ?) +RETURNING severity_id +` + +type CreateSeverityParams struct { + VulnerabilityID int64 `db:"vulnerability_id" json:"vulnerabilityId"` + Type string `db:"type" json:"type"` + Score string `db:"score" json:"score"` + Source sql.NullString `db:"source" json:"source"` + Tag sql.NullString `db:"tag" json:"tag"` +} + +func (q *Queries) CreateSeverity(ctx context.Context, arg CreateSeverityParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createSeverity, + arg.VulnerabilityID, + arg.Type, + arg.Score, + arg.Source, + arg.Tag, + ) + var severity_id int64 + err := row.Scan(&severity_id) + return severity_id, err +} + +const createVulnerability = `-- name: CreateVulnerability :one +INSERT INTO vulnerabilities (provider_id, id, modified, published, withdrawn, summary_digest, detail_digest, database_specific_id) +VALUES (?, ?, ?, ?, ?, ?, ?, ?) +RETURNING vulnerability_id +` + +type CreateVulnerabilityParams struct { + ProviderID int64 `db:"provider_id" json:"providerId"` + ID string `db:"id" json:"id"` + Modified sql.NullString `db:"modified" json:"modified"` + Published sql.NullString `db:"published" json:"published"` + Withdrawn sql.NullString `db:"withdrawn" json:"withdrawn"` + SummaryDigest sql.NullString `db:"summary_digest" json:"summaryDigest"` + DetailDigest sql.NullString `db:"detail_digest" json:"detailDigest"` + DatabaseSpecificID sql.NullInt64 `db:"database_specific_id" json:"databaseSpecificId"` +} + +func (q *Queries) CreateVulnerability(ctx context.Context, arg CreateVulnerabilityParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createVulnerability, + arg.ProviderID, + arg.ID, + arg.Modified, + arg.Published, + arg.Withdrawn, + arg.SummaryDigest, + arg.DetailDigest, + arg.DatabaseSpecificID, + ) + var vulnerability_id int64 + err := row.Scan(&vulnerability_id) + return vulnerability_id, err +} diff --git a/grype/db/v6/store/repository/database_specific.sql.go b/grype/db/v6/store/repository/database_specific.sql.go new file mode 100644 index 00000000..158adf43 --- /dev/null +++ b/grype/db/v6/store/repository/database_specific.sql.go @@ -0,0 +1,50 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.26.0 +// source: database_specific.sql + +package repository + +import ( + "context" +) + +const createDatabaseSpecific = `-- name: CreateDatabaseSpecific :one +INSERT INTO database_specific (entity_type) +VALUES (?) +RETURNING db_specific_id +` + +func (q *Queries) CreateDatabaseSpecific(ctx context.Context, entityType string) (int64, error) { + row := q.db.QueryRowContext(ctx, createDatabaseSpecific, entityType) + var db_specific_id int64 + err := row.Scan(&db_specific_id) + return db_specific_id, err +} + +const listDatabaseSpecific = `-- name: ListDatabaseSpecific :many +SELECT db_specific_id, entity_type FROM database_specific +` + +func (q *Queries) ListDatabaseSpecific(ctx context.Context) ([]DatabaseSpecific, error) { + rows, err := q.db.QueryContext(ctx, listDatabaseSpecific) + if err != nil { + return nil, err + } + defer rows.Close() + items := []DatabaseSpecific{} + for rows.Next() { + var i DatabaseSpecific + if err := rows.Scan(&i.DbSpecificID, &i.EntityType); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/grype/db/v6/store/repository/database_specific_nvd.sql.go b/grype/db/v6/store/repository/database_specific_nvd.sql.go new file mode 100644 index 00000000..858101e4 --- /dev/null +++ b/grype/db/v6/store/repository/database_specific_nvd.sql.go @@ -0,0 +1,74 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.26.0 +// source: database_specific_nvd.sql + +package repository + +import ( + "context" + "database/sql" +) + +const createDatabaseSpecificNvd = `-- name: CreateDatabaseSpecificNvd :one +INSERT INTO database_specific_nvd (db_specific_id, vulnStatus, cisaExploitAdd, cisaActionDue, cisaRequiredAction, cisaVulnerabilityName) +VALUES (?, ?, ?, ?, ?, ?) +RETURNING db_specific_id +` + +type CreateDatabaseSpecificNvdParams struct { + DbSpecificID int64 `db:"db_specific_id" json:"dbSpecificId"` + Vulnstatus sql.NullString `db:"vulnstatus" json:"vulnstatus"` + Cisaexploitadd sql.NullString `db:"cisaexploitadd" json:"cisaexploitadd"` + Cisaactiondue sql.NullString `db:"cisaactiondue" json:"cisaactiondue"` + Cisarequiredaction sql.NullString `db:"cisarequiredaction" json:"cisarequiredaction"` + Cisavulnerabilityname sql.NullString `db:"cisavulnerabilityname" json:"cisavulnerabilityname"` +} + +func (q *Queries) CreateDatabaseSpecificNvd(ctx context.Context, arg CreateDatabaseSpecificNvdParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createDatabaseSpecificNvd, + arg.DbSpecificID, + arg.Vulnstatus, + arg.Cisaexploitadd, + arg.Cisaactiondue, + arg.Cisarequiredaction, + arg.Cisavulnerabilityname, + ) + var db_specific_id int64 + err := row.Scan(&db_specific_id) + return db_specific_id, err +} + +const listDatabaseSpecificNvd = `-- name: ListDatabaseSpecificNvd :many +SELECT db_specific_id, vulnstatus, cisaexploitadd, cisaactiondue, cisarequiredaction, cisavulnerabilityname FROM database_specific_nvd +` + +func (q *Queries) ListDatabaseSpecificNvd(ctx context.Context) ([]DatabaseSpecificNvd, error) { + rows, err := q.db.QueryContext(ctx, listDatabaseSpecificNvd) + if err != nil { + return nil, err + } + defer rows.Close() + items := []DatabaseSpecificNvd{} + for rows.Next() { + var i DatabaseSpecificNvd + if err := rows.Scan( + &i.DbSpecificID, + &i.Vulnstatus, + &i.Cisaexploitadd, + &i.Cisaactiondue, + &i.Cisarequiredaction, + &i.Cisavulnerabilityname, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/grype/db/v6/store/repository/db.go b/grype/db/v6/store/repository/db.go new file mode 100644 index 00000000..cb69c20d --- /dev/null +++ b/grype/db/v6/store/repository/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.26.0 + +package repository + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/grype/db/v6/store/repository/models.go b/grype/db/v6/store/repository/models.go new file mode 100644 index 00000000..61ccec57 --- /dev/null +++ b/grype/db/v6/store/repository/models.go @@ -0,0 +1,181 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.26.0 + +package repository + +import ( + "database/sql" +) + +type Affected struct { + AffectedID int64 `db:"affected_id" json:"affectedId"` + EntityType string `db:"entity_type" json:"entityType"` +} + +type AffectedCpe struct { + AffectedID int64 `db:"affected_id" json:"affectedId"` + VulnerabilityID int64 `db:"vulnerability_id" json:"vulnerabilityId"` + Type string `db:"type" json:"type"` + Vendor sql.NullString `db:"vendor" json:"vendor"` + Product string `db:"product" json:"product"` + Version sql.NullString `db:"version" json:"version"` + Update sql.NullString `db:"update" json:"update"` + TargetSoftware sql.NullString `db:"target_software" json:"targetSoftware"` +} + +type AffectedDistroPackage struct { + AffectedID int64 `db:"affected_id" json:"affectedId"` + VulnerabilityID int64 `db:"vulnerability_id" json:"vulnerabilityId"` + PackageName string `db:"package_name" json:"packageName"` + OsID int64 `db:"os_id" json:"osId"` +} + +type AffectedPackage struct { + AffectedID int64 `db:"affected_id" json:"affectedId"` + VulnerabilityID sql.NullInt64 `db:"vulnerability_id" json:"vulnerabilityId"` + Ecosystem sql.NullString `db:"ecosystem" json:"ecosystem"` + PackageName sql.NullString `db:"package_name" json:"packageName"` + Purl sql.NullString `db:"purl" json:"purl"` +} + +type AffectedSeverity struct { + AffectedSeverityID int64 `db:"affected_severity_id" json:"affectedSeverityId"` + AffectedID int64 `db:"affected_id" json:"affectedId"` + Type string `db:"type" json:"type"` + Score string `db:"score" json:"score"` + Source sql.NullString `db:"source" json:"source"` + Tag sql.NullString `db:"tag" json:"tag"` +} + +type AffectedVersion struct { + VersionID int64 `db:"version_id" json:"versionId"` + AffectedID int64 `db:"affected_id" json:"affectedId"` + Version string `db:"version" json:"version"` +} + +type Alias struct { + AliasID int64 `db:"alias_id" json:"aliasId"` + VulnerabilityID int64 `db:"vulnerability_id" json:"vulnerabilityId"` + Alias string `db:"alias" json:"alias"` +} + +type DatabaseSpecific struct { + DbSpecificID int64 `db:"db_specific_id" json:"dbSpecificId"` + EntityType string `db:"entity_type" json:"entityType"` +} + +type DatabaseSpecificNvd struct { + DbSpecificID int64 `db:"db_specific_id" json:"dbSpecificId"` + Vulnstatus sql.NullString `db:"vulnstatus" json:"vulnstatus"` + Cisaexploitadd sql.NullString `db:"cisaexploitadd" json:"cisaexploitadd"` + Cisaactiondue sql.NullString `db:"cisaactiondue" json:"cisaactiondue"` + Cisarequiredaction sql.NullString `db:"cisarequiredaction" json:"cisarequiredaction"` + Cisavulnerabilityname sql.NullString `db:"cisavulnerabilityname" json:"cisavulnerabilityname"` +} + +type DbMetadatum struct { + BuildTimestamp string `db:"build_timestamp" json:"buildTimestamp"` + SchemaVersion int64 `db:"schema_version" json:"schemaVersion"` +} + +type Description struct { + Digest string `db:"digest" json:"digest"` + Value string `db:"value" json:"value"` +} + +type LogicalPackage struct { + LogicalPackageID int64 `db:"logical_package_id" json:"logicalPackageId"` + AffectedID int64 `db:"affected_id" json:"affectedId"` +} + +type NotAffectedVersion struct { + VersionID int64 `db:"version_id" json:"versionId"` + AffectedID int64 `db:"affected_id" json:"affectedId"` + Version string `db:"version" json:"version"` +} + +type OperatingSystem struct { + OsID int64 `db:"os_id" json:"osId"` + Name string `db:"name" json:"name"` + MajorVersion string `db:"major_version" json:"majorVersion"` + MinorVersion sql.NullString `db:"minor_version" json:"minorVersion"` + Codename sql.NullString `db:"codename" json:"codename"` +} + +type PackageDigest struct { + VulnerabilityID int64 `db:"vulnerability_id" json:"vulnerabilityId"` + DigestAlgorithm string `db:"digest_algorithm" json:"digestAlgorithm"` + DigestValue string `db:"digest_value" json:"digestValue"` +} + +type PackageQualifier struct { + QualifierID int64 `db:"qualifier_id" json:"qualifierId"` + AffectedID sql.NullInt64 `db:"affected_id" json:"affectedId"` + EntityType string `db:"entity_type" json:"entityType"` +} + +type PackageQualifierPlatformCpe struct { + QualifierID int64 `db:"qualifier_id" json:"qualifierId"` + Cpe string `db:"cpe" json:"cpe"` +} + +type PackageQualifierRpmModularity struct { + QualifierID int64 `db:"qualifier_id" json:"qualifierId"` + Module string `db:"module" json:"module"` +} + +type Provider struct { + ProviderID int64 `db:"provider_id" json:"providerId"` + Name string `db:"name" json:"name"` + Version sql.NullString `db:"version" json:"version"` + DateCaptured sql.NullString `db:"date_captured" json:"dateCaptured"` + InputDigest sql.NullString `db:"input_digest" json:"inputDigest"` + DataOciRepository sql.NullString `db:"data_oci_repository" json:"dataOciRepository"` +} + +type RangeEvent struct { + EventID int64 `db:"event_id" json:"eventId"` + AffectedID int64 `db:"affected_id" json:"affectedId"` + Type string `db:"type" json:"type"` + Repo sql.NullString `db:"repo" json:"repo"` + Introduced sql.NullString `db:"introduced" json:"introduced"` + Fixed sql.NullString `db:"fixed" json:"fixed"` + LastAffected sql.NullString `db:"last_affected" json:"lastAffected"` + Limit sql.NullString `db:"limit" json:"limit"` + State sql.NullString `db:"state" json:"state"` +} + +type References struct { + ReferenceID int64 `db:"reference_id" json:"referenceId"` + VulnerabilityID int64 `db:"vulnerability_id" json:"vulnerabilityId"` + Type string `db:"type" json:"type"` + Url string `db:"url" json:"url"` +} + +type RelatedVulnerability struct { + RelatedID int64 `db:"related_id" json:"relatedId"` + VulnerabilityID int64 `db:"vulnerability_id" json:"vulnerabilityId"` + RelatedVulnerabilityID string `db:"related_vulnerability_id" json:"relatedVulnerabilityId"` +} + +type Severity struct { + SeverityID int64 `db:"severity_id" json:"severityId"` + VulnerabilityID int64 `db:"vulnerability_id" json:"vulnerabilityId"` + Type string `db:"type" json:"type"` + Score string `db:"score" json:"score"` + Source sql.NullString `db:"source" json:"source"` + Tag sql.NullString `db:"tag" json:"tag"` +} + +type Vulnerability struct { + VulnerabilityID int64 `db:"vulnerability_id" json:"vulnerabilityId"` + ProviderID int64 `db:"provider_id" json:"providerId"` + ID string `db:"id" json:"id"` + Modified sql.NullString `db:"modified" json:"modified"` + Published sql.NullString `db:"published" json:"published"` + Withdrawn sql.NullString `db:"withdrawn" json:"withdrawn"` + SummaryDigest sql.NullString `db:"summary_digest" json:"summaryDigest"` + DetailDigest sql.NullString `db:"detail_digest" json:"detailDigest"` + DatabaseSpecificID sql.NullInt64 `db:"database_specific_id" json:"databaseSpecificId"` +} diff --git a/grype/db/v6/store/repository/operating_system.sql.go b/grype/db/v6/store/repository/operating_system.sql.go new file mode 100644 index 00000000..b8e315dc --- /dev/null +++ b/grype/db/v6/store/repository/operating_system.sql.go @@ -0,0 +1,235 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.26.0 +// source: operating_system.sql + +package repository + +import ( + "context" + "database/sql" +) + +const createOperatingSystem = `-- name: CreateOperatingSystem :one +INSERT INTO operating_systems (name, major_version, minor_version, codename) +VALUES (?, ?, ?, ?) +RETURNING os_id +` + +type CreateOperatingSystemParams struct { + Name string `db:"name" json:"name"` + MajorVersion string `db:"major_version" json:"majorVersion"` + MinorVersion sql.NullString `db:"minor_version" json:"minorVersion"` + Codename sql.NullString `db:"codename" json:"codename"` +} + +func (q *Queries) CreateOperatingSystem(ctx context.Context, arg CreateOperatingSystemParams) (int64, error) { + row := q.db.QueryRowContext(ctx, createOperatingSystem, + arg.Name, + arg.MajorVersion, + arg.MinorVersion, + arg.Codename, + ) + var os_id int64 + err := row.Scan(&os_id) + return os_id, err +} + +const listOperatingSystems = `-- name: ListOperatingSystems :many + +SELECT os_id, name, major_version, minor_version, codename FROM operating_systems +ORDER BY name, major_version, minor_version +` + +// TODO: this ends up creating interface{} values for input params, which is not great at runtime safety-wise... but this is a single flex statement, replacing all of the other ones below +// -- name: ListOperatingSystems :many +// SELECT * FROM operating_systems +// WHERE (name = COALESCE(NULLIF(@name, ”), name)) +// +// AND (major_version = COALESCE(NULLIF(@major_version, ''), major_version)) +// AND (minor_version = COALESCE(?, minor_version)) +// AND (codename = COALESCE(?, codename)) +// +// ORDER BY name, major_version, minor_version; +func (q *Queries) ListOperatingSystems(ctx context.Context) ([]OperatingSystem, error) { + rows, err := q.db.QueryContext(ctx, listOperatingSystems) + if err != nil { + return nil, err + } + defer rows.Close() + items := []OperatingSystem{} + for rows.Next() { + var i OperatingSystem + if err := rows.Scan( + &i.OsID, + &i.Name, + &i.MajorVersion, + &i.MinorVersion, + &i.Codename, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listOperatingSystemsByCodename = `-- name: ListOperatingSystemsByCodename :many +SELECT os_id, name, major_version, minor_version, codename FROM operating_systems +WHERE codename = ? +ORDER BY name, major_version, minor_version +` + +func (q *Queries) ListOperatingSystemsByCodename(ctx context.Context, codename sql.NullString) ([]OperatingSystem, error) { + rows, err := q.db.QueryContext(ctx, listOperatingSystemsByCodename, codename) + if err != nil { + return nil, err + } + defer rows.Close() + items := []OperatingSystem{} + for rows.Next() { + var i OperatingSystem + if err := rows.Scan( + &i.OsID, + &i.Name, + &i.MajorVersion, + &i.MinorVersion, + &i.Codename, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listOperatingSystemsByName = `-- name: ListOperatingSystemsByName :many +SELECT os_id, name, major_version, minor_version, codename FROM operating_systems +WHERE name = ? +ORDER BY name, major_version, minor_version +` + +func (q *Queries) ListOperatingSystemsByName(ctx context.Context, name string) ([]OperatingSystem, error) { + rows, err := q.db.QueryContext(ctx, listOperatingSystemsByName, name) + if err != nil { + return nil, err + } + defer rows.Close() + items := []OperatingSystem{} + for rows.Next() { + var i OperatingSystem + if err := rows.Scan( + &i.OsID, + &i.Name, + &i.MajorVersion, + &i.MinorVersion, + &i.Codename, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listOperatingSystemsByNameAndExactVersion = `-- name: ListOperatingSystemsByNameAndExactVersion :many +SELECT os_id, name, major_version, minor_version, codename FROM operating_systems +WHERE name = ? + AND major_version = ? + AND minor_version = ? +ORDER BY name, major_version, minor_version +` + +type ListOperatingSystemsByNameAndExactVersionParams struct { + Name string `db:"name" json:"name"` + MajorVersion string `db:"major_version" json:"majorVersion"` + MinorVersion sql.NullString `db:"minor_version" json:"minorVersion"` +} + +func (q *Queries) ListOperatingSystemsByNameAndExactVersion(ctx context.Context, arg ListOperatingSystemsByNameAndExactVersionParams) ([]OperatingSystem, error) { + rows, err := q.db.QueryContext(ctx, listOperatingSystemsByNameAndExactVersion, arg.Name, arg.MajorVersion, arg.MinorVersion) + if err != nil { + return nil, err + } + defer rows.Close() + items := []OperatingSystem{} + for rows.Next() { + var i OperatingSystem + if err := rows.Scan( + &i.OsID, + &i.Name, + &i.MajorVersion, + &i.MinorVersion, + &i.Codename, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listOperatingSystemsByNameAndMajorVersion = `-- name: ListOperatingSystemsByNameAndMajorVersion :many +SELECT os_id, name, major_version, minor_version, codename FROM operating_systems +WHERE name = ? + AND major_version = ? +ORDER BY name, major_version, minor_version +` + +type ListOperatingSystemsByNameAndMajorVersionParams struct { + Name string `db:"name" json:"name"` + MajorVersion string `db:"major_version" json:"majorVersion"` +} + +func (q *Queries) ListOperatingSystemsByNameAndMajorVersion(ctx context.Context, arg ListOperatingSystemsByNameAndMajorVersionParams) ([]OperatingSystem, error) { + rows, err := q.db.QueryContext(ctx, listOperatingSystemsByNameAndMajorVersion, arg.Name, arg.MajorVersion) + if err != nil { + return nil, err + } + defer rows.Close() + items := []OperatingSystem{} + for rows.Next() { + var i OperatingSystem + if err := rows.Scan( + &i.OsID, + &i.Name, + &i.MajorVersion, + &i.MinorVersion, + &i.Codename, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +}